設計模式學習筆記 之「多用組合,少用繼承」 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();
}
}
}
需求完成!!!
最終結構如下:
設計原則:多用組合,少用繼承


※大話數據結構——使用棧實現簡單的四則運算
※Object和異常類
※移動端主流適配方案
※Swift教程之基本操作符
※「js高手之路」html5 canvas動畫教程-實時獲取滑鼠的當前坐標
TAG:IT優就業 |