【设计模式】创建型模式——建造者模式

创建型模式——建造者模式

一、定义

建造者模式是一种创建型设计模式, 使你能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象。

二、问题

现在你需要建造一个日志对象,来打印日志。首先需要打印日志时间,日志体,……日志尾。但是如果你还想要在日志尾后打印日志的署名,如何做?
1、扩展基类(日志类)
然后创建一系列涵盖所有参数组合的子类。 但最终你将面对相当数量的子类。 任何新增的参数 (例如署名) 都会让这个层次结构更加复杂。
3个参数就有7种组合,4个参数有15种组合……太多了!
2、无需生成子类。 你可以在 基类中创建一个包括所有可能参数的超级构造函数, 并用它来控制日志对象。 这种方法确实可以避免生成子类, 但它却会造成另外一个问题。参数太多了,而且较多参数都用不到

Log(LogTime logTime,LogBody logBody,LogFoot logFoot,LogName logname ……)

三、解决方案

建造者模式建议将对象构造代码从产品类中抽取出来, 并将其放在一个名为建造者的独立对象中。建造者模式让你能够分步骤创建复杂对象。

该模式会将对象构造过程划分为一组步骤, 比如 创建日志时间和 创建日志体等。 每次创建对象时, 你都需要通过建造者对象执行一系列步骤。 重点在于你无需调用所有步骤, 而只需调用创建特定对象配置所需的那些步骤即可。
当你需要创建不同形式的产品时, 其中的一些构造步骤可能需要不同的实现。 例如, 控制台打印日志 或者文件打印日志。
在这种情况下, 你可以创建多个不同的建造者, 用不同方式实现一组相同的创建步骤。 然后你就可以在创建过程中使用这些创建者来生成不同类型的对象。

四、代码实现

1、建造者 (Builder) 接口声明在所有类型生成器中通用的产品构造步骤。
package com.atmae.builder;

/**
 * @Author: Mae
 * @Date: 2022/4/24
 * @Time: 8:48
 * @Description:
 */
public interface Builder {
    
    
    void buildTimer();
    void buildBody();
    void buildFoot();
}

2、具体建造者 (Concrete Builders) 提供构造过程的不同实现。 具体生成器也可以构造不遵循通用接口的产品。

package com.atmae.builder;

import java.util.Date;

/**
 * @Author: Mae
 * @Date: 2022/4/24
 * @Time: 8:52
 * @Description:
 */
public class ConsoleBuilder implements Builder{
    
    
    private final ConsoleLog consoleLog=new ConsoleLog();

    @Override
    public void buildTimer() {
    
    
        ;this.consoleLog.setLogTime("控制台日志时间"+new Date());
    }

    @Override
    public void buildBody() {
    
    
    this.consoleLog.setLogBody("控制台日志体");
    }

    @Override
    public void buildFoot() {
    
    
    this.consoleLog.setLogFoot("控制台日志尾");
    }

    public ConsoleLog getConsoleLog() {
    
    
        return consoleLog;
    }
}

package com.atmae.builder;

import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;

/**
 * @Author: Mae
 * @Date: 2022/4/24
 * @Time: 8:57
 * @Description:
 */
public class FileBuilder implements Builder {
    
    

    private final FileLog fileLog = new FileLog();

    @Override
    public void buildTimer() {
    
    
        this.fileLog.setLogTime("文件日志时间:" + new Date());
    }

    @Override
    public void buildBody() {
    
    
        this.fileLog.setLogBody("文件日志体");
    }

    @Override
    public void buildFoot() {
    
    
        this.fileLog.setLogFoot("文件日志尾");
    }

    public FileLog getFileLog() {
    
    
        return fileLog;
    }
}

3、指挥者类(Director) 类定义调用构造步骤的顺序, 这样你就可以创建和复用特定的产品配置。

package com.atmae.builder;

/**
 * @Author: Mae
 * @Date: 2022/4/24
 * @Time: 9:00
 * @Description:
 */
public class Director {
    
    
    private Builder builder;

    public Director(Builder builder) {
    
    
        this.builder = builder;
    }

    public void log() {
    
    
        this.builder.buildTimer();
        this.builder.buildBody();
        this.builder.buildFoot();
    }

}

4、产品 (Products) 是最终生成的对象。 由不同生成器构造的产品无需属于同一类层次结构或接口。

package com.atmae.builder;

/**
 * @Author: Mae
 * @Date: 2022/4/24
 * @Time: 13:05
 * @Description:
 */
public class ConsoleLog {
    
    
    private String logTime;
    private String logBody;
    private String logFoot;

    public void show(){
    
    
        System.out.println("logTime:"+logTime+"========="+
                "\nlogBody:"+logBody+"========"+
                "\nlogFoot:"+logFoot+"========");
    }

    public void setLogTime(String logTime) {
    
    
        this.logTime = logTime;
    }

    public void setLogBody(String logBody) {
    
    
        this.logBody = logBody;
    }

    public void setLogFoot(String logFoot) {
    
    
        this.logFoot = logFoot;
    }
}

package com.atmae.builder;

/**
 * @Author: Mae
 * @Date: 2022/4/24
 * @Time: 13:05
 * @Description:
 */
public class FileLog {
    
    
    private String logTime;
    private String logBody;
    private String logFoot;

   public void show(){
    
    
       System.out.println("logTime:"+logTime+"========="+
               "\nlogBody:"+logBody+"========"+
               "\nlogFoot:"+logFoot+"========");
   }

    public void setLogTime(String logTime) {
    
    
        this.logTime = logTime;
    }

    public void setLogBody(String logBody) {
    
    
        this.logBody = logBody;
    }

    public void setLogFoot(String logFoot) {
    
    
        this.logFoot = logFoot;
    }
}

5、客户端 (Client) 必须将某个建造者对象与指挥类关联。 一般情况下, 你只需通过指挥类构造函数的参数进行一次性关联即可。 此后指挥类就能使用建造者对象完成后续所有的构造任务。

package com.atmae.builder;

/**
 * @Author: Mae
 * @Date: 2022/4/24
 * @Time: 9:02
 * @Description:
 */
public class Client {
    
    
    public static void main(String[] args) {
    
    
        FileBuilder fileBuilder = new FileBuilder();
        Director director = new Director(fileBuilder);
        director.log();
        FileLog fileLog = fileBuilder.getFileLog();
        fileLog.show();
    }
}

五、UML图

在这里插入图片描述

六、建造者模式使用场景

  • 避免 “重叠构造函数” 的出现(构造函数中有十个可选参数, 那么调用该函数会非常不方便)
  • 希望使用代码创建不同形式的产品(文件日志/控制台日志)时,可使用建造者模式
  • 使用建造者构造组合树或其他复杂对象

七、总结

1、优点

  • 你可以分步创建对象, 暂缓创建步骤或递归运行创建步骤。
  • 生成不同形式的产品时, 你可以复用相同的制造代码。
  • 单一职责原则。 你可以将复杂构造代码从产品的业务逻辑中分离出来。

2、缺点

  • 由于该模式需要新增多个类, 因此代码整体复杂程度会有所增加。

八、与其他模式的关系

  • 建造者重点关注如何分步生成复杂对象。 抽象工厂专门用于生产一系列相关对象。 抽象工厂会马上返回产品, 建造者则允许你在获取产品前执行一些额外构造步骤。

猜你喜欢

转载自blog.csdn.net/weixin_51799151/article/details/124565863
今日推荐