「设计模式(六) - Builder模式」
一、可定制化的
电脑的组装在生活中并不陌生,大家都有电脑,当然需求不一样配置也不一样。以Macbook Pro为例,像UI设计对图像模块GPU要求比较高,跑IDEA的对内存要求就比较高,可能会加装32G内存更高的就是64G了。如果是对付日常的办公,刷剧那默认的配置就已经足够了,如8G标配。类似的在软件开发过程中,需要根据需要自定义搭配不同的选择来构建对象的一般能够用Builder模式很好的解释,看看具体的定义是怎样的。
二、Builder模式
三、结构组成
-
结构类图:
-
构造器接口Builder:通常会包含构建部件模块的抽象方法,以及对象创建完成后的结果方法getResult()。
-
构造器具体实现类ConcreteBuilder:对Builder接口的具体实现,构建产品Product各个部件,组装成完整的产品对象,并通过**getResult()**返回具体的对象。
-
产品的抽象接口Product:需要构造的对象,一般包含多个组成部分,构成较为复杂的对象。
-
定制者(指挥者)Director:决定了Product的具体创建组成,例如包含何种组件。指导出Product复杂对象的创建,类图表示较为清晰;一般使用Builder来完成创建过程。
四、代码实现
1.设计一个简单的文件导出系统
- 元素Element对应这里的Product,即产品的接口
public interface Element {
//no method
}
- 元素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();
}
- 建造器的具体实现-纯文本文档建造器
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();
}
}
-
由于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文档可能包含多种HtmlParagraph与HtmlImage的集合,定义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();
}
}
-
“纯文本文档”内容
-
“html”文档
2.优缺点与局限性思考
五、实际应用中的变形
- 自身项目中,对于简单筛选对象的定制:
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);
}
}
}