您的位置:首頁>設計>正文

設計模式學習筆記 之“多用組合,少用繼承” C 代碼

原始需求和設計

事情是這樣開始的, 公司需要做一套程式, 鴨子, 設計如下:

一個鴨子父類, 多個派生類, 三個可override的方法。

第一次需求變更

我們要會飛的鴨子!!!!!

所以我們做了如下的更改:

父類加了fly方法, 嗯, 所有的鴨子都會飛了, 需求實現!

問題發生了, 因為不是所有的鴨子都會飛

我們可以在派生類中把父類的fly方法中的內容覆蓋掉, 那麼這個鴨子就不會飛了!

那麼問題又來了,

如果再出現幾個新型鴨子都不會飛, 是不是每個都得覆蓋一遍fly方法啊????

也許, 可以用介面?

把每個方法都做成介面, 如圖:

這是超笨的方法, 如果一些鴨子的飛行方式發生變化, 那麼得改多少個類啊。 。 。

現在的情況是:

繼承不行, 因為鴨子的行為(需求)在子類裡面不斷變化, 而使用介面又無法進行複用。

幸好, 物件導向軟體發展有這樣一個原則:

找出應用中可能需要變化的地方, 把它們獨立起來, 不要和那些不需要發生變化的代碼混在一起。

這句話另一種思考方式就是:把變化的部分取出並封裝起來, 以便以後可以輕鬆的改動或擴展, 而不影響其他部分。

所以我們應該把鴨子的行為都提取出來。

根據需求, 我們知道鴨子的fly和quack行為經常發生變化,

所以我們現在的設計是這樣的:

設計原則:

針對介面程式設計而不是針對實現程式設計。

這是變化的部分, 對於Fly和Quack分別定義介面。

namespace DesignPatterns.Intro.Bases{ public interface IFlyBehavior { void Fly(); }}namespace DesignPatterns.Intro.Bases{ public interface IQuackBehavior { void Quack(); }}

然後實現幾種類型的Fly和Quack:

namespace DesignPatterns.Intro.Derives{ public class Squeak: IQuackBehavior { public void Quack() { Console.WriteLine("吱吱"); } }}namespace DesignPatterns.Intro.Derives{ public class NormalQuack: IQuackBehavior { public void Quack() { Console.WriteLine("呱呱"); } }}namespace DesignPatterns.Intro.Derives{ public class MuteQuack: IQuackBehavior { public void Quack() { Console.WriteLine("---------"); } }}整合鴨子的行為

讓我們來定義鴨子:

namespace ConsoleApp2.Bases{ public abstract class Duck { private readonly IFlyBehavior _flyBehavior; private readonly IQuackBehavior _quackBehavior; protected Duck(IFlyBehavior flyBehavior = null, IQuackBehavior quackBehavior = null) { _flyBehavior = flyBehavior ?? new FlyNoWay(); _quackBehavior = quackBehavior ?? new MuteQuack(); } public abstract void Display(); public void PerformFly() { _flyBehavior.Fly(); } public void PerformQuack() { _quackBehavior.Quack(); } public void Swim() { Console.WriteLine("所有的鴨子都會游泳"); } }}

這是鴨子的抽象類別。

建立實際的鴨子:

namespace ConsoleApp2.Derives{ public class MallardDuck: Duck { public MallardDuck(IFlyBehavior flyBehavior = null, IQuackBehavior quackBehavior = null) : base(flyBehavior, quackBehavior) { } public override void Display() { Console.WriteLine("我是個野鴨..."); } }}測試鴨子namespace ConsoleApp2{ class Program { static void Main(string[] args) { var duck = new MallardDuck(new FlyNoWay(), new NormalQuack()); duck.PerformFly(); duck.PerformQuack(); duck.Display(); Console.ReadLine(); } }}

這時, 需求終於完成了!

我們的鴨子根據傳入的Fly和Quack實現類不同而具有不同的效果!

需求又變了, 要求鴨子的行為可以隨時改變

這時, 我們需要動態設定行為, 我們只需要加入Set方法即可:

Duck最新的代碼是:

namespace ConsoleApp2.Bases{ public abstract class Duck { public IFlyBehavior FlyBehavior { private get; set; } public IQuackBehavior QuackBehavior { private get; set; } protected Duck(IFlyBehavior flyBehavior = null, IQuackBehavior quackBehavior = null) { FlyBehavior = flyBehavior ?? new FlyNoWay(); QuackBehavior = quackBehavior ?? new MuteQuack(); } public abstract void Display(); public void PerformFly() { FlyBehavior.Fly(); } public void PerformQuack() { QuackBehavior.Quack(); } public void Swim() { Console.WriteLine("所有的鴨子都會游泳"); } }}

測試效果:

namespace ConsoleApp2{ class Program { static void Main(string[] args) { var duck = new MallardDuck(); duck.PerformFly(); duck.PerformQuack(); duck.Display(); duck.FlyBehavior = new FlyWithWings(); duck.QuackBehavior = new Squeak(); duck.PerformFly(); duck.PerformQuack(); Console.ReadLine(); } }}

需求完成!!!

最終結構如下:

設計原則:多用組合,少用繼承

設計原則:多用組合,少用繼承
Next Article
喜欢就按个赞吧!!!
点击关闭提示