建造模式详解
理解
当创建一个复杂对象时,这个对象由各个部分的子对象构成,这个复杂对象的各个部分经常面临着剧烈的变化。我理解的主要有两种建造模式,一些基本部件不会变,而其组合经常变化的时候,例如餐厅点餐;另外一种是,组合不会改变,或是部件的装配的顺序一定时,例如电脑的构建。
示例
随意组合的建造模式
模拟餐厅点餐的系统:其中提供Edible接口,代表可食用的食物,若干食物类对其实现。Combo是套餐容器类,ComboBuilder可建造出套餐。
接口:
public interface Edible {
void eat();
}
public interface Dishes extends Edible {
}
public interface Drinks extends Edible {
}
public interface Meal extends Edible {
}
实现:
public class Bread implements Meal {
@Override
public void eat() {
System.out.println("bread was eaten");
}
}
public class Cola implements Drinks {
@Override
public void eat() {
System.out.println("cola was drunk");
}
}
public class FriedChicken implements Dishes {
@Override
public void eat() {
System.out.println("friedChicken was eaten");
}
}
public class FryMeat implements Dishes {
@Override
public void eat() {
System.out.println("FryMeat was eaten");
}
}
public class Juice implements Drinks {
@Override
public void eat() {
System.out.println("juice was drunk");
}
}
public class Noodle implements Meal {
@Override
public void eat() {
System.out.println("noodle was eaten");
}
}
public class Rice implements Meal {
@Override
public void eat() {
System.out.println("rice was eaten");
}
}
public class Soup implements Drinks {
@Override
public void eat() {
System.out.println("soup was drunk");
}
}
套餐容器类:
public class Combo {
List<Edible> combo;
Combo() {
this.combo = new ArrayList<>();
}
public List<Edible> getCombo() {
return combo;
}
}
套餐构建类
public class ComboBuilder {
Combo combo;
public ComboBuilder() {
combo = new Combo();
}
public ComboBuilder append(Edible edible) {
combo.getCombo().add(edible);
return this;
}
public Combo build() {
return combo;
}
}
测试Main方法
public static void main(String[] args) {
System.out.println("套餐一");
Combo combo = new ComboBuilder().append(new Rice()).append(new Juice()).append(new FryMeat()).build();
combo.getCombo().forEach(Edible::eat);
System.out.println();
System.out.println("套餐二");
Combo combo1 = new ComboBuilder().append(new Bread()).append(new Cola()).append(new FriedChicken()).build();
combo1.getCombo().forEach(Edible::eat);
}
/*output:
套餐一
rice was eaten
juice was drunk
FryMeat was eaten
套餐二
bread was eaten
cola was drunk
friedChicken was eaten
*/
有导演角色的建造者模式
模拟餐厅构建电脑的系统:其中提供ComputerBuilder接口,代表构建电脑的接口。ComputerDirector是电脑导演类,Computer即使电脑实体类,由其中四个部件接口构成。
电脑实体和其部件接口
@Data//lombok插件,构建属性的get和set方法,toString()等等。
public class Computer {
private Host host;
private Keyboard keyboard;
private Monitor monitor;
private Mouse mouse;
public void toUse() {
host.use();
keyboard.use();
monitor.use();
mouse.use();
}
}
public interface Host {
void use();
}
public interface Keyboard {
void use();
}
public interface Monitor {
void use();
}
public interface Mouse {
void use();
}
构建电脑类及其实现,这里使用了lamda表达式实现部件接口
public interface ComputerBuilder {
void buildHost();
void buildKeyboard();
void buildMouse();
void buildMonitor();
Computer build();
}
public class HpComputerBuilder implements ComputerBuilder {
private Computer computer;
public HpComputerBuilder() {
computer = new Computer();
}
public void buildHost() {
computer.setHost(() -> System.out.println("hp host used"));
}
public void buildKeyboard() {
computer.setKeyboard(() -> System.out.println("hp keyboard used"));
}
public void buildMouse() {
computer.setMouse(() -> System.out.println("hp mouse used"));
}
public void buildMonitor() {
computer.setMonitor(() -> System.out.println("hp monitor used"));
}
public Computer build() {
return computer;
}
}
public class HwComputerBuilder implements ComputerBuilder {
private Computer computer;
public HwComputerBuilder() {
computer = new Computer();
}
public void buildHost() {
computer.setHost(() -> System.out.println("hw host used"));
}
public void buildKeyboard() {
computer.setKeyboard(() -> System.out.println("hw keyboard used"));
}
public void buildMouse() {
computer.setMouse(() -> System.out.println("hw mouse used"));
}
public void buildMonitor() {
computer.setMonitor(() -> System.out.println("hw monitor used"));
}
public Computer build() {
return computer;
}
}
导演类,控制构建的顺序,结构等
public class ComputerDirector {
public void construct(ComputerBuilder computerBuilder) {
computerBuilder.buildHost();
computerBuilder.buildKeyboard();
computerBuilder.buildMonitor();
computerBuilder.buildMouse();
}
}
测试类
public static void main(String[] args) {
//新建导演对象
ComputerDirector computerDirector = new ComputerDirector();
//新建惠普电脑构建对象
ComputerBuilder hpComputerBuilder = new HpComputerBuilder();
//导演对象指导惠普构建
computerDirector.construct(hpComputerBuilder);
//构建对象构建出电脑
Computer hpComputer = hpComputerBuilder.build();
//打印出结果
hpComputer.toUse();
System.out.println("----------------------- ");
//同理构建出华为电脑
ComputerBuilder hwComputerBuilder = new HwComputerBuilder();
computerDirector.construct(hwComputerBuilder);
Computer hwComputer = hwComputerBuilder.build();
hwComputer.toUse();
}
/*output:
hp host used
hp keyboard used
hp monitor used
hp mouse used
-----------------------
hw host used
hw keyboard used
hw monitor used
hw mouse used
*/
使用场景
- 需要生成的对象具有复杂的内部结构
- 一些基本部件不会变,而其组合经常变化的时候(随意组合的建造模式)
- 关注与零件装配的顺序(有导演角色的建造者模式)
运用
- jdk的StringBuilder就是经典的随意组合的建造模式
- lombok中@builder就是对实体类使用随意组合的建造模式
- protobuf生成出来的java类也是使用随意组合的建造模式
总结
优点:
随意组合的建造模式将构建的细节暴露出来,可以方便组合;有导演角色的建造者模式将构建类抽象,又提供了导演类指导构建过程,使建造者独立,易扩展。
缺点:
对于随意组合的建造模式来说,构建的组合必须受限于组合的元素有共同点;对于有导演角色的建造者模式,当构建的对象内部结构发生变化时,不易扩展。
抽象工厂模式与有导演角色的建造者模式的区别
抽象工厂重点在于构建一系列的产品族,建造者模式重点在于构建出部件组成的整体产品。以上放的电脑举个例子:如果使用抽象工厂模式,重点就是生产主机,键盘,鼠标,显示器等系列产品;如果使用的是建造者模式,重点就是生产由主机,键盘,鼠标,显示器这些部件构成的电脑。
注意:抽象工厂模式与有导演角色的建造者模式对于部件的扩展,或是说产品系列的扩展都是不易扩展的。对于工厂和建造者的扩展时容易扩展的。