建造者模式
定义:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
作用:
在用户不知道对象的建造过程和细节的情况下,可以直接创建复杂的对象
优点:
- 产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必要知道产品内部组成的细节
- 将复杂产品的创建步骤分截在不同的方法中,使得创建过程更加清晰
- 具体的建造者之间使相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的代码,符合”开闭原则“。
缺点:
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
应用场景:
- 需要生成的产品对象有复杂的内部结构,这些产品对象具有共性
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品
- 适合于一个具有较多的零件(属性)的产品(对象)的创建过程
1、测试案例一
先设想一个场景:工程师修建房子,并且他知道他需要做的工作是打地基、铺电线、打钢筋、粉刷墙壁以及最终得到房子,但是他不会自己做,这个具体的事情由工人完成,他只是负责指挥工人完成相应的工作。
这段话里面含有四个对象:
- 工程师:建造出来的房子、口头上的命令工作
- 抽象的工作(口头上的工作):名义上的建造房子需要做的四项工作、得到房子
- 工人:真实完成的四项工作、建造出来的产品
- 修建出来的房子:房子是由四个工作累积起来的
-
抽象的工作类:Buider.java
package pers.mobian.buider.demo01; public abstract class Buider { abstract void buiderA(); //地基 abstract void buiderB(); //钢筋工程 abstract void buiderC(); //铺电线 abstract void buiderD(); //粉刷 //完工:得到具体的产品 abstract Product getProduct(); }
-
工人类:Worker.java
package pers.mobian.buider.demo01; //具体的建造者:工人 public class Worker extends Buider{ private Product product; public Worker() { product = new Product(); } @Override void buiderA() { product.setBuiderA("地基"); System.out.println("地基"); } @Override void buiderB() { product.setBuiderB("钢筋工程"); System.out.println("钢筋工程"); } @Override void buiderC() { product.setBuiderC("铺电线"); System.out.println("铺电线"); } @Override void buiderD() { product.setBuiderD("粉刷"); System.out.println("粉刷"); } @Override Product getProduct() { return product; } }
-
修建出来的房子:Product.java
package pers.mobian.buider.demo01; public class Product { private String buiderA; private String buiderB; private String buiderC; private String buiderD; @Override public String toString() { return "Product{" + "buiderA='" + buiderA + '\'' + ", buiderB='" + buiderB + '\'' + ", buiderC='" + buiderC + '\'' + ", buiderD='" + buiderD + '\'' + '}'; } public String getBuiderA() { return buiderA; } public void setBuiderA(String buiderA) { this.buiderA = buiderA; } public String getBuiderB() { return buiderB; } public void setBuiderB(String buiderB) { this.buiderB = buiderB; } public String getBuiderC() { return buiderC; } public void setBuiderC(String buiderC) { this.buiderC = buiderC; } public String getBuiderD() { return buiderD; } public void setBuiderD(String buiderD) { this.buiderD = buiderD; } }
-
工程师:Director.java
package pers.mobian.buider.demo01; //指挥:核心,负责只会构建一个工程,工程如果构建, public class Director { //指挥工人,按照顺序修建房子 public Product build(Buider buider) { buider.buiderA(); buider.buiderB(); buider.buiderC(); buider.buiderD(); return buider.getProduct(); } }
-
验房,即测试类:Test.java
package pers.mobian.buider.demo01; public class Test { public static void main(String[] args) { //叫对应的工程师 Director director = new Director(); //工程师去指挥对应的工人 Product build = director.build(new Worker()); //打印输出修好了的房子 System.out.println(build.toString()); } }
-
结果
地基 钢筋工程 铺电线 粉刷 Product{buiderA='地基', buiderB='钢筋工程', buiderC='铺电线', buiderD='粉刷'}
总结:测试类只需要简单的调用工程师类,具体工程师去叫哪个工人,得出什么样的产品,测试类不需要关心。并且,用此方法实现的话,代码有很强的可扩展性。需要不同的产品只需要让工程师调用不同的工人即可。
但是有时候,我们需要简化的系统结构,可以把Director和抽象类进行一个结合。通过静态内部类方式实现零件无序装配构造,此方式更加的灵活且符合规定。内部有复杂对象的默认实现,使用时间可以根据用户需求自由定义更改内容,并且无需修改具体的构造方式。就饿能够生产出不同的复杂产品。
例如:你去快餐,服务员(具体的建造者Worker)可以随意搭配任意几种产品(零件Builder)组成一套套餐(产品Product),然后出售给客户。比第一种方式少了指挥者,主要是因为第二种方式把指挥者交给了用户自己来操作,使得产品更加灵活。
2、测试案例二
设想一个场景:某天客户(Test)去一个餐馆吃饭,但是他却不知道改吃什么,于是就点了一个他们店的经典套餐(默认菜单)。第二天他又去,但是这次他只想喝白开水,于是他就叫服务员(Worker),让他给他来四杯白开水,服务员问了一下后厨(Builder),后厨说还有白开水,于是服务员就修改了菜单,然后就上了四倍白开水。
这段话里面含有四个对象:
- 客户:叫服务员、得到菜
- 后厨(口头上的工作):询问是否还有菜、做菜
- 服务员:负责修改菜单、上菜
- 菜单:菜单上是经典套餐、菜单上是自己点的水
-
后厨:Builder.java
package pers.mobian.buider.demo02; public abstract class Builder { abstract Builder builderA(String msg); abstract Builder builderB(String msg); abstract Builder builderC(String msg); abstract Builder builderD(String msg); abstract Product getProduct(); }
-
菜单:Product.java
package pers.mobian.buider.demo02; public class Product { private String builderA = "青椒肉丝"; private String builderB = "豆腐汤"; private String builderC = "白米饭"; private String builderD = "咸菜"; public String getBuilderA() { return builderA; } public void setBuilderA(String builderA) { this.builderA = builderA; } public String getBuilderB() { return builderB; } public void setBuilderB(String builderB) { this.builderB = builderB; } public String getBuilderC() { return builderC; } public void setBuilderC(String builderC) { this.builderC = builderC; } public String getBuilderD() { return builderD; } public void setBuilderD(String builderD) { this.builderD = builderD; } //打印具体的产品 @Override public String toString() { return "Product{" + "builderA='" + builderA + '\'' + ", builderB='" + builderB + '\'' + ", builderC='" + builderC + '\'' + ", builderD='" + builderD + '\'' + '}'; } }
-
服务员:Worker.java
package pers.mobian.buider.demo02; public class Worker extends Builder{ private Product product; public Worker() { product = new Product(); } @Override Builder builderA(String msg) { product.setBuilderA(msg); return this; } @Override Builder builderB(String msg) { product.setBuilderB(msg); return this; } @Override Builder builderC(String msg) { product.setBuilderC(msg); return this; } @Override Builder builderD(String msg) { product.setBuilderD(msg); return this; } @Override Product getProduct() { return product; } }
-
用户测试类:Test.java
package pers.mobian.buider.demo02; //类似于之前是工程师Directer public class Test { public static void main(String[] args) { Worker worker = new Worker(); //默认套餐 System.out.println(worker.getProduct().toString()); //用户修改以后的套餐,此处可以使用链式编程,直接修改好一整个菜单 Product product = worker.builderA("白开水1号").builderB("白开水2号").builderC("白开水3号").builderD("白开水4号"). getProduct(); System.out.println(product.toString()); } }
-
结果:
Product{builderA='青椒肉丝', builderB='豆腐汤', builderC='白米饭', builderD='咸菜'} Product{builderA='白开水1号', builderB='白开水2号', builderC='白开水3号', builderD='白开水4号'}
总结:此方式比第一种更加的灵活。
3、与抽象工厂做比较
- 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族
- 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象。
- 如果将抽象工厂模式看出汽车而配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件得组装可以返回一辆完整得汽车。