天天看點

設計模式學習(一):多用組合少用繼承(C#)第一次需求變更問題發生了,因為不是所有的鴨子都會飛也許,可以用接口?現在的情況是:找出應用中可能需要變化的地方,把它們獨立起來,不要和那些不需要發生變化的代碼混在一起。針對接口程式設計而不是針對實作程式設計。整合鴨子的行為測試鴨子需求又變了,要求鴨子的行為可以随時改變設計原則:多用組合,少用繼承

《深入淺出設計模式》學習筆記第一章

原始需求和設計

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

設計模式學習(一):多用組合少用繼承(C#)第一次需求變更問題發生了,因為不是所有的鴨子都會飛也許,可以用接口?現在的情況是:找出應用中可能需要變化的地方,把它們獨立起來,不要和那些不需要發生變化的代碼混在一起。針對接口程式設計而不是針對實作程式設計。整合鴨子的行為測試鴨子需求又變了,要求鴨子的行為可以随時改變設計原則:多用組合,少用繼承

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

第一次需求變更

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

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

設計模式學習(一):多用組合少用繼承(C#)第一次需求變更問題發生了,因為不是所有的鴨子都會飛也許,可以用接口?現在的情況是:找出應用中可能需要變化的地方,把它們獨立起來,不要和那些不需要發生變化的代碼混在一起。針對接口程式設計而不是針對實作程式設計。整合鴨子的行為測試鴨子需求又變了,要求鴨子的行為可以随時改變設計原則:多用組合,少用繼承

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

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

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

那麼問題又來了,如果再出現幾個新型鴨子都不會飛,是不是每個都得覆寫一遍fly方法啊????

也許,可以用接口?

把每個方法都做成接口,如圖:

設計模式學習(一):多用組合少用繼承(C#)第一次需求變更問題發生了,因為不是所有的鴨子都會飛也許,可以用接口?現在的情況是:找出應用中可能需要變化的地方,把它們獨立起來,不要和那些不需要發生變化的代碼混在一起。針對接口程式設計而不是針對實作程式設計。整合鴨子的行為測試鴨子需求又變了,要求鴨子的行為可以随時改變設計原則:多用組合,少用繼承

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

現在的情況是:

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

幸好,面向對象軟體開發有這樣一個原則:

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

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

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

根據需求,我們知道鴨子的fly和quack行為經常發生變化,是以我們現在的設計是這樣的:

設計模式學習(一):多用組合少用繼承(C#)第一次需求變更問題發生了,因為不是所有的鴨子都會飛也許,可以用接口?現在的情況是:找出應用中可能需要變化的地方,把它們獨立起來,不要和那些不需要發生變化的代碼混在一起。針對接口程式設計而不是針對實作程式設計。整合鴨子的行為測試鴨子需求又變了,要求鴨子的行為可以随時改變設計原則:多用組合,少用繼承

設計原則:

針對接口程式設計而不是針對實作程式設計。

設計模式學習(一):多用組合少用繼承(C#)第一次需求變更問題發生了,因為不是所有的鴨子都會飛也許,可以用接口?現在的情況是:找出應用中可能需要變化的地方,把它們獨立起來,不要和那些不需要發生變化的代碼混在一起。針對接口程式設計而不是針對實作程式設計。整合鴨子的行為測試鴨子需求又變了,要求鴨子的行為可以随時改變設計原則:多用組合,少用繼承

 這是變化的部分,對于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("---------");
        }
    }
}      

整合鴨子的行為

讓我們來定義鴨子:

設計模式學習(一):多用組合少用繼承(C#)第一次需求變更問題發生了,因為不是所有的鴨子都會飛也許,可以用接口?現在的情況是:找出應用中可能需要變化的地方,把它們獨立起來,不要和那些不需要發生變化的代碼混在一起。針對接口程式設計而不是針對實作程式設計。整合鴨子的行為測試鴨子需求又變了,要求鴨子的行為可以随時改變設計原則:多用組合,少用繼承
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方法即可:

設計模式學習(一):多用組合少用繼承(C#)第一次需求變更問題發生了,因為不是所有的鴨子都會飛也許,可以用接口?現在的情況是:找出應用中可能需要變化的地方,把它們獨立起來,不要和那些不需要發生變化的代碼混在一起。針對接口程式設計而不是針對實作程式設計。整合鴨子的行為測試鴨子需求又變了,要求鴨子的行為可以随時改變設計原則:多用組合,少用繼承

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();
        }
    }
}      

需求完成!!!

最終結構如下:

設計模式學習(一):多用組合少用繼承(C#)第一次需求變更問題發生了,因為不是所有的鴨子都會飛也許,可以用接口?現在的情況是:找出應用中可能需要變化的地方,把它們獨立起來,不要和那些不需要發生變化的代碼混在一起。針對接口程式設計而不是針對實作程式設計。整合鴨子的行為測試鴨子需求又變了,要求鴨子的行為可以随時改變設計原則:多用組合,少用繼承

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

下面是我的關于ASP.NET Core Web API相關技術的公衆号--草根專欄:

設計模式學習(一):多用組合少用繼承(C#)第一次需求變更問題發生了,因為不是所有的鴨子都會飛也許,可以用接口?現在的情況是:找出應用中可能需要變化的地方,把它們獨立起來,不要和那些不需要發生變化的代碼混在一起。針對接口程式設計而不是針對實作程式設計。整合鴨子的行為測試鴨子需求又變了,要求鴨子的行為可以随時改變設計原則:多用組合,少用繼承

繼續閱讀