【设计模式】建造者模式

news/2024/10/18 8:22:59/

【设计模式】建造者模式

参考资料:

重学 Java 设计模式:实战建造者模式「各项装修物料组合套餐选配场景」

建造者模式——链式调用

五分钟彻底了解建造者模式

文章目录

  • 【设计模式】建造者模式
    • 一、建造者模式介绍
      • 1.1、定义
      • 1.2、角色概述
    • 二、案例场景模拟
      • 2.1、场景简述
      • 2.2、if/else实现需求
        • 2.2.1、测试验证
      • 2.3、建造者模式重构代码
        • 2.3.1、测试验证
    • 三、总结
      • 3.1、建造者模式的应用场景
      • 3.2、建造者模式优缺点
      • 3.3、建造者模式和工厂模式的区别

一、建造者模式介绍

1.1、定义

官方定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

通俗解读

提供一种创建对象的方式,创建的东西细节复杂,还必须暴露给使用者。屏蔽过程而不屏蔽细节

建造者模式所完成的内容就是通过将多个简单对象通过一步步的组装构建出一个复杂对象的过程。

类似建房子,只需要把材料和设计图纸给工人,就能建成想要的房子,不关注工人建房子的过程,但对于细节,我们又可以自己设计。

image-20230504113212108

1.2、角色概述

建造者模式结构图:

image-20230504113457316

  • Builder(抽象建造者):它为创建一个产品Product对象的各个部件指定抽象接口,在该接口中一般声明两类方法,一类方法是buildPart(),它们用于创建复杂对象的各个部件;另一类方法是getResult(),它们用于返回复杂对象。Builder既可以是抽象类,也可以是接口。
  • ConcreteBuilder(具体建造者):它实现了Builder接口,实现各个部件的具体构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。
  • Product(产品角色):它是被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程。
  • Director(指挥者):指挥者又称为导演类,它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件和反射机制),然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。

二、案例场景模拟

2.1、场景简述

物料接口

public interface Matter {String scene();      // 场景;地板、地砖、涂料、吊顶String brand();      // 品牌String model();      // 型号BigDecimal price();  // 价格String desc();       // 描述}  
  • 物料接口提供了基本的信息,以保证所有的装修材料都可以按照统一标准进行获取。

吊顶(ceiling)

一级顶

public class LevelOneCeiling implements Matter {public String scene() {return "吊顶";}public String brand() {return "装修公司自带";}public String model() {return "一级顶";}public BigDecimal price() {return new BigDecimal(260);}public String desc() {return "造型只做低一级,只有一个层次的吊顶,一般离顶120-150mm";}}

二级顶

public class LevelTwoCeiling  implements Matter {public String scene() {return "吊顶";}public String brand() {return "装修公司自带";}public String model() {return "二级顶";}public BigDecimal price() {return new BigDecimal(850);}public String desc() {return "两个层次的吊顶,二级吊顶高度一般就往下吊20cm,要是层高很高,也可增加每级的厚度";}}

涂料(coat)

多乐士

public class DuluxCoat  implements Matter {public String scene() {return "涂料";}public String brand() {return "多乐士(Dulux)";}public String model() {return "第二代";}public BigDecimal price() {return new BigDecimal(719);}public String desc() {return "多乐士是阿克苏诺贝尔旗下的著名建筑装饰油漆品牌,产品畅销于全球100个国家,每年全球有5000万户家庭使用多乐士油漆。";}}

立邦

public class LiBangCoat implements Matter {public String scene() {return "涂料";}public String brand() {return "立邦";}public String model() {return "默认级别";}public BigDecimal price() {return new BigDecimal(650);}public String desc() {return "立邦始终以开发绿色产品、注重高科技、高品质为目标,以技术力量不断推进科研和开发,满足消费者需求。";}}  

地板(floor)

德尔

public class DerFloor implements Matter {public String scene() {return "地板";}public String brand() {return "德尔(Der)";}public String model() {return "A+";}public BigDecimal price() {return new BigDecimal(119);}public String desc() {return "DER德尔集团是全球领先的专业木地板制造商,北京2008年奥运会家装和公装地板供应商";}}   

圣象

public class ShengXiangFloor implements Matter {public String scene() {return "地板";}public String brand() {return "圣象";}public String model() {return "一级";}public BigDecimal price() {return new BigDecimal(318);}public String desc() {return "圣象地板是中国地板行业著名品牌。圣象地板拥有中国驰名商标、中国名牌、国家免检、中国环境标志认证等多项荣誉。";}}   

地砖(tile)

东鹏

public class DongPengTile implements Matter {public String scene() {return "地砖";}public String brand() {return "东鹏瓷砖";}public String model() {return "10001";}public BigDecimal price() {return new BigDecimal(102);}public String desc() {return "东鹏瓷砖以品质铸就品牌,科技推动品牌,口碑传播品牌为宗旨,2014年品牌价值132.35亿元,位列建陶行业榜首。";}}

马可波罗

public class MarcoPoloTile implements Matter {public String scene() {return "地砖";}public String brand() {return "马可波罗(MARCO POLO)";}public String model() {return "缺省";}public BigDecimal price() {return new BigDecimal(140);}public String desc() {return "“马可波罗”品牌诞生于1996年,作为国内最早品牌化的建陶品牌,以“文化陶瓷”占领市场,享有“仿古砖至尊”的美誉。";}}
  • 以上就是本次装修公司所提供的装修配置单,接下我们会通过案例去使用不同的物料组合出不同的套餐服务。

2.2、if/else实现需求

public class DecorationPackageController {public String getMatterList(BigDecimal area, Integer level) {List<Matter> list = new ArrayList<Matter>(); // 装修清单BigDecimal price = BigDecimal.ZERO;          // 装修价格// 豪华欧式if (1 == level) {LevelTwoCeiling levelTwoCeiling = new LevelTwoCeiling(); // 吊顶,二级顶DuluxCoat duluxCoat = new DuluxCoat();                   // 涂料,多乐士ShengXiangFloor shengXiangFloor = new ShengXiangFloor(); // 地板,圣象list.add(levelTwoCeiling);list.add(duluxCoat);list.add(shengXiangFloor);price = price.add(area.multiply(new BigDecimal("0.2")).multiply(levelTwoCeiling.price()));price = price.add(area.multiply(new BigDecimal("1.4")).multiply(duluxCoat.price()));price = price.add(area.multiply(shengXiangFloor.price()));}// 轻奢田园if (2 == level) {LevelTwoCeiling levelTwoCeiling = new LevelTwoCeiling(); // 吊顶,二级顶LiBangCoat liBangCoat = new LiBangCoat();                // 涂料,立邦MarcoPoloTile marcoPoloTile = new MarcoPoloTile();       // 地砖,马可波罗list.add(levelTwoCeiling);list.add(liBangCoat);list.add(marcoPoloTile);price = price.add(area.multiply(new BigDecimal("0.2")).multiply(levelTwoCeiling.price()));price = price.add(area.multiply(new BigDecimal("1.4")).multiply(liBangCoat.price()));price = price.add(area.multiply(marcoPoloTile.price()));}// 现代简约if (3 == level) {LevelOneCeiling levelOneCeiling = new LevelOneCeiling();  // 吊顶,二级顶LiBangCoat liBangCoat = new LiBangCoat();                 // 涂料,立邦DongPengTile dongPengTile = new DongPengTile();           // 地砖,东鹏list.add(levelOneCeiling);list.add(liBangCoat);list.add(dongPengTile);price = price.add(area.multiply(new BigDecimal("0.2")).multiply(levelOneCeiling.price()));price = price.add(area.multiply(new BigDecimal("1.4")).multiply(liBangCoat.price()));price = price.add(area.multiply(dongPengTile.price()));}StringBuilder detail = new StringBuilder("\r\n-------------------------------------------------------\r\n" +"装修清单" + "\r\n" +"套餐等级:" + level + "\r\n" +"套餐价格:" + price.setScale(2, BigDecimal.ROUND_HALF_UP) + " 元\r\n" +"房屋面积:" + area.doubleValue() + " 平米\r\n" +"材料清单:\r\n");for (Matter matter: list) {detail.append(matter.scene()).append(":").append(matter.brand()).append("、").append(matter.model()).append("、平米价格:").append(matter.price()).append(" 元。\n");}return detail.toString();}}
  • 首先这段代码所要解决的问题就是接收入参;装修面积(area)、装修等级(level),根据不同类型的装修等级选择不同的材料。
  • 其次在实现过程中可以看到每一段if块里,都包含着不通的材料(吊顶,二级顶、涂料,立邦、地砖,马可波罗),最终生成装修清单和装修成本。
  • 最后提供获取装修详细信息的方法,返回给调用方,用于知道装修清单。

2.2.1、测试验证

编写测试类:

@Test
public void test_DecorationPackageController(){DecorationPackageController decoration = new DecorationPackageController();// 豪华欧式System.out.println(decoration.getMatterList(new BigDecimal("132.52"),1));// 轻奢田园System.out.println(decoration.getMatterList(new BigDecimal("98.25"),2));// 现代简约System.out.println(decoration.getMatterList(new BigDecimal("85.43"),3));
}

结果:

-------------------------------------------------------
装修清单
套餐等级:1
套餐价格:198064.39 元
房屋面积:132.52 平米
材料清单:
吊顶:装修公司自带、二级顶、平米价格:850 元。
涂料:多乐士(Dulux)、第二代、平米价格:719 元。
地板:圣象、一级、平米价格:318 元。-------------------------------------------------------
装修清单
套餐等级:2
套餐价格:119865.00 元
房屋面积:98.25 平米
材料清单:
吊顶:装修公司自带、二级顶、平米价格:850 元。
涂料:立邦、默认级别、平米价格:650 元。
地砖:马可波罗(MARCO POLO)、缺省、平米价格:140 元。-------------------------------------------------------
装修清单
套餐等级:3
套餐价格:90897.52 元
房屋面积:85.43 平米
材料清单:
吊顶:装修公司自带、一级顶、平米价格:260 元。
涂料:立邦、默认级别、平米价格:650 元。
地砖:东鹏瓷砖、10001、平米价格:102 元。Process finished with exit code 0
  • 看到输出的这个结果,已经很有装修公司提供报价单的感觉了。以上这段使用ifelse方式实现的代码,目前已经满足的我们的一些功能。但随着老板对业务的快速发展要求,会提供很多的套餐针对不同的户型。那么这段实现代码将迅速扩增到几千行,甚至在修修改改中,已经像膏药一样难以维护。

2.3、建造者模式重构代码

建造者模式主要解决的问题是在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的过程构成;由于需求的变化,这个复杂对象的各个部分经常面临着重大的变化,但是将它们组合在一起的过程却相对稳定。

这里我们会把构建的过程交给创建者类,而创建者通过使用我们的构建工具包,去构建出不同的装修套餐

建造者模型结构

image-20230504131758137

工程中有三个核心类和一个测试类,核心类是建造者模式的具体实现。与ifelse实现方式相比,多出来了两个二外的类。具体功能如下;

  • Builder,建造者类具体的各种组装由此类实现。
  • DecorationPackageMenu,是IMenu接口的实现类,主要是承载建造过程中的填充器。相当于这是一套承载物料和创建者中间衔接的内容。

,那么接下来会分别讲解几个类的具体实现。

定义装修包接口

public interface IMenu {IMenu appendCeiling(Matter matter); // 吊顶IMenu appendCoat(Matter matter);    // 涂料IMenu appendFloor(Matter matter);   // 地板IMenu appendTile(Matter matter);    // 地砖String getDetail();                 // 明细 }
  • 接口类中定义了填充各项物料的方法;吊顶涂料地板地砖,以及最终提供获取全部明细的方法。

装修包实现

public class DecorationPackageMenu implements IMenu {private List<Matter> list = new ArrayList<Matter>();  // 装修清单private BigDecimal price = BigDecimal.ZERO;      // 装修价格private BigDecimal area;  // 面积private String grade;     // 装修等级;豪华欧式、轻奢田园、现代简约private DecorationPackageMenu() {}public DecorationPackageMenu(Double area, String grade) {this.area = new BigDecimal(area);this.grade = grade;}public IMenu appendCeiling(Matter matter) {list.add(matter);price = price.add(area.multiply(new BigDecimal("0.2")).multiply(matter.price()));return this;}public IMenu appendCoat(Matter matter) {list.add(matter);price = price.add(area.multiply(new BigDecimal("1.4")).multiply(matter.price()));return this;}public IMenu appendFloor(Matter matter) {list.add(matter);price = price.add(area.multiply(matter.price()));return this;}public IMenu appendTile(Matter matter) {list.add(matter);price = price.add(area.multiply(matter.price()));return this;}public String getDetail() {StringBuilder detail = new StringBuilder("\r\n-------------------------------------------------------\r\n" +"装修清单" + "\r\n" +"套餐等级:" + grade + "\r\n" +"套餐价格:" + price.setScale(2, BigDecimal.ROUND_HALF_UP) + " 元\r\n" +"房屋面积:" + area.doubleValue() + " 平米\r\n" +"材料清单:\r\n");for (Matter matter: list) {detail.append(matter.scene()).append(":").append(matter.brand()).append("、").append(matter.model()).append("、平米价格:").append(matter.price()).append(" 元。\n");}return detail.toString();}}
  • 装修包的实现中每一个方法都会了 this,也就可以非常方便的用于连续填充各项物料。
  • 同时在填充时也会根据物料计算平米数下的报价,吊顶和涂料按照平米数适量乘以常数计算。
  • 最后同样提供了统一的获取装修清单的明细方法

建造者方法

public class Builder {public IMenu levelOne(Double area) {return new DecorationPackageMenu(area, "豪华欧式").appendCeiling(new LevelTwoCeiling())    // 吊顶,二级顶.appendCoat(new DuluxCoat())             // 涂料,多乐士.appendFloor(new ShengXiangFloor());     // 地板,圣象}public IMenu levelTwo(Double area){return new DecorationPackageMenu(area, "轻奢田园").appendCeiling(new LevelTwoCeiling())   // 吊顶,二级顶.appendCoat(new LiBangCoat())           // 涂料,立邦.appendTile(new MarcoPoloTile());       // 地砖,马可波罗}public IMenu levelThree(Double area){return new DecorationPackageMenu(area, "现代简约").appendCeiling(new LevelOneCeiling())   // 吊顶,二级顶.appendCoat(new LiBangCoat())           // 涂料,立邦.appendTile(new DongPengTile());        // 地砖,东鹏}}

2.3.1、测试验证

编写测试类:

@Test
public void test_Builder(){Builder builder = new Builder();// 豪华欧式System.out.println(builder.levelOne(132.52D).getDetail());// 轻奢田园System.out.println(builder.levelTwo(98.25D).getDetail());// 现代简约System.out.println(builder.levelThree(85.43D).getDetail());
}

结果:

-------------------------------------------------------
装修清单
套餐等级:豪华欧式
套餐价格:198064.39 元
房屋面积:132.52 平米
材料清单:
吊顶:装修公司自带、二级顶、平米价格:850 元。
涂料:多乐士(Dulux)、第二代、平米价格:719 元。
地板:圣象、一级、平米价格:318 元。-------------------------------------------------------
装修清单
套餐等级:轻奢田园
套餐价格:119865.00 元
房屋面积:98.25 平米
材料清单:
吊顶:装修公司自带、二级顶、平米价格:850 元。
涂料:立邦、默认级别、平米价格:650 元。
地砖:马可波罗(MARCO POLO)、缺省、平米价格:140 元。-------------------------------------------------------
装修清单
套餐等级:现代简约
套餐价格:90897.52 元
房屋面积:85.43 平米
材料清单:
吊顶:装修公司自带、一级顶、平米价格:260 元。
涂料:立邦、默认级别、平米价格:650 元。
地砖:东鹏瓷砖、10001、平米价格:102 元。Process finished with exit code 0
  • 测试结果是一样的,调用方式也基本类似。但是目前的代码结构却可以让你很方便的很有调理的进行扩展业务开发。而不是像以往一样把所有代码都写到ifelse里面。

三、总结

3.1、建造者模式的应用场景

  1. 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
  2. 当你的对象的构造方法有三四个甚至更多的时候可以使用建造者模式替代
  3. 当初始化一个对象特别复杂,参数多,而且很多参数都有默认值的时候

3.2、建造者模式优缺点

优点:

  1. 在建造者模式中,客户不必知道产品内部的组成细节
  2. 封装性好,创建和使用隔离
  3. 扩展性好,建造类直接独立,一定程度上解耦

缺点:

  1. 产生多余的建造类
  2. 产品内部发生变化,建造者都要修改,违反开闭原则同时修改成本也比较大

3.3、建造者模式和工厂模式的区别

  • 建造者模式更加注重方法的调用顺序,工厂模式注重创建对象
  • 创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工程模式创建出来的都一样(这里可能有人疑问为啥一样,应为建造者模式对象的一些属性我们都是可以赋值的,可以有的有有的没有自然对象也就不一样了。工厂模式不一样只有一种创建方式要么属性全都有要么全没有所以一样)
  • 关注维度不一样,工厂只关注对象的创建,建造者不仅要创建,还要知道这个对象是由那些部件组成的
  • 建造者模式根据建造过程中的顺序不一样,最终的对象部件组成也有可能是不一样的(那些不关系调用顺序的类可就是一样的)

http://www.ppmy.cn/news/63352.html

相关文章

Acid burn(★★)

运行程序 先是弹出一个neg 然后是真正的程序界面 有一个输入Serial和Name的判断 还有一个只输入Serial的判断 查壳 没有壳&#xff0c;是Delphi程序 先除去一个Neg 找到Neg弹出的程序&#xff0c;在程序头下个断&#xff0c;运行程序&#xff0c;此时栈顶是调用此功能的…

Gradle使用

下载Gradle Gradle Distributions 配置环境变量 测试是否成功 cmd输入gradle -v 在.gradle目录下创建一个init.gradle allprojects { repositories { maven { url file:///D:/maven/myRepository} ## 这里是本地maven仓库地址,没有就会依次向下设置的地址寻…

将ssh发布密钥添加到服务器的ssh授权密钥中,但是为什么我仍然无法ssh登录到此服务器?

我已经将ssh发布密钥添加到服务器的ssh授权密钥中&#xff0c;但是为什么我仍然无法ssh登录到此服务器&#xff1f; 即使将ssh公钥添加到服务器的授权密钥中&#xff0c;您也可能无法通过SSH登录到服务器&#xff0c;这有几个原因: 1.服务器的authorized_keys文件的权限不正确…

微信小程序商城搭建--后端+前端+小程序端

介绍&#xff1a; 前端技术&#xff1a;React、AntdesignPro、umi、JavaScript、ES6、TypeScript、 小程序 后端技术&#xff1a;Springboot、Mybatis、Spring、Mysql 软件架构&#xff1a; 后端采用Springboot搭配前端React进行开发&#xff0c;完成用户管理、轮播图管理、…

django:django2配置websocket

源码地址&#xff1a; https://gitee.com/liuhaizhang/django2-configuring-websockethttps://gitee.com/liuhaizhang/django2-configuring-websocket python3.9.0 django2.2.1 channels2.2.0 项目结构&#xff1a; test_websocket_django2 -chat -home -test_websocket_dja…

用ascii画十二生肖

十二生肖&#xff0c;也称为“十二属相”&#xff0c;是中国传统的生肖分类法&#xff0c;它将时间和人的出生年份联系起来&#xff0c;以十二种动物为代表来区分不同的年份和人的性格特征。这十二种动物分别为鼠、牛、虎、兔、龙、蛇、马、羊、猴、鸡、狗和猪&#xff0c;每个…

一体化管理系统如何使企业受益?

在当今世界&#xff0c;大多数企业使用管理系统来制定政策和程序&#xff0c;以帮助实现其业务目标。管理体系是用于确保企业能够管理实现其目标所需的所有任务的流程和程序的结构。 公司或企业会有支持客户、采购、项目、考勤、财务等管理系统&#xff0c;这些管理系统通常在…

树的储存结构和表示法_20230506

树的储存结构和表示法 前言 树是一类非常重要的数据结构&#xff0c;它是图和其它更高阶数据的基础&#xff0c;人们对树的储存结构和表示法进行了大量研究&#xff0c;这里介绍三种常见的链表结构来表示树的基本方法。 树的双亲表示法 假设以一组连续空间储存数据的结点&a…