「设计模式(六) - Builder模式」

「设计模式(六) - Builder模式」
一、可定制化的

电脑的组装在生活中并不陌生,大家都有电脑,当然需求不一样配置也不一样。以Macbook Pro为例,像UI设计对图像模块GPU要求比较高,跑IDEA的对内存要求就比较高,可能会加装32G内存更高的就是64G了。如果是对付日常的办公,刷剧那默认的配置就已经足够了,如8G标配。类似的在软件开发过程中,需要根据需要自定义搭配不同的选择来构建对象的一般能够用Builder模式很好的解释,看看具体的定义是怎样的。

二、Builder模式

维基百科

The builder pattern is a design pattern designed to provide a flexible solution to various object creation problems in object-oriented programming. The intent of the Builder design pattern is to separate the construction of a complex object from its representation. It is one of the Gang of Four design patterns.

通俗的表示:即将一个复杂对象的构建与表示相分离开,同样的构建过程根据需要可以创建不同的表示。称之为Builder模式建造者模式

三、结构组成
  • 结构类图:
    Buidelr模式

  • 构造器接口Builder:通常会包含构建部件模块的抽象方法,以及对象创建完成后的结果方法getResult()

  • 构造器具体实现类ConcreteBuilder:对Builder接口的具体实现,构建产品Product各个部件,组装成完整的产品对象,并通过**getResult()**返回具体的对象。

  • 产品的抽象接口Product:需要构造的对象,一般包含多个组成部分,构成较为复杂的对象。

  • 定制者(指挥者)Director:决定了Product的具体创建组成,例如包含何种组件。指导出Product复杂对象的创建,类图表示较为清晰;一般使用Builder来完成创建过程。

四、代码实现
1.设计一个简单的文件导出系统

根据需要配置对应的文档系统并导出文件,例如导出纯文本内容**.text**、.html等文件。

  • 元素Element对应这里的Product,即产品的接口
public interface Element {
  //no method
}

1.为提供默认的方法,由实现类自行决定实现何种细节。

  • 元素Element具体实现类-文本
/**
 * Created by Sai
 * on: 25/01/2022 00:48.
 * Description:
 */
public class Text implements Element {
    private final String content;

    public Text(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }
}
  • 元素Element具体实现类-图片
/**
 * Created by Sai
 * on: 25/01/2022 00:49.
 * Description:
 */
public class Image implements Element {
    private final String source;

    public Image(String source) {
        this.source = source;
    }

    public String getSource() {
        return source;
    }
}
  • 建造器接口Builder,这里既然是文档类型的建造器,命名规范一点的话则为DocumentBuilder,下同:
/**
 * Created by Sai
 * on: 25/01/2022 00:52.
 * Description:
 */
public interface DocumentBuilder {
    void addText(Text text);

    void addImage(Image image);

    String getResult();
}

1.分别提供了两个添加内容的方法,addTextaddImage

2.提供一个返回具体内容的方法,getResult

  • 建造器的具体实现-纯文本文档建造器
public class TextDocumentBuilder implements DocumentBuilder {

    private final StringBuilder builder = new StringBuilder();

    @Override
    public void addText(Text text) {
        builder.append(text.getContent());
    }

    @Override
    public void addImage(Image image) {
        //empty implements
    }

    @Override
    public String getResult() {
        return builder.toString();
    }
}

1.其中纯文本内容不支持添加图片,添加图片方法则未实现,为空方法。

  • 由于html结构相对于纯文本来说较为复杂,包括标签,段落等等,如果完全依赖于Element不利于扩展,实现起来比较麻烦,因此对于html类型的文档文件,单独实现自己的一套元素组件。以Element为媒介动态添加进去。

  • 单独实现html特有的元素HtmlElement

public class HtmlElement {
  //empty method
}
  • Html图片元素组件HtmlImage继承自HtmlElement
public class HtmlImage extends HtmlElement {
    private final String source;

    public HtmlImage(String source) {
        this.source = source;
    }

    @Override
    public String toString() {
        return String.format("<img src=\"%s\" />", source);
    }
}
  • Html段落文本组件HtmlParagraph同样继承自HtmlElement
public class HtmlParagraph extends HtmlElement {
    private final String text;

    public HtmlParagraph(String text) {
        this.text = text;
    }

    @Override
    public String toString() {
        return String.format("<p>%s</p>", text);
    }
}
  • 一份完整的Html文档可能包含多种HtmlParagraphHtmlImage的集合,定义html文档文件类-HtmlDocument
public class HtmlDocument {
    private final List<HtmlElement> elements = new ArrayList<>();

    public void add(HtmlElement element) {
        elements.add(element);
    }

    @Override
    public String toString() {
        var builder = new StringBuilder();
        builder.append("<html>");
        for (HtmlElement element : elements)
            builder.append(element.toString());
        builder.append("</html>");
        return builder.toString();
    }
}
  • 对于html建造器的实现:由于html文档的特殊性,虽然依据类图的实现流程需要实现DocumentBuilder接口,显然仅仅DocumentBuilder中方法并不能很好的满足需求的定制,但是搭配重新定义一套html文档组装规则**(HtmlDocument、HtmlElement、HtmlImage、HtmlParagraph)**则能够很好的完成扩展:
public class HtmlDocumentBuilder implements DocumentBuilder {
    
    private final HtmlDocument document = new HtmlDocument();

    @Override
    public void addText(Text text) {
        document.add(new HtmlParagraph(text.getContent()));
    }

    @Override
    public void addImage(Image image) {
        document.add(new HtmlImage(image.getSource()));
    }

    @Override
    public String getResult() {
        return document.toString();
    }
}
  • 完整的文档导出类-Document
public class Document {
    private final List<Element> elements = new ArrayList<>();

    public void add(Element element) {
        elements.add(element);
    }

    public void export(DocumentBuilder builder, String fileName) throws IOException {
        for (Element element : elements) {
            if (element instanceof Text)
                builder.addText((Text) element);
            else if (element instanceof Image)
                builder.addImage((Image) element);
        }
        var writer = new FileWriter(fileName);
        writer.write(builder.getResult());
        writer.close();
    }
}
  • 测试Demo
public class Demo {
    public static void show() throws IOException {
        var document = new Document();
        document.add(new Text("\n\n\n\n快乐二狗🐶🐶🐶\nphp才是最好的语言🐶\n"));
        document.add(new Image("pic1.jpg"));

        document.export(new HtmlDocumentBuilder(), "sai.html");
        //文档不添加图片
        document.export(new TextDocumentBuilder(), "sai.txt");
    }

    public static void main(String[] args) throws IOException {
        show();
    }
}

1.简单的添加了一个文本内容与一个“图片内容”,分别组成了纯文本的文档Html的文档,但文本的文档是没有添加图片的。

2.导出的文件在同包下,sai.htmlsai.text文件。

  • “纯文本文档”内容
    在这里插入图片描述

  • “html”文档
    在这里插入图片描述

2.优缺点与局限性思考

1.Builder模式很好的将构建与表现相分离,客户端或者Director可以根据需要灵活的选择,在同一套构建算法下产生不同的产品,使得表现形式与生产的耦合程度降低。

2.具体的构建细节包含在系统内部,客户端仅仅只需要通过对Builder接口的调用即可完成创建所需要的对象,降低了系统出错的风险。

3.加强了代码的复用性。

4.当然缺点也是很明显的,如果对象太过于复杂,组装的配件过多往往不好掌控,造成臃肿,结构也不是很清晰。

5.如果改变产品原有的实现,那么整套流程都需要做出相应的调整,假设产品本身过于复杂,那么对于后期的维护是很不利的。在考虑使用时应根据实际情况,对于复杂且变化频繁的对象并不适合使用。

五、实际应用中的变形

实际使用过程中往往简化了标准化的构建流程,当然也是根据具体的业务场景,一般会省略了Director指导类,Builder接口以及具体的ConcreteBuilder实现类,而直接将Builder作为内部类实现在了目标产品类之中。根据调用者的选择得到不同的产品,当然这种比较单一,可以说是最为简单的一种。仅仅是一种思想上的应用,或者说也是一种**“取巧”**的做法。

  • 自身项目中,对于简单筛选对象的定制:
public class FilterGroup {
    private int id;
    private String title;
    private boolean supportMultiSelected;
    private List<FilterOrderDTO> filterFactors;

    private FilterGroup(Builder builder) {
        if (builder == null) {
            return;
        }
        this.id = builder.id;
        this.title = builder.title;
        this.supportMultiSelected = builder.supportMultiSelected;
        this.filterFactors = builder.filterFactors;
    }

    public int getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public boolean isSupportMultiSelected() {
        return supportMultiSelected;
    }

    public List<FilterOrderDTO> getFilterFactors() {
        return filterFactors;
    }

    public static class Builder {
        private int id;
        private String title;
        private boolean supportMultiSelected;
        private List<FilterOrderDTO> filterFactors;

        public Builder setId(int id) {
            this.id = id;
            return this;
        }

        public Builder setTitle(String title) {
            this.title = title;
            return this;
        }

        public Builder setSupportMultiSelected(boolean supportMultiSelected) {
            this.supportMultiSelected = supportMultiSelected;
            return this;
        }

        public Builder setFilterFactors(List<FilterOrderDTO> filterFactors) {
            this.filterFactors = filterFactors;
            return this;
        }

        public FilterGroup build() {
            return new FilterGroup(this);
        }
    }
}

1.Builder以静态内部类的形式存在于产品的内部,而产品的特征则是:不同的构建组成所对应的产品不同,表现出来的性质也是不同的。其次Builder中只提供了set方法,而没有get方法。

2.目标产品FilterGroup的构造方法是私有的,并且以Builder为参数。另外只提供了get方法而没有set方法。

3.即对于目标的产品的构建是通过Builder来完成的,对于用户它仅仅是“可读的”。

4.像实际开发中,这种分部分构建产品与整体相分离的可以考虑使用Builder模式,核心思想即为整体构建与部件之间的分离,松散产品构建产品表示之间的耦合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快乐二狗呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值