【设计模式】14.创建型模式-建造者(Builder)
一、描述
是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。建造者模式按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)。
角色包含以下分类:
产品(Product):产品以及其包含的属性。
抽象建造者(AbstractBuilder):包含产品实体、建造产品属性的抽象方法、返回当前产品的方法。
具体建造者(ConstructBuilder):实现抽象建造者产品属性的抽象方法。
指挥者(Director):指定生产产品的建造流程。
类图如下:
二、优点
1.使用建造者模式可以使客户端不必知道产品内部组成的细节
2.具体的建造者类之间是相互独立的,这有利于系统的扩展
3.具体的建造者相互独立,因此可以对建造的过程逐步细化,而不会对其他模块产生任何影响
三、缺点
1.建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制
2.如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大
四、与工厂模式(工厂方法模式、抽象工厂模式)区别
1.适用场景:
(1)在工厂模式中,工厂方法模式适用于多种产品创建场景,抽象工厂模式适用于多种品牌产品创建的场景。
(2)建造者模式适用于创建一系列相同属性,相同过程的产品。
2.结构上:
(1)工厂模式中,需要通过工厂创建特定产品。
(2)建造者模式中,需要通过建造者逐个赋予产品属性创建产品。
五、示例
以“房”为例,无论是普通房子还是别墅,盖房(即建造者)步骤分为设计房屋结构、打地基、砌筑墙体、封顶、装修等六个步骤。房子(即产品)由设计图、地基、墙体、房顶、装修几部分组成。
有两种方式:原始建造者模式以及注解版建造者模式。首先介绍原始建造者模式:
代码:
(1)房:House
@Data public class House { /** 设计图 */ private String design; /** 地基 */ private String foundation; /** 墙体 */ private String wall; /** 房顶 */ private String roof; /** 装修 */ private String decorate; }
(2)建造者抽象类:HouseBuilder
public abstract class HouseBuilder { public House house = new House(); /** * 设计图纸 */ public abstract void setDesign(); /** * 打地基 */ public abstract void setFoundation(); /** * 砌墙 */ public abstract void setWall(); /** * 封顶 */ public abstract void setRoof(); /** * 装修 */ public abstract void setDecorate(); public House getHouse() { return house; } }
(3)平房:BungalowBuilder
public class BungalowBuilder extends HouseBuilder { /** * 设计图纸 */ public void setDesign() { house.setDesign("平房:设计图纸"); } /** * 打地基 */ public void setFoundation() { house.setFoundation("平房:打地基"); } /** * 砌墙 */ public void setWall() { house.setWall("平房:砌墙"); } /** * 封顶 */ public void setRoof() { house.setRoof("平房:封顶"); } /** * 装修 */ public void setDecorate() { house.setDecorate("平房:装修"); } }
(4)别墅:VillaBuilder
public class VillaBuilder extends HouseBuilder { /** * 设计图纸 */ public void setDesign() { house.setDesign("别墅:设计图纸"); } /** * 打地基 */ public void setFoundation() { house.setFoundation("别墅:打地基"); } /** * 砌墙 */ public void setWall() { house.setFoundation("别墅:砌墙"); } /** * 封顶 */ public void setRoof() { house.setFoundation("别墅:封顶"); } /** * 装修 */ public void setDecorate() { house.setFoundation("别墅:装修"); } }
(5)指挥者,负责调用建造者盖房顺序:Director
public class Director { public HouseBuilder houseBuilder; public void setHouseBuilder(HouseBuilder houseBuilder) { this.houseBuilder = houseBuilder; } public void construct() { houseBuilder.setDesign(); houseBuilder.setFoundation(); houseBuilder.setWall(); houseBuilder.setRoof(); houseBuilder.setDecorate(); } public House getHouse() { return houseBuilder.getHouse(); } }
(6)客户,调用指挥者盖房:client
public class Client { public static void main(String[] args) { BungalowBuilder bungalowBuilder = new BungalowBuilder(); Director director = new Director(); director.setHouseBuilder(bungalowBuilder); director.construct(); House house = director.getHouse(); System.out.println(house.toString()); } }
效果:
注解式的建造者模式:
客
1.房子:house
@Data @Builder public class House { /** 设计图 */ private String design; /** 地基 */ private String foundation; /** 墙体 */ private String wall; /** 房顶 */ private String roof; /** 装修 */ private String decorate; }
2.客户:client
public class Client { public static void main(String[] args) { House house = new House.HouseBuilder().design("图纸").foundation("地基").roof("房顶").decorate("装修").build(); System.out.println("house:" + house.toString()); } }
两种方式在现在来说,第二种注解方式是比较常用的。第二种注解方式优化了结构和代码,而且一行代码就可以省掉三四个类,真的不要太爽。现在对于建造者模式,平时主要用于某些固定格式的实体类赋值等场景。