您好,登錄后才能下訂單哦!
這篇文章主要介紹Java實踐之建造者模式的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
無論承接什么樣的需求,是不是身邊總有那么幾個人代碼寫的爛,但是卻時常有測試小姐姐過來聊天(求改bug)、有產(chǎn)品小伙伴送吃的(求寫需求)、有業(yè)務小妹妹陪著改代碼(求上線),直至領(lǐng)導都認為他的工作很重要,而在旁邊的你只能蹭點吃的。
這樣的小伙伴,可能把代碼寫的很直接,ifelse
多用一點,滿足于先臨時支持一下,想著這也沒什么的。而且這樣的業(yè)務需求要的急又都是增刪改查的內(nèi)容,實在不想做設計。而如果有人提到說好好設計下,可能也會被反對不要過渡設計。
第一次完成產(chǎn)品需求實在是很快,但互聯(lián)網(wǎng)的代碼不比傳統(tǒng)企業(yè)。在傳統(tǒng)行業(yè)可能一套代碼能用十年,但在互聯(lián)網(wǎng)高速的迭代下你的工程,一年就要變動幾十次。如果從一開始就想著只要完成功能就可以,那么隨之而來的是后續(xù)的需求難以承接,每次看著成片成片的代碼,實在不知如何下手。
在研發(fā)流程規(guī)范下執(zhí)行,才能寫出好程序!
一個項目的上線往往要經(jīng)歷業(yè)務需求
、產(chǎn)品設計
、研發(fā)實現(xiàn)
、測試驗證
、上線部署
到正式開量
,而這其中對研發(fā)非常重要的一換就是研發(fā)實現(xiàn)的過程,又可以包括為;架構(gòu)選型
、功能設計
、設計評審
、代碼實現(xiàn)
、代碼評審
、單測覆蓋率檢查
、編寫文檔
、提交測試
。所以在一些流程規(guī)范下,其實很難讓你隨意開發(fā)代碼。
開發(fā)代碼的過程不是炫技
,就像蓋房子如果不按照圖紙來修建,回首就在山墻上搭一個廚房衛(wèi)??!可能在現(xiàn)實場景中這很荒唐,但在功能開發(fā)中卻總有這樣的代碼。
所以我們也需要一些設計模式的標準思想,去建設代碼結(jié)構(gòu),提升全局把控能力。
1.JDK 1.8
2.Idea + Maven
工程 | 描述 |
---|---|
itstack-demo-design-3-00 | 場景模擬工程,模擬裝修過程中的套餐選擇(豪華、田園、簡約) |
itstack-demo-design-3-01 | 使用一坨代碼實現(xiàn)業(yè)務需求,也是對ifelse的使用 |
itstack-demo-design-3-02 | 通過設計模式優(yōu)化改造代碼,產(chǎn)生對比性從而學習 |
建造者模式所完成的內(nèi)容就是通過將多個簡單對象通過一步步的組裝構(gòu)建出一個復雜對象的過程。
那么,哪里有這樣的場景呢?
例如你玩王者榮耀的時的初始化界面;有三條路、有樹木、有野怪、有守衛(wèi)塔等等,甚至依賴于你的網(wǎng)絡情況會控制清晰度。而當你換一個場景進行其他不同模式的選擇時,同樣會建設道路、樹木、野怪等等,但是他們的擺放和大小都有不同。這里就可以用到建造者模式來初始化游戲元素。
而這樣的根據(jù)相同的物料
,不同的組裝所產(chǎn)生出的具體的內(nèi)容,就是建造者模式的最終意圖,也就是;將一個復雜的構(gòu)建與其表示相分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
這里我們模擬裝修公司對于設計出一些套餐裝修服務的場景。
很多裝修公司都會給出自家的套餐服務,一般有;歐式豪華、輕奢田園、現(xiàn)代簡約等等,而這些套餐的后面是不同的商品的組合。例如;一級&二級吊頂、多樂士涂料、圣象地板、馬可波羅地磚等等,按照不同的套餐的價格選取不同的品牌組合,最終再按照裝修面積給出一個整體的報價。
這里我們就模擬裝修公司想推出一些套餐裝修服務,按照不同的價格設定品牌選擇組合,以達到使用建造者模式的過程。
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(地磚)
,這么四項內(nèi)容。(實際的裝修物料要比這個多的多)
public interface Matter { String scene(); // 場景;地板、地磚、涂料、吊頂 String brand(); // 品牌 String model(); // 型號 BigDecimal price(); // 價格 String desc(); // 描述 }
物料接口提供了基本的信息,以保證所有的裝修材料都可以按照統(tǒng)一標準進行獲取。
一級頂
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 "多樂士是阿克蘇諾貝爾旗下的著名建筑裝飾油漆品牌,產(chǎn)品暢銷于全球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 "立邦始終以開發(fā)綠色產(chǎn)品、注重高科技、高品質(zhì)為目標,以技術(shù)力量不斷推進科研和開發(fā),滿足消費者需求。"; } }
德爾
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德爾集團是全球領(lǐng)先的專業(yè)木地板制造商,北京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 "圣象地板是中國地板行業(yè)著名品牌。圣象地板擁有中國馳名商標、中國名牌、國家免檢、中國環(huán)境標志認證等多項榮譽。"; } }
東鵬
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 "東鵬瓷磚以品質(zhì)鑄就品牌,科技推動品牌,口碑傳播品牌為宗旨,2014年品牌價值132.35億元,位列建陶行業(yè)榜首。"; } }
馬可波羅
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年,作為國內(nèi)最早品牌化的建陶品牌,以“文化陶瓷”占領(lǐng)市場,享有“仿古磚至尊”的美譽。"; } }
以上就是本次裝修公司所提供的裝修配置單
,接下我們會通過案例去使用不同的物料組合出不同的套餐服務。
講道理沒有ifelse解決不了的邏輯,不行就在加一行!
每一個章節(jié)中我們都會使用這樣很直白的方式去把功能實現(xiàn)出來,在通過設計模式去優(yōu)化完善。這樣的代碼結(jié)構(gòu)也都是非常簡單的,沒有復雜的類關(guān)系結(jié)構(gòu),都是直來直去的代碼。除了我們經(jīng)常強調(diào)的這樣的代碼不能很好的擴展外,做一些例子demo工程還是可以的。
itstack-demo-design-3-01
└── src
└── main
└── java
└── org.itstack.demo.design
└── DecorationPackageController.java
一個類幾千行的代碼你是否見過,嚯?那今天就讓你見識一下有這樣潛質(zhì)的類!
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())); } // 現(xiàn)代簡約 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(); } }
首先這段代碼所要解決的問題就是接收入?yún)ⅲ谎b修面積(area)、裝修等級(level),根據(jù)不同類型的裝修等級選擇不同的材料。
其次在實現(xiàn)過程中可以看到每一段if
塊里,都包含著不通的材料(吊頂,二級頂、涂料,立邦、地磚,馬可波羅),最終生成裝修清單和裝修成本。
最后提供獲取裝修詳細信息的方法,返回給調(diào)用方,用于知道裝修清單。
接下來我們通過junit單元測試的方式驗證接口服務,強調(diào)日常編寫好單測可以更好的提高系統(tǒng)的健壯度。
編寫測試類:
@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)); // 現(xiàn)代簡約 System.out.println(decoration.getMatterList(new BigDecimal("85.43"),3)); }
結(jié)果:
-------------------------------------------------------
裝修清單
套餐等級: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
看到輸出的這個結(jié)果,已經(jīng)很有裝修公司提供報價單的感覺了。以上這段使用ifelse
方式實現(xiàn)的代碼,目前已經(jīng)滿足的我們的也許功能。但隨著老板對業(yè)務的快速發(fā)展要求,會提供很多的套餐針對不同的戶型。那么這段實現(xiàn)代碼將迅速擴增到幾千行,甚至在修修改改中,已經(jīng)像膏藥一樣難以維護。
接下來使用建造者模式來進行代碼優(yōu)化,也算是一次很小的重構(gòu)。
建造者模式主要解決的問題是在軟件系統(tǒng)中,有時候面臨著"一個復雜對象"的創(chuàng)建工作,其通常由各個部分的子對象用一定的過程構(gòu)成;由于需求的變化,這個復雜對象的各個部分經(jīng)常面臨著重大的變化,但是將它們組合在一起的過程卻相對穩(wěn)定。
這里我們會把構(gòu)建的過程交給創(chuàng)建者
類,而創(chuàng)建者通過使用我們的構(gòu)建工具包
,去構(gòu)建出不同的裝修套餐
。
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
建造者模型結(jié)構(gòu)
工程中有三個核心類和一個測試類,核心類是建造者模式的具體實現(xiàn)。與ifelse
實現(xiàn)方式相比,多出來了兩個二外的類。具體功能如下;
Builder
,建造者類具體的各種組裝由此類實現(xiàn)。DecorationPackageMenu
,是IMenu
接口的實現(xiàn)類,主要是承載建造過程中的填充器。相當于這是一套承載物料和創(chuàng)建者中間銜接的內(nèi)容。
好,那么接下來會分別講解幾個類的具體實現(xiàn)。
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; // 裝修等級;豪華歐式、輕奢田園、現(xiàn)代簡約 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(); } }
裝修包的實現(xiàn)中每一個方法都會了 this
,也就可以非常方便的用于連續(xù)填充各項物料。
同時在填充時也會根據(jù)物料計算平米數(shù)下的報價,吊頂和涂料按照平米數(shù)適量乘以常熟計算。
最后同樣提供了統(tǒng)一的獲取裝修清單的明細方法。
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, "現(xiàn)代簡約") .appendCeiling(new LevelOneCeiling()) // 吊頂,二級頂 .appendCoat(new LiBangCoat()) // 涂料,立邦 .appendTile(new DongPengTile()); // 地磚,東鵬 } }
建造者的使用中就已經(jīng)非常容易了,統(tǒng)一的建造方式,通過不同物料填充出不同的裝修風格;豪華歐式
、輕奢田園
、現(xiàn)代簡約
,如果將來業(yè)務擴展也可以將這部分內(nèi)容配置到數(shù)據(jù)庫自動生成。但整體的思想還可以使用創(chuàng)建者模式進行搭建。
編寫測試類:
@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()); // 現(xiàn)代簡約 System.out.println(builder.levelThree(85.43D).getDetail()); }
結(jié)果:
-------------------------------------------------------
裝修清單
套餐等級:豪華歐式
套餐價格:198064.39 元
房屋面積:132.52 平米
材料清單:
吊頂:裝修公司自帶、二級頂、平米價格:850 元。
涂料:多樂士(Dulux)、第二代、平米價格:719 元。
地板:圣象、一級、平米價格:318 元。
-------------------------------------------------------
裝修清單
套餐等級:輕奢田園
套餐價格:119865.00 元
房屋面積:98.25 平米
材料清單:
吊頂:裝修公司自帶、二級頂、平米價格:850 元。
涂料:立邦、默認級別、平米價格:650 元。
地磚:馬可波羅(MARCO POLO)、缺省、平米價格:140 元。
-------------------------------------------------------
裝修清單
套餐等級:現(xiàn)代簡約
套餐價格:90897.52 元
房屋面積:85.43 平米
材料清單:
吊頂:裝修公司自帶、一級頂、平米價格:260 元。
涂料:立邦、默認級別、平米價格:650 元。
地磚:東鵬瓷磚、10001、平米價格:102 元。
Process finished with exit code 0
測試結(jié)果是一樣的,調(diào)用方式也基本類似。但是目前的代碼結(jié)構(gòu)卻可以讓你很方便的很有調(diào)理的進行擴展業(yè)務開發(fā)。而不是以往一樣把所有代碼都寫到ifelse
里面。
通過上面對建造者模式的使用,已經(jīng)可以摸索出一點心得。那就是什么時候會選擇這樣的設計模式,當:一些基本物料不會變,而其組合經(jīng)常變化的時候
,就可以選擇這樣的設計模式來構(gòu)建代碼。
此設計模式滿足了單一職責原則以及可復用的技術(shù)、建造者獨立、易擴展、便于控制細節(jié)風險。但同時當出現(xiàn)特別多的物料以及很多的組合后,類的不斷擴展也會造成難以維護的問題。但這種設計結(jié)構(gòu)模型可以把重復的內(nèi)容抽象到數(shù)據(jù)庫中,按照需要配置。這樣就可以減少代碼中大量的重復。
設計模式能帶給你的是一些思想,但在平時的開發(fā)中怎么樣清晰的提煉出符合此思路的建造模塊,是比較難的。需要經(jīng)過一些鍛煉和不斷承接更多的項目,從而獲得這部分經(jīng)驗。有的時候你的代碼寫的好,往往是倒逼的,復雜的業(yè)務頻繁的變化,不斷的挑戰(zhàn)!
以上是“Java實踐之建造者模式的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。