做者:小傅哥
博客:https://bugstack.cn - 原創系列專題文章
html
沉澱、分享、成長,讓本身和他人都能有所收穫!😄
亂碼七糟 [luàn qī bā zāo],我時常懷疑這個成語是來形容程序猿的!
java
不管承接什麼樣的需求,是否是身邊總有那麼幾我的代碼寫的爛,可是卻時常有測試小姐姐過來聊天(求改bug)、有產品小夥伴送吃的(求寫需求)、有業務小妹妹陪着改代碼(求上線),直至領導都認爲他的工做很重要,而在旁邊的你只能蹭點吃的。數據庫
那你說,CRUD的代碼還想讓我怎麼樣?
設計模式
這樣的小夥伴,可能把代碼寫的很直接,ifelse
多用一點,知足於先臨時支持一下,想着這也沒什麼的。並且這樣的業務需求要的急又都是增刪改查的內容,實在不想作設計。而若是有人提到說好好設計下,可能也會被反對不要過渡設計。網絡
貼膏藥似的修修補補,一次比一次恐怖!
架構
第一次完成產品需求實在是很快,但互聯網的代碼不比傳統企業。在傳統行業可能一套代碼能用十年,但在互聯網高速的迭代下你的工程,一年就要變更幾十次。若是從一開始就想着只要完成功能就能夠,那麼隨之而來的是後續的需求難以承接,每次看着成片成片的代碼,實在不知如何下手。app
在研發流程規範下執行,才能寫出好程序!
微服務
一個項目的上線每每要經歷業務需求
、產品設計
、研發實現
、測試驗證
、上線部署
到正式開量
,而這其中對研發很是重要的一換就是研發實現的過程,又能夠包括爲;架構選型
、功能設計
、設計評審
、代碼實現
、代碼評審
、單測覆蓋率檢查
、編寫文檔
、提交測試
。因此在一些流程規範下,其實很難讓你隨意開發代碼。工具
開發代碼的過程不是炫技
,就像蓋房子若是不按照圖紙來修建,回首就在山牆上搭一個廚房衛浴!可能在現實場景中這很荒唐,但在功能開發中卻總有這樣的代碼。單元測試
因此咱們也須要一些設計模式的標準思想,去建設代碼結構,提高全局把控能力。
bugstack蟲洞棧
,回覆源碼下載
獲取(打開獲取的連接,找到序號18)工程 | 描述 |
---|---|
itstack-demo-design-3-00 | 場景模擬工程,模擬裝修過程當中的套餐選擇(豪華、田園、簡約) |
itstack-demo-design-3-01 | 使用一坨代碼實現業務需求,也是對ifelse的使用 |
itstack-demo-design-3-02 | 經過設計模式優化改造代碼,產生對比性從而學習 |
建造者模式所完成的內容就是經過將多個簡單對象經過一步步的組裝構建出一個複雜對象的過程。
那麼,哪裏有這樣的場景呢?
例如你玩王者榮耀的時的初始化界面;有三條路、有樹木、有野怪、有守衛塔等等,甚至依賴於你的網絡狀況會控制清晰度。而當你換一個場景進行其餘不一樣模式的選擇時,一樣會建設道路、樹木、野怪等等,可是他們的擺放和大小都有不一樣。這裏就能夠用到建造者模式來初始化遊戲元素。
而這樣的根據相同的物料
,不一樣的組裝所產生出的具體的內容,就是建造者模式的最終意圖,也就是;將一個複雜的構建與其表示相分離,使得一樣的構建過程能夠建立不一樣的表示。
這裏咱們模擬裝修公司對於設計出一些套餐裝修服務的場景。
不少裝修公司都會給出自家的套餐服務,通常有;歐式豪華、輕奢田園、現代簡約等等,而這些套餐的後面是不一樣的商品的組合。例如;一級&二級吊頂、多樂士塗料、聖象地板、馬可波羅地磚等等,按照不一樣的套餐的價格選取不一樣的品牌組合,最終再按照裝修面積給出一個總體的報價。
這裏咱們就模擬裝修公司想推出一些套餐裝修服務,按照不一樣的價格設定品牌選擇組合,以達到使用建造者模式的過程。
itstack-demo-design-3-00 └── src └── main └── java └── org.itstack.demo.design ├── ceilling │ ├── LevelOneCeiling.java │ └── LevelTwoCeiling.java ├── coat │ ├── DuluxCoat.java │ └── LiBangCoat.java │ └── LevelTwoCeiling.java ├── floor │ ├── DerFloor.java │ └── ShengXiangFloor.java ├── tile │ ├── DongPengTile.java │ └── MarcoPoloTile.java └── Matter.java
在模擬工程中提供了裝修中所須要的物料;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"; } }
二級頂
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,要是層高很高,也可增長每級的厚度"; } }
多樂士
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 "立邦始終以開發綠色產品、注重高科技、高品質爲目標,以技術力量不斷推動科研和開發,知足消費者需求。"; } }
德爾
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 "聖象地板是中國地板行業著名品牌。聖象地板擁有中國馳名商標、中國名牌、國家免檢、中國環境標誌認證等多項榮譽。"; } }
東鵬
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年,做爲國內最先品牌化的建陶品牌,以「文化陶瓷」佔領市場,享有「仿古磚至尊」的美譽。"; } }
裝修配置單
,接下咱們會經過案例去使用不一樣的物料組合出不一樣的套餐服務。講道理沒有ifelse解決不了的邏輯,不行就在加一行!
每個章節中咱們都會使用這樣很直白的方式去把功能實現出來,在經過設計模式去優化完善。這樣的代碼結構也都是很是簡單的,沒有複雜的類關係結構,都是直來直去的代碼。除了咱們常常強調的這樣的代碼不能很好的擴展外,作一些例子demo工程仍是能夠的。
itstack-demo-design-3-01 └── src └── main └── java └── org.itstack.demo.design └── DecorationPackageController.java
一個類幾千行的代碼你是否見過,嚯?那今天就讓你見識一下有這樣潛質的類!
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(); } }
if
塊裏,都包含着不通的材料(吊頂,二級頂、塗料,立邦、地磚,馬可波羅),最終生成裝修清單和裝修成本。接下來咱們經過junit單元測試的方式驗證接口服務,強調平常編寫好單測能夠更好的提升系統的健壯度。
編寫測試類:
@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 元。 地磚:東鵬瓷磚、1000一、平米價格:102 元。 Process finished with exit code 0
ifelse
方式實現的代碼,目前已經知足的咱們的也許功能。但隨着老闆對業務的快速發展要求,會提供不少的套餐針對不一樣的戶型。那麼這段實現代碼將迅速擴增到幾千行,甚至在修修改改中,已經像膏藥同樣難以維護。接下來使用建造者模式來進行代碼優化,也算是一次很小的重構。
建造者模式主要解決的問題是在軟件系統中,有時候面臨着"一個複雜對象"的建立工做,其一般由各個部分的子對象用必定的過程構成;因爲需求的變化,這個複雜對象的各個部分常常面臨着重大的變化,可是將它們組合在一塊兒的過程卻相對穩定。
這裏咱們會把構建的過程交給建立者
類,而建立者經過使用咱們的構建工具包
,去構建出不一樣的裝修套餐
。
itstack-demo-design-3-02 └── src ├── main │ └── java │ └── org.itstack.demo.design │ ├── Builder.java │ ├── DecorationPackageMenu.java │ └── IMenu.java └── test └── java └── org.itstack.demo.design.test └── ApiTest.java
建造者模型結構
工程中有三個核心類和一個測試類,核心類是建造者模式的具體實現。與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()); // 地磚,東鵬 } }
豪華歐式
、輕奢田園
、現代簡約
,若是未來業務擴展也能夠將這部份內容配置到數據庫自動生成。但總體的思想還可使用建立者模式進行搭建。編寫測試類:
@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 元。 地磚:東鵬瓷磚、1000一、平米價格:102 元。 Process finished with exit code 0
ifelse
裏面。一些基本物料不會變,而其組合常常變化的時候
,就能夠選擇這樣的設計模式來構建代碼。