设计模式 - 创建型模式_建造者模式
创始人
2024-05-19 11:08:02
0

文章目录

  • 创建型模式
  • 概述
  • Case
  • 模拟工程
  • Bad Impl
  • Better Impl (建造者模式重构代码)
  • 小结

在这里插入图片描述


创建型模式

创建型模式提供创建对象的机制, 能够提升已有代码的灵活性和可复⽤性。

类型实现要点
工厂方法定义⼀个创建对象的接⼝,让其⼦类⾃⼰决定实例化哪⼀个⼯⼚类,⼯⼚模式使其创建过程延迟到⼦类进⾏。
抽象工厂提供⼀个创建⼀系列相关或相互依赖对象的接⼝,⽽⽆需指定它们具体的类。
建造者将⼀个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示
原型⽤原型实例指定创建对象的种类,并且通过拷⻉这些原型创建新的对象。
单例保证⼀个类仅有⼀个实例,并提供⼀个访问它的全局访问点。

概述

在这里插入图片描述

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

举个例子玩王者荣耀的时的初始化界⾯;有三条路、有树⽊、有野怪、有守卫塔等等,甚⾄依赖于你的⽹络情况会控制清晰度。⽽当你换⼀个场景进⾏其他不同模式的选择时,同样会建设道路、树⽊、野怪等等,但是他们的摆放和⼤⼩都有不同。这⾥就可以⽤到建造者模式来初始化游戏元素。

⽽这样的根据相同的物料 ,不同的组装所产⽣出的具体的内容,就是建造者模式的最终意图,也就是将⼀个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。


Case

在这里插入图片描述

模拟装修公司对于设计出⼀些套餐装修服务的场景。

套餐服务:欧式豪华、轻奢⽥园、现代简约等等,⽽这些套餐的后⾯是不同的商品的组合。例如: ⼀级&⼆级吊顶、多乐⼠涂料、圣象地板、⻢可波罗地砖等等,按照不同的套餐的价格选取不同的品牌组合,最终再按照装修⾯积给出⼀个整体的报价。

这⾥我们就模拟装修公司想推出⼀些套餐装修服务,按照不同的价格设定品牌选择组合,以达到使⽤建造者模式的过程。


模拟工程

在这里插入图片描述
提供了装修中所需要的物料: ceilling(吊顶) 、 coat(涂料) 、 floor(地板) 、tile(地砖) ,这四项内容。

【 物料接⼝ 】

public interface Matter {/*** 场景;地板、地砖、涂料、吊顶*/String scene();/*** 品牌*/String brand();/*** 型号*/String model();/*** 平米报价*/BigDecimal price();/*** 描述*/String desc();}

物料接⼝提供了基本的信息,以保证所有的装修材料都可以按照统⼀标准进⾏获取

具体的材料,我们以一级顶为例

在这里插入图片描述

/*** 吊顶* 品牌;装修公司自带* 型号:一级顶*/
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";}}

其他材料同样的实现了Matter接口 。
在这里插入图片描述

以上就是装修配置单 ,接下我们去使⽤不同的物料组合出不同的套餐服务。


Bad Impl

讲道理没有ifelse解决不了的逻辑,不⾏就在加⼀⾏!

我们先使⽤这样很直⽩的⽅式去把功能实现出来,在通过设计模式去优化完善。

⼀个类⼏千⾏的代码你是否⻅过,那今天⻅识⼀下有这样潜质的类!


public class DecorationPackageController {public String getMatterList(BigDecimal area, Integer level) {List list = new ArrayList(); // 装修清单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 块⾥,都包含着不通的材料(吊顶,⼆级顶、涂料,⽴邦、地砖,⻢可波罗),最终⽣成装修清单和装修成本。
  • 最后提供获取装修详细信息的⽅法,返回给调⽤⽅,⽤于知道装修清单。

【单元测试】

接下来我们通过junit单元测试的⽅式验证接⼝服务,强调⽇常编写好单测可以更好的提⾼系统的健壮度。

  @Testpublic 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 元。

以上这段使⽤ ifelse ⽅式实现的代码,⽬前已经满⾜的我们的也许功能。

但随着业务的快速发展要求,会提供很多的套餐针对不同的户型。那么这段实现代码将迅速扩增到⼏千⾏,甚⾄在修修改改中,已经像膏药⼀样难以维护。

在这里插入图片描述


Better Impl (建造者模式重构代码)

接下来使⽤建造者模式来进⾏代码优化,也算是⼀次很⼩的重构

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

这⾥我们会把构建的过程交给 创建者 类,⽽创建者通过使⽤我们的 构建⼯具包 ,去构建出不同的 装修套餐。

【工程结构】
在这里插入图片描述

建造者模式代码类关系

在这里插入图片描述

建造者模型结构

在这里插入图片描述

核⼼类是建造者模式的具体实现。与 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 list = new ArrayList();  // 装修清单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());        // 地砖,东鹏}}

建造者的使⽤中就已经⾮常容易了,统⼀的建造⽅式,通过不同物料填充出不同的装修⻛格; 豪华欧式 、 轻奢⽥园 、 现代简约 ,如果将来业务扩展也可以将这部分内容配置到数据库⾃动⽣成。但整体的思想还可以使⽤创建者模式进⾏搭建。


【单元测试】

   @Testpublic 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 元。

测试结果是⼀样的,调⽤⽅式也基本类似。但是⽬前的代码结构却可以很⽅便的很有调理的进⾏扩展业务开发。⽽不是以往⼀样把所有代码都写到 ifelse ⾥⾯。


小结

当⼀些基本物料不会变,⽽其组合经常变化的时候 ,就可以选择这样的设计模式来构建代码。

此设计模式满⾜了单⼀职责原则以及可复⽤的技术、建造者独⽴、易扩展、便于控制细节⻛险。

同时当出现特别多的物料以及很多的组合后,类的不断扩展也会造成难以维护的问题。但这种设计结构模型可以把重复的内容抽象到数据库中,按照需要配置。这样就可以减少代码中⼤量的重复。

在这里插入图片描述

相关内容

热门资讯

常用商务英语口语   商务英语是以适应职场生活的语言要求为目的,内容涉及到商务活动的方方面面。下面是小编收集的常用商务...
六年级上册英语第一单元练习题   一、根据要求写单词。  1.dry(反义词)__________________  2.writ...
复活节英文怎么说 复活节英文怎么说?复活节的英语翻译是什么?复活节:Easter;"Easter,anniversar...
2008年北京奥运会主题曲 2008年北京奥运会(第29届夏季奥林匹克运动会),2008年8月8日到2008年8月24日在中华人...
英语道歉信 英语道歉信15篇  在日常生活中,道歉信的使用频率越来越高,通过道歉信,我们可以更好地解释事情发生的...
六年级英语专题训练(连词成句... 六年级英语专题训练(连词成句30题)  1. have,playhouse,many,I,toy,i...
上班迟到情况说明英语   每个人都或多或少的迟到过那么几次,因为各种原因,可能生病,可能因为交通堵车,可能是因为天气冷,有...
小学英语教学论文 小学英语教学论文范文  引导语:英语教育一直都是每个家长所器重的,那么有关小学英语教学论文要怎么写呢...
英语口语学习必看的方法技巧 英语口语学习必看的方法技巧如何才能说流利的英语? 说外语时,我们主要应做到四件事:理解、回答、提问、...
四级英语作文选:Birth ... 四级英语作文范文选:Birth controlSince the Chinese Governmen...
金融专业英语面试自我介绍 金融专业英语面试自我介绍3篇  金融专业的学生面试时,面试官要求用英语做自我介绍该怎么说。下面是小编...
我的李老师走了四年级英语日记... 我的李老师走了四年级英语日记带翻译  我上了五个学期的小学却换了六任老师,李老师是带我们班最长的语文...
小学三年级英语日记带翻译捡玉... 小学三年级英语日记带翻译捡玉米  今天,我和妈妈去外婆家,外婆家有刚剥的`玉米棒上带有玉米籽,好大的...
七年级英语优秀教学设计 七年级英语优秀教学设计  作为一位兢兢业业的人民教师,常常要写一份优秀的教学设计,教学设计是把教学原...
我的英语老师作文 我的英语老师作文(通用21篇)  在日常生活或是工作学习中,大家都有写作文的经历,对作文很是熟悉吧,...
英语老师教学经验总结 英语老师教学经验总结(通用19篇)  总结是指社会团体、企业单位和个人对某一阶段的学习、工作或其完成...
初一英语暑假作业答案 初一英语暑假作业答案  英语练习一(基础训练)第一题1.D2.H3.E4.F5.I6.A7.J8.C...
大学生的英语演讲稿 大学生的英语演讲稿范文(精选10篇)  使用正确的写作思路书写演讲稿会更加事半功倍。在现实社会中,越...
VOA美国之音英语学习网址 VOA美国之音英语学习推荐网址 美国之音网站已经成为语言学习最重要的资源站点,在互联网上还有若干网站...
商务英语期末试卷 Part I Term Translation (20%)Section A: Translate ...