您好,登錄后才能下訂單哦!
這篇文章主要講解了“小白如何使用設(shè)計模式”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“小白如何使用設(shè)計模式”吧!
工廠模式(Factory Pattern)是最常用的設(shè)計模式之一。這種類型的設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。
在工廠模式中,我們在創(chuàng)建對象時不會對客戶端暴露創(chuàng)建邏輯,而是通過使用一個共同的接口來指向新創(chuàng)建的對象。
工廠模式可以分為三種,其中簡單工廠一般不被認(rèn)為是一種設(shè)計模式,可以將其看成是工廠方法的一種特殊。
簡單工廠
工廠方法
抽象工廠
平凡枯燥的文字總是讓人看得想睡覺,接下來我們用幾個情景案例來進(jìn)行分析
直接通過一個Factory【工廠類】類創(chuàng)建多個實(shí)體類的構(gòu)造方式。
時間:2021年2月19日 地點(diǎn):教室 人物:學(xué)生小白、老師、大佬黑皮
小白是一名大二的計算機(jī)系學(xué)生,懶惰不愛學(xué)習(xí)。今天早晨第一節(jié)課就因?yàn)樗瘧杏X遲到被老師逮個正著,這節(jié)課還正好是小白最頭疼的上機(jī)課"C#設(shè)計模式”。這不,課堂上到一半老師就開始提問,小白“光榮”的成為了老師的點(diǎn)名對象。
老師笑著說道:“小白,請你解答一下屏幕上的問題。”
題目:請使用c#、java、python、php或其他任一面向?qū)ο缶幊陶Z言實(shí)現(xiàn)輸入倆個合法數(shù)字和一個合法符號,輸出對應(yīng)結(jié)果的功能。
小白一看,這算什么題目,這么簡單,看我不手到擒來,伴隨著雙手噼里啪啦一頓敲擊聲音,屏幕上出現(xiàn)一串編碼。
**Calculator操作類 **
public class Calculator { public double GetResult(double A, double B, string operate) { double result = 0d; switch (operate) { case "+": result = A + B; break; case "-": result = A - B; break; case "*": result = A * B; break; case "/": result = A / B; break; default: break; } return result; } }
客戶端代碼
class Program { static void Main(string[] args) { Console.WriteLine("請輸入數(shù)字A"); string a = Console.ReadLine(); Console.WriteLine("請輸入數(shù)字B"); string b = Console.ReadLine(); Console.WriteLine("請選擇操作符號(+、-、*、/)"); string operate = Console.ReadLine(); Calculator box = new Calculator(); double result = box.GetResult(Convert.ToDouble(a), Convert.ToDouble(b), operate); Console.WriteLine(result); Console.ReadKey(); } }
小白:”老師,我寫好了“
老師:"不錯,寫的很好。使用到了面向?qū)ο笕筇匦灾械姆庋b,將計算方法封裝成了一個計算類,多個客戶端可以復(fù)用這個類。但是這其中也有不少的問題,哪位同學(xué)來回答一下。"
黑皮:”小白同學(xué)寫的代碼有倆處問題。
1、如果輸入A=10,B=0,程序就會報錯,沒有做輸入的有效性驗(yàn)證
2、如果操作符號不按照規(guī)定的輸入 ,也會導(dǎo)致報錯“
老師:”黑皮同學(xué)回答的非常好,但是這都只是針對代碼業(yè)務(wù)邏輯錯誤的描述。有沒有誰可以看出更加深層次的內(nèi)容。“
黑皮:”老師,你給點(diǎn)提示吧?!?/p>
老師:”這個可以會有點(diǎn)隱蔽,老師就給出一點(diǎn)提示。如果我們增加一個新的運(yùn)算怎么辦?“
小白立即回答到:”在switch中增加一個新的分支就可以了“。
老師:”這樣當(dāng)然是沒有錯誤的,但是問題在于,我只是增加了一個新的運(yùn)算符號,卻需要讓加減乘除所有的運(yùn)算都參加編譯,如果在修改的過程中不小心修改了其他的代碼,例如把+號改成了-號,那不是很糟糕,這就違背了開閉原則【對擴(kuò)展開放,對修改關(guān)閉】“
小白撓一撓頭問道:”開閉原則,這是什么?“
老師:”這就是你不認(rèn)真聽課落下的內(nèi)容,回去好好補(bǔ)習(xí)。黑皮同學(xué),不知道你Get到了老師的點(diǎn)沒有?“
黑皮:”我知道應(yīng)該如何修改了。小白實(shí)現(xiàn)了面向?qū)ο笕筇匦灾坏姆庋b,其實(shí)就是將其他倆個特性一起使用就可以完成老師要的功能“
小白:”多態(tài)和繼承?“
黑皮:”是的,等我改完你再看程序就應(yīng)該有感覺了“
public class Operate { public double NumberA { get; set; } public double NumberB { get; set; } public virtual double GetResult() { return 0; } } public class OperateAdd : Operate { public override double GetResult() { return this.NumberA +this.NumberB; } } public class OperateSub : Operate { public override double GetResult() { return this.NumberA - this.NumberB; } }
簡單工廠
public class OperateFactory { public static Operate GetOperateFactory(string operate) { Operate fac = null; switch (operate) { case "+": fac = new OperateAdd(); break; case "-": fac = new OperateSub(); break; case "*": fac = new OperateMul(); break; case "/": fac = new OperateDiv(); break; default: break; } return fac; } }
黑皮:”首先是一個運(yùn)算類,里面有倆個Number屬性和一個虛方法GetResult()。加減乘除四個方法作為運(yùn)算類的子類繼承,繼承后重寫GetResult()方法,調(diào)用基類的A和B公有屬性進(jìn)行不同的數(shù)學(xué)運(yùn)算?!?/p>
黑皮:”然后定義一個簡單工廠,靜態(tài)方法傳入操作符參數(shù)得到實(shí)際的業(yè)務(wù)處理類,客戶端得到處理類后對參數(shù)賦值。最后一步你應(yīng)該知道怎么做了吧“
小白:”我懂了,那客戶端這么調(diào)用就可以了“。
static void Main(string[] args) { Console.WriteLine("請選擇操作符號(+、-、*、/)"); string operateStr = Console.ReadLine(); Operate operate = OperateFactory.GetOperateFactory(operateStr); operate.NumberA = 10; operate.NumberB = 4; double result = operate.GetResult(); Console.WriteLine(result); Console.ReadKey(); }
老師:”看來倆位同學(xué)都已經(jīng)掌握了簡單工廠的使用,接下來我提問幾個問題,便于大家更快的掌握這種設(shè)計模式“
老師:”如果我們要修改除方法的邏輯,增加被除數(shù)為0的邏輯應(yīng)該怎么做“
小白:”直接修改OperateDiv類,這不會對其他造成影響“
老師:”如果我們要新增一個開根運(yùn)算應(yīng)該怎么做“
小白:”添加一個新的類,Operate開根類,里面描述開根的邏輯。在工廠方法中將新的操作符添加進(jìn)去即可。新增的操作單獨(dú)一個類,也不會對其他方法體造成影響“。
小白:”那客戶端需要做什么改變嗎?“
老師:”客戶端要做什么改變,客戶端只要處理好自己的事情就可以了!“
是的,客戶端不關(guān)心工廠創(chuàng)建了什么,工廠是一個黑盒子??蛻舳酥灰獋魅?yún)?shù),工廠負(fù)責(zé)將內(nèi)容生產(chǎn)后【實(shí)例化類的過程】給客戶端即可。
簡單工廠模式的工廠類一般是使用靜態(tài)方法,通過接收的參數(shù)不同來返回不同的對象實(shí)例。不修改代碼的話,是無法擴(kuò)展的 優(yōu)點(diǎn):客戶端可以免除直接創(chuàng)建產(chǎn)品對象的責(zé)任,而僅僅是“消費(fèi)”產(chǎn)品。簡單工廠模式通過這種做法實(shí)現(xiàn)了對責(zé)任的分割 缺點(diǎn):由于工廠類集中了所有實(shí)例的創(chuàng)建邏輯,違反了高內(nèi)聚責(zé)任分配原則,將全部創(chuàng)建邏輯集中到了一個工廠類中;它所能創(chuàng)建的類只能是事先考慮到的,如果需要添加新的類,則就需要改變工廠類了
時間:2021年2月19日下午 地點(diǎn):教室 人物:學(xué)生小白、老師、大佬黑皮
老師:”我們緊接著上午的設(shè)計模式繼續(xù),上午我們講的是簡單工廠,下午我們講下一個內(nèi)容工廠方法。工廠方法和簡單工廠其實(shí)大同小異,唯一的區(qū)別就在于每一個實(shí)現(xiàn)抽象類的實(shí)例(也叫做產(chǎn)品,即上午定義的加減乘除四個子類)都有一個對應(yīng)的工廠去創(chuàng)建。同學(xué)們了解一下老師說話的內(nèi)容,然后尋找一個場景編碼實(shí)現(xiàn)一下。最快完成的有課堂獎勵”
....幾分鐘過去了.....
小白:“老師,我完成了?!?/p>
老師:“好的,那我們請小白同學(xué)說明一下場景和實(shí)現(xiàn)的方式?!?/p>
我設(shè)計的是以水果作為場景的模式。
1、定義一個抽象類Fruit.cs,這個類定義倆個抽象方法printfColor()和printfName()。
2、實(shí)現(xiàn)倆種不同的水果分別繼承此抽象類并復(fù)寫抽象方法。
3、定義一個工廠接口,定義接口方法createFruit()
4、實(shí)現(xiàn)倆個不同的工廠分別實(shí)現(xiàn)水果實(shí)例的創(chuàng)建。
水果抽象類
public abstract class Fruit { public abstract void PrintfColor(); public abstract void PrintfName(); } public class Apple : Fruit { public override void PrintfColor() { Console.WriteLine("紅色"); } public override void PrintfName() { Console.WriteLine("蘋果"); } }
工廠接口
public interface IFruitFactory { Fruit CreateFruit(); } public class AppleFactory : IFruitFactory { public Fruit CreateFruit() { return new Apple(); } }
客戶端實(shí)現(xiàn)
//蘋果工廠 IFruitFactory appleFac = new AppleFactory(); Fruit apple = appleFac.CreateFruit(); apple.PrintfColor(); apple.PrintfName //橘子工廠 IFruitFactory orangeFac = new OrangeFactory(); Fruit orage = orangeFac.CreateFruit(); orage.PrintfColor(); orage.PrintfName();
老師:“看來小白同學(xué)已經(jīng)對上午的內(nèi)容有了一個充分的了解,果然好好上課才能夠?qū)W習(xí)到新的知識。逃課是沒有益處的”
老師:“只是這樣的案例太過簡單,可能其他同學(xué)不是很能理解為什么要這樣 ,我來舉一個實(shí)際場景的案例方便大家理解。在實(shí)際的工作過程中我們總會用到日志組件,例如Nlog,Log4net這種第三方組件,這種組件都支持可配置化的多源輸出。當(dāng)我們在配置文件(json/xml)中增加一個“輸出到控制臺的參數(shù)”,程序 就會將內(nèi)容輸出到控制臺,當(dāng)配置一個輸入到文件的參數(shù),程序就會將內(nèi)容輸出到指定的文件。這種場景的實(shí)現(xiàn)其實(shí)就是一種典型的工廠方法。下面我來分析一下過程”
1、讀取配置文件(json/xml)
2、獲取所有的配置方式,循環(huán)遍歷
3、判斷配置類型,如果是輸入到文件的配置。new一個文件日志工廠,將配置信息作為參數(shù)傳遞,便于后期方法調(diào)用;如果是輸入到控制臺的配置。new一個日志工廠也是做同樣的操作
4、每一個工廠只管理自己的事情,但是應(yīng)該都擁有日志輸出這個接口。
5、當(dāng)上層調(diào)用打印方法時候,循環(huán)遍歷所有的工廠,調(diào)用接口的日志輸出輸出方法
工廠方法是針對每一種產(chǎn)品提供一個工廠類。通過不同的工廠實(shí)例來創(chuàng)建不同的產(chǎn)品實(shí)例。在同一等級結(jié)構(gòu)中,支持增加任意產(chǎn)品 優(yōu)點(diǎn):允許系統(tǒng)在不修改具體工廠角色的情況下引進(jìn)新產(chǎn)品 缺點(diǎn):由于每加一個產(chǎn)品,就需要加一個產(chǎn)品工廠的類,增加了額外的開發(fā)量
抽象工廠模式為創(chuàng)建一組對象提供了一種解決方案。與工廠方法模式相比,抽象工廠模式中的具體工廠不只是創(chuàng)建一種產(chǎn)品,它負(fù)責(zé)創(chuàng)建一族產(chǎn)品。
時間:2021年2月20日上午 地點(diǎn):教室 人物:學(xué)生小白、老師、黑皮
新的一天又開始了,“設(shè)計模式”課程在小白的眼中好像沒有那么復(fù)雜了,今天小白早早地就到了教室,準(zhǔn)備迎接老師新的鞭策。
老師:”同學(xué)們早上好,今天我們繼續(xù)昨日的課程。昨天講的是工廠方法,今天我們在此基礎(chǔ)上做一點(diǎn)改進(jìn),看看又有什么新的變化。小白同學(xué)學(xué)習(xí)熱情很高嘛,現(xiàn)在都知道坐在第一排了。不錯不錯,值得鼓勵”
小白:”嘻嘻“
老師:“好,那開始今天的課程。今天要講的模式是抽象工廠模式。通過和工廠模式做比較,同學(xué)們可以比較清晰的發(fā)現(xiàn)這倆都之間的區(qū)別。我們用昨天小白同學(xué)的例子繼續(xù)開拓。”
此時有蘋果和橘子倆個產(chǎn)品,分別對應(yīng)蘋果工廠和橘子工廠。這是工廠方法的體現(xiàn)。可是如果有3個不同的工廠,他們分別都生產(chǎn)蘋果和橘子呢。
小白:“恩...那就多建立幾個工廠。每個產(chǎn)品分別對應(yīng)不同的工廠,應(yīng)該是這樣的一個結(jié)構(gòu),每一個工廠分別對應(yīng)生產(chǎn)產(chǎn)品的類”
A
A_蘋果工廠.cs
A_橘子工廠.cs
B
B_蘋果工廠.cs
B_橘子工廠.cs
C
C_蘋果工廠.cs
C_橘子工廠.cs
老師:“這樣的方式當(dāng)然是可以的,可以如果我有10個工廠呢,難道我們要建立10*2=20個類嗎,這樣程序的復(fù)雜度就是直線上升,不利于維護(hù)?!?/p>
小白:“那怎么辦呢,用老師你說的那種抽象工廠嗎?如果用,又應(yīng)該怎么做呢”
老師:“是的,在這樣的場景下,抽象工廠是最能匹配的設(shè)計模式。其實(shí)做法非常簡單,對昨天的代碼進(jìn)行一些修改即可”
水果抽象類
新增一個Name屬性,方便后期打印不同的工廠。
public abstract class Fruit { public string Name { get; set; } public abstract void PrintfColor(); public abstract void PrintfName(); } public class Apple : Fruit { public Apple(string name) { this.Name = name; } public override void PrintfColor() { Console.WriteLine(this.Name + "紅色"); } public override void PrintfName() { Console.WriteLine(this.Name + "蘋果"); } }
工廠接口
老師:“這一處的改動就比較明顯。原來的接口中方法輸出唯一的產(chǎn)品——因?yàn)橹懊恳粋€工廠只生產(chǎn)一件產(chǎn)品。現(xiàn)在輸出倆個產(chǎn)品,即繼承工廠接口的類必須實(shí)現(xiàn)生產(chǎn)蘋果和橘子的方法。這樣的好處在于,每一個工廠負(fù)責(zé)管理自己產(chǎn)品的實(shí)現(xiàn),避免了每一個產(chǎn)品都需要創(chuàng)建一個工廠的操作。“
解釋:
工廠生產(chǎn)蘋果和橘子。當(dāng)有多個工廠的時候,每一個工廠都實(shí)現(xiàn)生產(chǎn)蘋果和橘子。而不是生產(chǎn)A廠蘋果需要一個工廠實(shí)現(xiàn)類,生產(chǎn)B廠蘋果又需要一個。如下所示
舊模式
A
A_蘋果工廠.
A_橘子工廠
B
B_蘋果工廠
B_橘子工廠
C
C_蘋果工廠
新模式
A 工廠
蘋果/橘子
B 工廠
蘋果/橘子
C 工廠
蘋果/橘子
老師:“這樣復(fù)雜度由原來的6變成了3。”
小白:"我明白了,又學(xué)習(xí)到了新的東西。"
public interface IFruitFactory { Fruit CreateApple(string name); Fruit CreateOrange(string name); } public class AFactory : IFruitFactory { public Fruit CreateApple(string name) { return new Apple(name); } public Fruit CreateOrange(string name) { return new Orange(name); } }
客戶端實(shí)現(xiàn)
IFruitFactory fac = new AFactory(); Fruit a_Apple = fac.CreateApple("a工廠"); Fruit a_Orange = fac.CreateOrange("a工廠"); a_Apple.PrintfName(); a_Orange.PrintfName(); IFruitFactory b_fac = new BFactory(); Fruit b_Apple = b_fac.CreateApple("b工廠"); Fruit b_Orange = b_fac.CreateOrange("b工廠"); b_Apple.PrintfName(); b_Orange.PrintfName();
小白:“可是在什么樣的場景下用這種模式呢,我好像一下子想不到”
老師:“抽象工廠的使用相對來說比較少,但也不是沒有。我舉一個例子,在后端開始中我們有各種的組件【按鈕,抽屜,導(dǎo)航欄】等等,這些組件有對應(yīng)的皮膚,對皮膚的開發(fā)就是抽象工廠的實(shí)現(xiàn)。工廠接口是對每個組件的定義,每個皮膚就是一個工廠的實(shí)現(xiàn)。如果要切換皮膚,只需要實(shí)例化不同的工廠即可?!?/p>
小白:“哦。就和游戲中的皮膚切換一樣嗎?”
老師:“你也可以這樣理解,設(shè)計模式只是一種通用解決方案,可以應(yīng)用在不同的場景下,大家可以挑最適應(yīng)自己,最好理解的場景下手?!?/p>
下課鈴聲又響起了
老師:“好了,這節(jié)課就到這里。下節(jié)課我們講其他的設(shè)計模式,希望大家準(zhǔn)時聽講?!?/p>
抽象工廠是應(yīng)對產(chǎn)品族概念的。應(yīng)對產(chǎn)品族概念而生,增加新的產(chǎn)品線很容易,但是無法增加新的產(chǎn)品。比如,每個汽車公司可能要同時生產(chǎn)轎車、貨車、客車,那么每一個工廠都要有創(chuàng)建轎車、貨車和客車的方法 優(yōu)點(diǎn):向客戶端提供一個接口,使得客戶端在不必指定產(chǎn)品具體類型的情況下,創(chuàng)建多個產(chǎn)品族中的產(chǎn)品對象 缺點(diǎn):增加新的產(chǎn)品等級結(jié)構(gòu)很復(fù)雜,需要修改抽象工廠和所有的具體工廠類,對“開閉原則”的支持呈現(xiàn)傾斜性
感謝各位的閱讀,以上就是“小白如何使用設(shè)計模式”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對小白如何使用設(shè)計模式這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。