大話設計模式讀書筆記

駑馬農夫發表於2017-04-23
主題 概要
設計模式 大話設計模式讀書筆記
編輯 時間
新建 20170423
序號 參考資料
1 大話設計模式

重新看了一遍設計模式,除了一些已經特別熟悉的模式,都自己敲了一遍程式碼,有些豁然開朗的感覺。

策略模式(Strategy)

型別:行為型
使用場景:當一個類的行為或其演算法可以在執行時更改。定義一系列的演算法,把它們一個個封裝起來,並且使它們可以相互替換。可以消除if…else…所帶來的複雜和難以維護。
這裡寫圖片描述

這個圖還是比較簡單的,轉換成程式碼:

抽象策略類:

namespace DesignModeDemo.Stragegy
{
   abstract class Strategy
    {
        public abstract void algorithemInterface();
    }
}

具體策略類:

namespace DesignModeDemo.Stragegy
{
    class ConcreteStartegyA:Strategy
    {
        public override void algorithemInterface()
        {
            Console.WriteLine("Strategy A");
        }
    }
}
namespace DesignModeDemo.Stragegy
{
    class ConcreteStartegyB : Strategy
    {
        public override void algorithemInterface()
        {
            Console.WriteLine("Strategy B");
        }
    }
}
namespace DesignModeDemo.Stragegy
{
    class ConcreteStrategyC:Strategy
    {
        public override void algorithemInterface()
        {
            Console.WriteLine("Strategy C");
        }
    }
}

上下文:

namespace DesignModeDemo.Stragegy
{
    class Context
    {
        private Strategy strategy;

        public Context(Strategy strategy)
        {
            this.strategy = strategy;
        }

        public void contextInterface()
        {
            strategy.algorithemInterface();
        }
    }
}

客戶端程式碼:

 static void Main(string[] args)
        {
            Context ctA = new Context(new ConcreteStartegyA());
            ctA.contextInterface();
            Context ctB = new Context(new ConcreteStartegyB());
            ctB.contextInterface();
            Context ctC = new Context(new ConcreteStrategyC());
            ctC.contextInterface();

            Console.Read();
        }

Story:
小菜實現一個商場收銀系統,有很多不同的打折策略,並結合工廠方法:
這裡寫圖片描述

幾種策略:

namespace DesignModeDemo.Stragegy
{
    abstract class CashSuper
    {
        public abstract void acceptCash();
    }
}
namespace DesignModeDemo.Stragegy
{
    class CashNormal:CashSuper
    {
        public override void acceptCash()
        {
            Console.WriteLine("CashNormal");
        }
    }
}
namespace DesignModeDemo.Stragegy
{
    class CashRebate:CashSuper
    {
        public override void acceptCash()
        {
            Console.WriteLine("CashRebate");
        }
    }
}

上下文:

namespace DesignModeDemo.Stragegy
{
    class CashContext
    {
        private CashSuper cashStrategy;

        public CashContext(CashSuper cashStrategy)
        {
            this.cashStrategy = cashStrategy;
        }

        public CashContext(string cashStrategy)  //--工廠模式,隱藏具體策略細節,更好
        {
            switch (cashStrategy)
            {
                case "normal":
                    this.cashStrategy = new CashNormal();
                    break;
                case "rebate":
                    this.cashStrategy = new CashRebate();
                    break;
                default:
                    break;
            }
        }


        public void getResult()
        {
            cashStrategy.acceptCash();
        }
    }
}

客戶端程式碼:

static void Main(string[] args)
        {
            CashContext ccA = new CashContext("normal");
            ccA.getResult();
            CashContext ccB = new CashContext("rebate");
            ccB.getResult();

            Console.Read();
        }

裝飾器模式(Decorator Pattern)

型別:結構型
使用場景:當需要向一個現有物件新增新的功能時,如果使用繼承方式,隨著功能的擴充套件,子類會很膨脹。而使用裝飾器給一個物件新增一些額外的功能,比生成子類更為靈活。
這裡寫圖片描述
這個圖剛開始,不易理解的點兒是Decorator繼承了Component,同時Decorator又維護了一個對Component的引用。
轉換成程式碼:

namespace DesignModeDemo.Decorator
{
    abstract class Component
    {
        public abstract void operation();
    }
}
namespace DesignModeDemo.Decorator
{
    class ConcreteComponent:Component
    {
        public override void operation()
        {
            Console.WriteLine("具體物件的操作");
        }
    }
}

裝飾者,繼承自一個元件,並維護一個元件的引用:

namespace DesignModeDemo.Decorator
{
    abstract class Decorator:Component
    {
        private Component component;

        public void setComponent(Component component)
        {
            this.component = component;
        }

        public override void operation()
        {
            if (component!=null)
            {
                component.operation();
            }
        }
    }
}

給元件新增特有的裝飾行為:

namespace DesignModeDemo.Decorator
{
    class ConcerateDecoratorA:Decorator
    {
        private void addBeave() { Console.WriteLine("ConcerateDecoratorA"); }//--A類裝飾器特有

        public override void operation()
        {
            base.operation();
            addBeave();
        }
    }
}
namespace DesignModeDemo.Decorator
{
    class ConcerateDecoratorB:Decorator
    {
        private void addState() { Console.WriteLine("ConcerateDecoratorB"); }//--B類裝飾器特有

        public override void operation()
        {
            base.operation();
            addState();
        }
    }
}

客戶端程式碼:

 static void Main(string[] args)
        {
            ConcreteComponent comp = new ConcreteComponent();
            ConcerateDecoratorA deA = new ConcerateDecoratorA();
            ConcerateDecoratorB deB = new ConcerateDecoratorB();

            deA.setComponent(comp);
            deB.setComponent(deA);
            deB.operation(); //--deB包裝了 addState和addBeave功能

            Console.Read();
        }

Story:
小菜實現一個可以給人搭配不同的服務的系統。

第一版本結構圖:
這裡寫圖片描述
所有功能放在一起。
第二版本結構圖:
這裡寫圖片描述
人與服飾分離,並且把服飾抽象出來。
第三版本結構圖:
這裡寫圖片描述
人與服飾分離,並且把服飾抽象出來。
具體實現:
人為具體元件,但這裡直接是具體類,沒有用介面:

namespace DesignModeDemo.Decorator
{
    class Person
    {
        private string name;
        public Person() { }

        public Person(string name)
        {
            this.name = name;
        }

        public virtual void show()
        {
            Console.WriteLine("裝扮的{0}",name);
        }
    }
}

服飾為裝飾類的基類:

namespace DesignModeDemo.Decorator
{
    class Finery:Person
    {
        private Person person;

        public void decorator(Person person)
        {
            this.person = person;
        }

        public override void show()
        {
            if(person!=null)
            {
                person.show();
            }
        }
    }
}

兩個具體的裝飾類,包裝了自己的功能:

namespace DesignModeDemo.Decorator
{
    class TShirts:Finery
    {
        public override void show()
        {
            Console.WriteLine("Add TShirts decoreator");
            base.show();
        }
    }
}
namespace DesignModeDemo.Decorator
{
    class BigTrouser:Finery
    {
        public override void show()
        {
            Console.WriteLine("Add BigTrouser decoreator");
            base.show();
        }
    }
}

客戶端呼叫:

static void Main(string[] args)
        {
            Person person = new Person("小菜");

            TShirts ts = new TShirts();
            BigTrouser bt = new BigTrouser();

            ts.decorator(person);
            bt.decorator(ts);
            bt.show();            

            Console.Read();
        }

執行結果:
這裡寫圖片描述
在原有功能上,增加了兩個裝飾類自己包裝的功能。

代理模式

型別:結構型
使用場景:為其它物件提供一種代理以控制對這個物件的訪問。主要解決在直接訪問物件時帶來的問題。比如智慧指標、遠端代理等場景。
這裡寫圖片描述

注意這裡,Proxy與RealSubject共用相同的介面。
抽像Subject

namespace DesignModeDemo.proxy
{
    abstract class Subject
    {
        public abstract void request();
    }
}

具體Subject:

namespace DesignModeDemo.proxy
{
    class RealSubject:Subject
    {
        public override void request()
        {
            Console.WriteLine("RealSubject");
        }
    }
}

代理類:

namespace DesignModeDemo.proxy
{
    class Proxy:Subject
    {
        private Subject subject;

        public override void request()
        {
            if(null==subject)
            {
                subject = new RealSubject();
            }

            subject.request();
        }
    }
}

客戶端:

  static void Main(string[] args)
        {
            Proxy proxy = new Proxy();
            proxy.request();

            Console.Read();
        }

Story:
追女孩兒的故事,追求者通過代理人來追女孩,追求者應該和女孩不相識,所有活動都通過代理來進行。
這裡寫圖片描述
女孩:

namespace DesignModeDemo.proxy
{
    class SchoolGirl
    {
        private string name;
        public SchoolGirl(string name)
        {
            this.name = name;
        }

        public string Name { get { return name; } }
    }
}

送禮物介面:

namespace DesignModeDemo.proxy
{
    interface IGiveGift
    {
        void giveDolls();
        void giveFlowers();
        void giveChocolate();
    }
}

追求者:

namespace DesignModeDemo.proxy
{
    class Pursuit:IGiveGift
    {
        private SchoolGirl mm;

        public Pursuit(SchoolGirl mm)
        {
            this.mm = mm;
        }

        public void giveDolls()
        {
            Console.WriteLine(mm.Name+" give your dolls");
        }

        public void giveFlowers()
        {
            Console.WriteLine(mm.Name + " give your flowrs");
        }

        public void giveChocolate()
        {
            Console.WriteLine(mm.Name + " give your chocolate");
        }

    }
}

追求者代理:

namespace DesignModeDemo.proxy
{
    class PursuitProxy:IGiveGift
    {
        Pursuit pursuit;

        public PursuitProxy(SchoolGirl mm)
        {
            pursuit = new Pursuit(mm);
        }

        public void giveDolls()
        {
            pursuit.giveDolls();
        }

        public void giveFlowers()
        {
            pursuit.giveFlowers() ;
        }

        public void giveChocolate()
        {
           pursuit.giveChocolate();
        }
    }
}

原型模式

型別:建立型
使用場景:直接通過拷貝原型,建立重複的物件。實現克隆操作,在 JAVA 繼承 Cloneable,重寫 clone(),在 .NET 中可以使用 Object 類的 MemberwiseClone() 方法來實現物件的淺拷貝或通過序列化的方式來實現深拷貝。

這裡寫圖片描述

轉換成程式碼:
抽象原型:

namespace DesignModeDemo.prototype
{
    abstract class Prototype
    {
        private string id;
        public Prototype(string id)
        {
            this.id = id;
        }

        public string ID
        {
            get { return id; }
        }

        public abstract Prototype clone();
    }
}

具體原型:

namespace DesignModeDemo.prototype
{
    class ConcretePrototype1:Prototype
    {
        public ConcretePrototype1(string id):base(id)
        {

        }

        public override Prototype clone()
        {
            return this.MemberwiseClone() as Prototype;
        }
    }
}

客戶端程式碼:

 static void Main(string[] args)
        {
            ConcretePrototype1 cc = new ConcretePrototype1("cc");
            ConcretePrototype1 dd = cc.clone() as ConcretePrototype1;
            Console.WriteLine("Cloned:" + dd.ID);

            Console.Read();
        }

Story:
招聘網站上,每個人可以建立多份簡歷,可以使用原型模式,直接clone一份簡歷,只需修改想要修改的資訊。clone時要注意淺複製與深層複製的區別。

工作經歷類:

namespace DesignModeDemo.prototype
{
    class WorkExperience:ICloneable
    {
        private string workDate;
        private string workCompany;

        public string WorkDate
        {
            get
            {
                return workDate;
            }

            set
            {
                workDate = value;
            }
        }

        public string WorkCompany
        {
            get
            {
                return workCompany;
            }

            set
            {
                workCompany = value;
            }
        }

        public object Clone()
        {
            return this.MemberwiseClone() as object;
        }
    }
}

簡歷類:

namespace DesignModeDemo.prototype
{
    class Resume:ICloneable
    {
        private string name;
        private string age;
        private string sex;

        private WorkExperience workExperience;

        public Resume(string name)
        {
            this.name = name;
            workExperience = new WorkExperience();
        }

        //--私有建構函式,clone工作經歷
        private Resume(WorkExperience workExperience)
        {
            this.workExperience = workExperience.Clone() as WorkExperience;
        }
        public void setPersonInfo(string age,string sex)
        {
            this.age = age;
            this.sex = sex;
        }

        public void setWorkExperience(string workDate,string workCompany)
        {
            workExperience.WorkDate = workDate;
            workExperience.WorkCompany = workCompany;
        }

        public void display()
        {
            Console.WriteLine("name={0},sex={1},age={2}",name,sex,age);
            Console.WriteLine("Work experience,time={0},company={1}", workExperience.WorkDate,workExperience.WorkCompany);
        }

        public object Clone()
        {
            Resume newResume = new Resume(workExperience);  //--先clone 工作經歷
            newResume.name = name;
            newResume.age = age;
            newResume.sex = sex;

            return newResume;
        }
    }
}

客戶端:

static void Main(string[] args)
        {
            Resume a = new Resume("daliao");
            a.setPersonInfo("25", "man");
            a.setWorkExperience("2years", "zte");

            Resume b = a.Clone() as Resume;
            b.setWorkExperience("2years", "huawei");
            a.display();
            b.display();
            Console.Read();
        }

輸出:

這裡寫圖片描述

模版模式

型別:行為型
使用場景:在抽象類中定義一個操作中演算法的骨架,而將一些步驟延遲到子類中,子類可以按需要重寫方法實現,模版方法使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。

這裡寫圖片描述

轉換成程式碼:
抽象類:

namespace DesignModeDemo.templatemethod
{
    abstract class AbstractClass
    {
        //--抽象方法,延遲到子類中實現
        public abstract void primitiveOpertion1();
        public abstract void primitiveOpertion2();

        //--演算法骨架,可以呼叫抽象方法
        public void templateMethod()
        {
            primitiveOpertion1();
            primitiveOpertion2();
        }
    }
}

具體類A:

namespace DesignModeDemo.templatemethod
{
    class ConcerateClassA:AbstractClass
    {
        public override void primitiveOpertion1()
        {
            Console.WriteLine("ConcerateClassA:primitiveOpertion1");
        }

        public override void primitiveOpertion2()
        {
            Console.WriteLine("ConcerateClassA:primitiveOpertion2");
        }
    }
}

具體類B:

namespace DesignModeDemo.templatemethod
{
    class ConcerateClassB:AbstractClass
    {
        public override void primitiveOpertion1()
        {
            Console.WriteLine("ConcerateClassB:primitiveOpertion1");
        }

        public override void primitiveOpertion2()
        {
            Console.WriteLine("ConcerateClassB:primitiveOpertion2");
        }
    }
}

客戶端程式碼:

  static void Main(string[] args)
        {
            AbstractClass c;
            c = new ConcerateClassA();
            c.templateMethod();

            c = new ConcerateClassB();
            c.templateMethod();
            Console.Read();
        }

Story:
考試試卷,試題不變,答題步驟不變,每個考生的答案不相同,可以採用此模式。

這裡寫圖片描述

試卷:

namespace DesignModeDemo.templatemethod
{
    abstract class TestPaper
    {
        public void question1()
        {
            Console.WriteLine("how old are you? a:20;b:30;c:40");
            Console.WriteLine("answer is :" + answer1());
        }
        protected abstract string answer1();

    }
}

考生A答案:

namespace DesignModeDemo.templatemethod
{
    class TestPaperA:TestPaper
    {
        protected override string answer1()
        {
            return "b";
        }
    }
}

考生B答案:

namespace DesignModeDemo.templatemethod
{
    class TestPaperB:TestPaper
    {
        protected override string answer1()
        {
            return "c";
        }
    }
}

客戶端:

static void Main(string[] args)
        {
            Console.WriteLine("StudentA answer");
            TestPaperA tpa = new TestPaperA();
            tpa.question1();

            Console.WriteLine("StudentB answer");
            TestPaperB tpb = new TestPaperB();
            tpb.question1();

            Console.Read();
        }

輸出:
這裡寫圖片描述

外觀模式

型別:結構型
使用場景:為子系統的一組介面提供一個一致的介面,外觀模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。它隱藏了系統的複雜性,並向客戶端提供了一個客戶端可以訪問系統的介面。
用外觀模式的時機:首先,在設計初期階段,應該要有意識的將不同的兩個層分離,層與層之間建立facade;其次,在開發階段,子系統會越來越複雜,增加外觀facade可以提供一個簡單的介面,減少它們之間的依賴。最後,在維護複雜程式碼時,也可以把遺留程式碼增加一個清晰的簡單介面。如:
這裡寫圖片描述

這裡寫圖片描述

轉換成程式碼:
已存在的子系統:

namespace DesignModeDemo.facade
{
    class SubSystemOne
    {
        public void methodOne()
        {
            Console.WriteLine("SubSystemOne:methodOne");
        }
    }

    class SubSystemTwo
    {
        public void methodTwo()
        {
            Console.WriteLine("SubSystemTwo:methodTwo");
        }
    }

}

Facade:

namespace DesignModeDemo.facade
{
    class Facade
    {
        SubSystemOne one;
        SubSystemTwo two;

        public Facade()
        {
            one = new SubSystemOne();
            two = new SubSystemTwo();
        }

        //--提供對外介面,隱藏細節
        public void methodA()
        {
            one.methodOne();
            two.methodTwo();
        }
    }
}

客戶端程式碼:

      static void Main(string[] args)
        {
            Facade fa = new Facade();
            fa.methodA();

            Console.Read();
        }

Story:
全民投資,炒股、炒房、炒國債,可以自己親力親為,也可以交給一家基金公司打理,把操作細節留給基金公司,客戶只要與基金公司聯絡。
地產:

namespace DesignModeDemo.facade
{
    class Realty
    {
        public void buy()
        {
            Console.WriteLine("buy Realty");
        }
        public void sell()
        {
            Console.WriteLine("sell Realty");
        }
    }
}

股票:

namespace DesignModeDemo.facade
{
    class Stock
    {
        public void buy()
        {
            Console.WriteLine("buy stock");
        }
        public void sell()
        {
            Console.WriteLine("sell stock");
        }
    }
}

國債:

namespace DesignModeDemo.facade
{
    class NationalDept
    {
        public void buy()
        {
            Console.WriteLine("buy NationalDept");
        }
        public void sell()
        {
            Console.WriteLine("sell NationalDept");
        }
    }
}

基金,隱藏了所有理財方式:

namespace DesignModeDemo.facade
{
    class Found
    {
        private Stock stock;
        private Realty realty;
        private NationalDept natioalDept;

        public Found()
        {
            stock = new Stock();
            realty = new Realty();
            natioalDept = new NationalDept();
         }

        public void buy()
        {
            stock.buy();
            realty.buy();
            natioalDept.buy();
        }

        public void sell()
        {
            stock.sell();
            realty.sell();
            natioalDept.sell();
        }
    }
}

客戶端程式碼:

 static void Main(string[] args)
        {
            Found found = new Found();
            found.buy();

            found.sell();
            Console.Read();
        }

輸出結果:
這裡寫圖片描述

建造者模式

型別:建立型
使用場景:
使用多個簡單的物件,一步一步構建成一個複雜的物件。將一個複雜物件的構建與其表示分離,使得同樣的構建過程可以建立不同的表示。

這裡寫圖片描述

這裡面有三個角色,指揮者根據使用者的需求構建物件;構造者用來裝配和構造各個部件;產品是具體的構建部件。
轉換成程式碼:

產品,包括了一個部件列表:

namespace DesignModeDemo.builder
{
    class Product
    {
        IList<string> part = new List<string>();//--產品部件

        public void add(string partItem)
        {
            part.Add(partItem);
        }

        public void show()
        {
            Console.WriteLine("build product");
            foreach (string p in part)
            {
                Console.WriteLine(p);
            }
        }
    }
}

抽象Builder類:

namespace DesignModeDemo.builder
{
    abstract class Builder
    {
        public abstract void buildPartA();

        public abstract void buildPartB();

        public abstract Product getResult();
    }
}

具體建造者類A:

namespace DesignModeDemo.builder
{
    class ConcreteBuildA:Builder
    {
        private Product product;

        public ConcreteBuildA()
        {
            product = new Product();
        }

        public override void buildPartA()
        {
            product.add("ConcreteBuildA:Product A");
        }

        public override void buildPartB()
        {
            product.add("ConcreteBuildA:Product B");
        }

        public override Product getResult()
        {
            return product;
        }

    }
}

具體建造者B:

namespace DesignModeDemo.builder
{
    class ConcereteBuildB:Builder
    {
        private Product product;

        public ConcereteBuildB()
        {
            product = new Product();
        }

        public override void buildPartA()
        {
            product.add("ConcereteBuildB:Product C");
        }

        public override void buildPartB()
        {
            product.add("ConcereteBuildB:Product D");
        }

        public override Product getResult()
        {
            return product;
        }
    }
}

指揮者:

namespace DesignModeDemo.builder
{
    class Director
    {
        public void construct(Builder builder)  //--指揮構造
        {
            builder.buildPartA();
            builder.buildPartB();
        }
    }
}

客戶端:

static void Main(string[] args)
        {
            Director director = new Director();
            ConcreteBuildA buildA = new ConcreteBuildA();
            ConcereteBuildB buildB = new ConcereteBuildB();

            director.construct(buildA);
            buildA.getResult().show();

            director.construct(buildB);
            buildB.getResult().show();

            Console.Read();
        }

輸出:
這裡寫圖片描述

Story:
畫一個小人,為了避免缺胳膊少腿,可利用建造者模式,由於涉及到UI,不好在控制檯實現一遍。

觀察者模式

型別:行為型
使用場景:定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件。這個主題物件在狀態發生變化時,會通知所有觀察者物件,使它們能夠自動更新自己。

這裡寫圖片描述

轉換成程式碼:
觀察者基類:

namespace DesignModeDemo.observer
{
    abstract class Observer
    {
        public abstract void update();
    }
}

具體觀察者:

namespace DesignModeDemo.observer
{
    class ConcerteObserver:Observer
    {
        private string name;

        private ConcerteSubject concerteSubject;  //--這裡沒有與具體主題解耦

        public ConcerteObserver(string name, ConcerteSubject concerteSubject)
        {
            this.name = name;
            this.concerteSubject = concerteSubject;
        }


        public override void update()
        {
            Console.WriteLine("Notify to :{0},Subject:{1} changed",name,concerteSubject.SubjectState);
        }

        public ConcerteSubject Subject
        {
            get { return concerteSubject; }
            set { concerteSubject = value; }
        }
    }
}

基類主題:

namespace DesignModeDemo.observer
{
    abstract class Subject
    {
        private IList<Observer> observers=new List<Observer>();

        public void attach(Observer o)
        {
            observers.Add(o);
        }

        public void detach(Observer o)
        {
            observers.Remove(o);
        }

        public void notify()
        {
            foreach(Observer o in observers)
            {
                o.update();
            }
        }
    }
}

具體主題:

namespace DesignModeDemo.observer
{
    class ConcerteSubject:Subject
    {
        private string subjectState;//--具體主題狀態

        public string SubjectState
        {
            get
            {
                return subjectState;
            }

            set
            {
                subjectState = value;
            }
        }
    }
}

客戶端程式碼:

 static void Main(string[] args)
        {
            ConcerteSubject cs = new ConcerteSubject();

            cs.attach(new ConcerteObserver("X", cs));
            cs.attach(new ConcerteObserver("Y", cs));
            cs.attach(new ConcerteObserver("Z", cs));

            cs.SubjectState = "new";
            cs.notify();

            Console.Read();
        }

輸出:
這裡寫圖片描述

Story:
老闆出去時,員工開始炒股票,老闆回來時,前臺發出通知,員工停止炒股票。
主題介面:

interface ISubject
    {
        void attach(AbstractObserver o);

        void detach(AbstractObserver o);

        void notify();

        string SubjectState
        {
            get;
            set;
        }
    }

具體主題Boss,維護一個觀察者列表:

  class Boss : ISubject
    {
        private IList<AbstractObserver> observers = new List<AbstractObserver>();

        private string state;

        public string SubjectState
        {
            get
            {
                return state;
            }

            set
            {
                state = value;
            }
        }

        public void attach(AbstractObserver o)
        {
            observers.Add(o);
        }

        public void detach(AbstractObserver o)
        {
            observers.Remove(o);
        }

        public void notify()
        {
            foreach (AbstractObserver o in observers)
            {
                o.update();
            }
        }
    }

抽象觀察者:

abstract class AbstractObserver
    {
        protected string name;
        protected ISubject subject;

        public AbstractObserver(string name, ISubject subject)
        {
            this.name = name;
            this.subject = subject;
        }

        public abstract void update();

    }

具體股票觀察者:

  class StockObserver : AbstractObserver
    {
        public StockObserver(string name, ISubject subject) : base(name, subject) { }

        public override void update()
        {
            Console.WriteLine("Notify to :{0},Boss:{1} minutes arrive,stop stock", name, subject.SubjectState);
        }
    }

具體NBA觀察者:

   class NBAObserver : AbstractObserver
    {
        public NBAObserver(string name, ISubject subject) : base(name, subject) { }

        public override void update()
        {
            Console.WriteLine("Notify to :{0},Boss:{1} minutes arrive,stop watch NBA", name, subject.SubjectState);
        }
    }

客戶端程式碼:

   static void Main(string[] args)
        {
            Boss bs = new Boss();

            bs.attach(new NBAObserver("shao", bs));
            bs.attach(new StockObserver("wang", bs));

            bs.SubjectState = "5";

            bs.notify();

            Console.Read();
        }

輸出:
這裡寫圖片描述

改進與不足:
當一個主題發生變化時,其實沒法為每個觀察者都去實現抽象觀察者的介面,因為有些觀察者早已被他們的製造商給封裝了。解決此類問題的方法是使用事件委拖。

“看股票觀察類”和“看NBA觀察類”,去掉了父類“抽象觀察者類”,並將update方法更名為各自適合的方法名。

主題介面,去掉了attach、detach方法:

interface ISubject
    {
        void notify();

        string SubjectState
        {
            get;
            set;
        }
    }

Boss主題,使用委託代替維護的觀察者列表。

class Boss : ISubject
    {
        public NotifyHandler notifyHandler=null;
        private string state;
        public delegate void NotifyHandler();

        public string SubjectState
        {
            get
            {
                return state;
            }

            set
            {
                state = value;
            }
        }


        public void notify()
        {
            notifyHandler();
        }
    }

股票觀察者,不再實現抽象觀察者的統一介面:

class StockObserver
    {
        private string name;
        private ISubject sub;

        public StockObserver(string name, ISubject sub)
        {
            this.name = name;
            this.sub = sub;

        }

        public void stopStock()
        {
            Console.WriteLine("Notify to :{0},Boss:{1} minutes arrive,stop stock", name, sub.SubjectState);
        }
    }

NBA觀察者,也再實現抽象觀察者的統一介面:

class NBAObserver
    {
        private string name;
        private ISubject subject;
        public NBAObserver(string name, ISubject subject)
        {
            this.name = name;
            this.subject = subject;
        }

        public void stopNBA()
        {
            Console.WriteLine("Notify to :{0},Boss:{1} minutes arrive,stop watch NBA", name, subject.SubjectState);
        }
    }

客戶端程式碼:

 static void Main(string[] args)
        {
            Boss bs = new Boss();

            bs.notifyHandler += new NBAObserver("shao", bs).stopNBA;
            bs.notifyHandler += new StockObserver("wang", bs).stopStock;

            bs.SubjectState = "5";

            bs.notify();

            Console.Read();
        }

輸出:
這裡寫圖片描述

委託相當於C語言的函式指標,一個委託可以搭載多個物件,所有方法被依次喚起,委託物件所搭載的方法並不需要屬於同一個類,也不限定方法的名稱,只需方法具有相同的引數列表和返回值型別。

介面卡模式

型別:結構型
使用場景:介面卡模式作為兩個不相容的介面之間的橋樑。它結合了兩個獨立介面的功能。
比如.net中的DataAdapter,用作DataSet和資料來源之間的介面卡以便檢索和儲存資料。不同的資料來源,都對映到Fill和Update來進行適配。
這裡寫圖片描述

轉換成程式碼:
被介面卡類:

namespace DesignModeDemo.adapter
{
    class Adaptee
    {
        public void specifiRequest()
        {
            Console.WriteLine("special request");
        }
    }
}

介面卡目標:

namespace DesignModeDemo.adapter
{
    class Target
    {
        public virtual void request()
        {
            Console.WriteLine("normal request");
        }
    }
}

介面卡:

namespace DesignModeDemo.adapter
{
    class Adapter:Target
    {
        private Adaptee adaptee = new Adaptee();

        public override void request()
        {
            adaptee.specifiRequest();
        }
    }
}

客戶端程式碼:

 static void Main(string[] args)
        {
            Adapter ada = new Adapter();
            ada.request();

            Console.Read();
        }

Story:
姚明去火箭打球時,需要翻譯,這個翻譯的功能就是一個介面卡模式。
這裡寫圖片描述

外藉中鋒:

namespace DesignModeDemo.adapter
{   
    class ForeginCenter
    {
        private string name;

        public string Name
        {
            get
            {
                return name;
            }

            set
            {
                name = value;
            }
        }

        public void 進攻()
        {
            Console.WriteLine("外藉中鋒{0},進攻",name);
        }

        public void 防守()
        {
            Console.WriteLine("外藉中鋒{0},防守", name);
        }
    }
}

Player類:

namespace DesignModeDemo.adapter
{
    abstract class Player
    {
        public abstract void attack();

        public abstract void defense();
    }
}

前鋒:

namespace DesignModeDemo.adapter
{
    class Forwards : Player
    {
        private string name;
        public string Name
        {
            get
            {
                return name;
            }

            set
            {
                name = value;
            }
        }
        public override void attack()
        {
            Console.WriteLine("前鋒{0},進攻", name);
        }

        public override void defense()
        {
            Console.WriteLine("前鋒{0},進攻", name);
        }
    }
}

翻譯:

namespace DesignModeDemo.adapter
{
    class Translator : Player
    {

        private ForeginCenter fc;

        public Translator(string name)
        {
            fc = new ForeginCenter();
            fc.Name = name;
        }

        public override void attack()
        {
           fc.進攻();
        }

        public override void defense()
        {
            fc.防守();
        }
    }
}

客戶端程式碼:

static void Main(string[] args)
        {
            Forwards forwards = new Forwards();
            forwards.Name = "麥迪";

            forwards.attack();
            forwards.defense();

            Translator tran = new Translator("姚明");
            tran.attack();
            tran.defense();

            Console.Read();
        }

輸出:
這裡寫圖片描述

一點思考:
介面卡模式和代理模式太相象了,他們最明顯的區別是代理和被代理者實際上具有相同的介面,結構圖如下:
這裡寫圖片描述

可能由於某種原因,客戶訪問不到被代理者,所以利用代理間接訪問。而介面卡模式,是因為介面不同,後期維護或用到第三方庫時作為一種統一的對映。

狀態模式

型別:行為型
使用場景:當一個物件的內在狀態改變時,允許改變其行為,這個物件看起來像是改變了其類。狀態模式主要解決的是當控制一個物件狀態轉換的條件表示式過於複雜時的情況。把狀態的判斷邏輯轉移到表示不同狀態的一系列類當中,可以把複雜的判斷邏輯簡化。
這裡寫圖片描述

這個模式的困惑之處是,上下文Context引用了一個狀態State,但同時會把Context自身作為引數傳遞給State,以使得具體State類中可以改變當前Context的狀態。

轉換成程式碼:
抽象狀態類:

namespace DesignModeDemo.state
{
    abstract class State
    {
        public abstract void handle(Context context);
    }
}

具體類:

namespace DesignModeDemo.state
{
    class ConcreteStateA : State
    {
        public override void handle(Context context)
        {
            Console.WriteLine("ConcreteStateA will be switch to ConcreteStateB");
            context.State=new ConcreteStateB();
        }
    }


    class ConcreteStateB: State
    {
        public override void handle(Context context)
        {
            Console.WriteLine("ConcreteStateB will be switch to ConcreteStateA");
            context.State = new ConcreteStateA();
        }
    }

}

上下文類:

namespace DesignModeDemo.state
{
    class Context
    {
        private State state;

        public Context(State state)
        {
            this.state = state;
        }

        public State State
        {
            get
            {
                return state;
            }

            set
            {
                state = value;
                Console.WriteLine("Current state:"+state.GetType().Name);
            }
        }

        public void request()
        {
            state.handle(this);  //--呼叫當前狀態的處理控制程式碼,並把上下文傳進去,以方便更改狀態
        }

    }
}

客戶端程式碼:

 static void Main(string[] args)
        {
            ConcreteStateA stateA = new ConcreteStateA();

            state.Context ct = new state.Context(stateA);

            //--同一個呼叫,狀態已經發生了改變
            ct.request();
            ct.request();

            Console.Read();
        }

輸出:
這裡寫圖片描述

Story:
程式設計師的工作時長,決定了他的工作效率。可以以時間為條件來判斷他處於什麼工作狀態。
如,早上9點打了雞血,晚上9點就很疲憊。
這裡寫圖片描述

工作狀態基類:

namespace DesignModeDemo.state
{
    abstract class WorkSateBase
    {

        public abstract void writePrograme(Worker worker);
    }
}

早、中、晚的工作狀態:

class ForenoonState : WorkSateBase
    {
        public override void writePrograme(Worker worker)
        {
            if(worker.Hour<12)
            {
                Console.WriteLine("時間:{0},工作激情",worker.Hour);
            }
            else
            {
                worker.WorkState = new NoonState();
                worker.writeProgram();
            }
        }
    }

    class NoonState : WorkSateBase
    {
        public override void writePrograme(Worker worker)
        {
            if (worker.Hour < 13)
            {
                Console.WriteLine("時間:{0},需吃飯", worker.Hour);
            }
            else
            {
                worker.WorkState = new AfternoonState();
                worker.writeProgram();
            }
        }
    }

    class AfternoonState : WorkSateBase
    {
        public override void writePrograme(Worker worker)
        {
            if (worker.Hour < 18)
            {
                Console.WriteLine("時間:{0},快下班", worker.Hour);
            }
            else
            {
                Console.WriteLine("時間:{0},下班啦", worker.Hour);
                worker.IsFinished = true;
                worker.WorkState = new ForenoonState();//--重新開始,很重要              
            }
        }
    }

工作類:

class Worker
    {
        private int hour;
        private bool isFinished;
        private WorkSateBase workState;

        public Worker(WorkSateBase workState)
        {
            this.WorkState = workState;
            isFinished = false;
        }

        public void writeProgram()
        {
            WorkState.writePrograme(this); //--當前狀態下寫程式,並可能切換工作狀態
        }

        public int Hour
        {
            get
            {  return hour; }

            set
            { hour = value; }
        }

        public bool IsFinished
        {
            get
            { return isFinished; }

            set
            { isFinished = value;}
        }

        internal WorkSateBase WorkState
        {
            get
            {    return workState;   }

            set
            {     workState = value;   }
        }
    }

客戶端程式碼:

 static void Main(string[] args)
        {
            Worker work = new Worker(new ForenoonState());
            work.Hour = 10;
            work.writeProgram();

            work.Hour = 17;
            work.writeProgram();

            work.Hour = 19;
            work.writeProgram();

            work.Hour = 9;
            work.writeProgram();
            Console.Read();
        }

輸出:
這裡寫圖片描述

備忘錄模式

型別:行為型
使用場景:在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態。這樣以後就可以將該物件恢復到原先儲存的狀態。

這裡寫圖片描述

這裡有三個角色:
Originator,發起者,負責記錄當前時刻的某些屬性到一個備忘錄或者從備忘錄中恢復。
Memento,備忘錄,儲存發起者的內部狀態。
Caretaker,管理者,用來管理備忘錄,相當於一個存取備忘錄的容器。

轉換成程式碼:
備忘錄:

namespace DesignModeDemo.memento
{
    class Memento
    {
        private string state;  //--需要建立備忘錄的資料

        public Memento(string state)
        {
            this.State = state;
        }

        public string State
        {
            get
            {
                return state;
            }

            set
            {
                state = value;
            }
        }
    }
}

管理者:

namespace DesignModeDemo.memento
{
    class Caretaker 
    {
        private Memento memento;  //--也可以定義成一個list,儲存多個時刻的備忘錄

        internal Memento Memento
        {
            get
            {
                return memento;
            }

            set
            {
                memento = value;
            }
        }
    }
}

發起者:

namespace DesignModeDemo.memento
{
    class Originator
    {
        private string state; //--需要儲存的屬性,可能有多個

        public string State
        {
            get
            {
                return state;
            }

            set
            {
                state = value;
            }
        }


        public Memento createMemento()  //--建立備忘錄
        {
            return new Memento(state);
        }

        public void setMemento(Memento memento) //--從備忘錄恢復
        {
            state = memento.State;
        }

        public void show()
        {
            Console.WriteLine("State= "+ state);
        }
    }
}

客戶端程式碼:

  static void Main(string[] args)
        {
            Originator originator = new Originator();
            originator.State = "before start";

            Caretaker back = new Caretaker();
            back.Memento = originator.createMemento();

            originator.State = "starting";
            originator.show();

            //--從備忘錄恢復
            originator.setMemento(back.Memento);
            originator.show();

            Console.Read();
        }

輸出:
這裡寫圖片描述

Story:
存放打遊戲中的引數,只是多了些引數,其它與上面的程式碼完全一致。

組合模式

型別:結構型
使用場景:又叫部分-整體模式,將物件組合成樹形結構以表示“部分-整體”的層次結構。
組合模式使得使用者對單個物件與組合物件的使用具有一致性。
這裡寫圖片描述

這個模式的要點是Composite類中,可能維護了很多子Component物件,可能一直遞迴下去。

轉換成程式碼:
抽象Component類:

namespace DesignModeDemo.composite
{
    abstract class Component
    {
        protected string name;

        public Component(string name)
        {
            this.name = name;
        }

        public abstract void addComponent(Component comp);
        public abstract void removeComponent(Component comp);
        public abstract void displayComponent(int  dept);
    }
}

組合物件:

 namespace DesignModeDemo.composite
{
    class Composite : Component
    {
        private IList<Component> children = new List<Component>();

        public Composite(string name) : base(name)
        {
        }

        public override void addComponent(Component comp)
        {
            children.Add(comp); 
        }

        public override void displayComponent(int dept)
        {
            Console.WriteLine(new String('-', dept) + name);

            //--遍歷顯示下級
            foreach(Component c in children)
            {
                c.displayComponent(dept+2);
            }
        }

        public override void removeComponent(Component comp)
        {
            children.Remove(comp);
        }
    }
}

客戶端程式碼:

           static void Main(string[] args)
        {
            Composite root = new Composite("root");
            root.addComponent(new Leaf("etc"));
            root.addComponent(new Leaf("lib"));

            Composite home = new Composite("home");
            home.addComponent(new Leaf("idcim"));
            home.addComponent(new Leaf("zxin10"));

            Composite cmdb = new Composite("cmdb");
            cmdb.addComponent(new Leaf("etc"));
            cmdb.addComponent(new Leaf("log"));

            home.addComponent(cmdb);

            root.addComponent(home);
            root.displayComponent(1);

            Console.Read();
        }

輸出:
這裡寫圖片描述

Story:
公司的管理系統,公司有很多平行的子部門,也可能有很多子公司,子公司具有相同的子部門。如:
這裡寫圖片描述

結構圖:
這裡寫圖片描述

抽象公司類:

namespace DesignModeDemo.composite
{
    abstract class Company
    {
        protected string name;
        public Company(string name)
        {
            this.name = name;
        }

        public abstract void add(Company comp);

        public abstract void remove(Company comp);

        public abstract void display(int dept);

        public abstract void doDuty();
    }
}

具體子公司、子部門:

namespace DesignModeDemo.composite
{
    class ChildCompany : Company
    {
        private IList<Company> childrenCompany=new List<Company>();
        public ChildCompany(string name) : base(name)
        {
        }

        public override void add(Company comp)
        {
            childrenCompany.Add(comp); 
        }

        public override void display(int dept)
        {
            Console.WriteLine(new String('-', dept) + name);

            //--遍歷顯示下級
            foreach (Company c in childrenCompany)
            {
                c.display(dept + 2);
            }
        }

        public override void doDuty()
        {
            foreach (Company c in childrenCompany)
            {
                c.doDuty();
            }
        }

        public override void remove(Company comp)
        {
            childrenCompany.Remove(comp) ;
        }
    }
}
namespace DesignModeDemo.composite
{
    class ChildCompany : Company
    {
        private IList<Company> childrenCompany=new List<Company>();
        public ChildCompany(string name) : base(name)
        {
        }

        public override void add(Company comp)
        {
            childrenCompany.Add(comp); 
        }

        public override void display(int dept)
        {
            Console.WriteLine(new String('-', dept) + name);

            //--遍歷顯示下級
            foreach (Company c in childrenCompany)
            {
                c.display(dept + 2);
            }
        }

        public override void doDuty()
        {
            foreach (Company c in childrenCompany)
            {
                c.doDuty();
            }
        }

        public override void remove(Company comp)
        {
            childrenCompany.Remove(comp) ;
        }
    }
}
namespace DesignModeDemo.composite
{
    class HRDepartment : Company
    {
        public HRDepartment(string name) : base(name)
        {
        }

        public override void add(Company comp)
        {

        }

        public override void display(int dept)
        {
            Console.WriteLine(new String('-', dept) + name);
        }

        public override void doDuty()
        {
            Console.WriteLine("{0},hr",name);
        }

        public override void remove(Company comp)
        {

        }
    }
}

客戶端:

 static void Main(string[] args)
        {
            ChildCompany root = new ChildCompany("北京總公司");
            root.add(new HRDepartment("總公司人力資源部"));
            root.add(new FinDepartment("總公司財務部"));

            ChildCompany home = new ChildCompany("華東公司");
            home.add(new HRDepartment("華東公司人力資源部"));
            home.add(new FinDepartment("華東公司財務部"));

            ChildCompany nj = new ChildCompany("南京公司");
            nj.add(new HRDepartment("南京公司人力資源部"));
            nj.add(new FinDepartment("南京公司財務部"));

            home.add(nj);

            root.add(home);
            root.display(1);

            Console.Read();
        }

輸出:
這裡寫圖片描述

橋接模式

型別:結構型
使用場景:將抽象部分與實現部分分離,使它們都可以獨立的變化。這句話不太好理解,需要結合結構圖:

這裡寫圖片描述

這裡可以看出來抽象與實現是分離的,抽象出了一個Operation方法,至於是選用A類的方法來實現還是B類的方法來實現,只要修改維護的聚合引用就行了。下面是一個更實際的結構圖:
這裡寫圖片描述
抽象層抽象出draw這個功能,畫紅色的圓還是畫綠色的圓等,由具體的類來實現。現在是單維度在變化,如果從Shape裡面繼承出一個方形出來,並且增加畫紅色的方形和畫綠色的方形,就成了多維度在變化,按需要組合即可。
將抽象部分與它的實現部分分離,可以理解為實現系統可能有多角度分類,每一種分類都有可能 變化,那麼就把這種多角度分離現來讓它們獨立變化,減少它們之間的耦合。

轉化成程式碼:
抽象Implementor類:

namespace DesignModeDemo.bridge
{
    abstract class Implementor
    {
        public abstract void operation();
    }
}

具體實現類:

namespace DesignModeDemo.bridge
{
    class CocreteImplementorA : Implementor
    {
        public override void operation()
        {
            Console.WriteLine("CocreteImplementorA operation");
        }
    }

    class CocreteImplementorB : Implementor
    {
        public override void operation()
        {
            Console.WriteLine("CocreteImplementorB operation");
        }
    }
}
class Abstraction
    {
        protected Implementor implementor;

        public void setImplementor(Implementor implementor)
        {
            this.implementor = implementor;
        }

        public virtual void operation()
        {
            implementor.operation();
        }
    }
namespace DesignModeDemo.bridge
{
    class RefinedAbstraction:Abstraction
    {
        public override void operation()
        {
            implementor.operation();
        }
    }
}

客戶端程式碼:

static void Main(string[] args)
        {
            Abstraction ab = new RefinedAbstraction();
            ab.setImplementor(new CocreteImplementorA());
            ab.operation();

            ab.setImplementor(new CocreteImplementorB());
            ab.operation();

            Console.Read();
        }

輸出:
這裡寫圖片描述

Story:
手機品牌和手機軟體是兩個不同的維度,手機品牌N上的軟體不能執行在手機品牌M上,結構圖應該使用組合,而不是繼承。
有三種結構圖:
這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述

前面兩個結構圖,當增加了手機品牌或增加了手機軟體時,還要去修改原來的類,違返了開放—封閉原則。
抽象手機軟體:

namespace DesignModeDemo.bridge
{
    abstract class HandsetSoft
    {
        public abstract void run();
    }
}

具體手機軟體:

namespace DesignModeDemo.bridge
{
    class HandsetGame : HandsetSoft
    {
        public override void run()
        {
            Console.WriteLine("執行手機遊戲");
        }
    }

    class HandsetMail : HandsetSoft
    {
        public override void run()
        {
            Console.WriteLine("執行手機郵箱");
        }
    }
}

手機品牌:

namespace DesignModeDemo.bridge
{
    abstract class HandsetBrand
    {
        protected HandsetSoft soft;        

        public void setHandsetSoft(HandsetSoft soft)
        {
            this.soft = soft;
        }

        public abstract void run();
    }

    class HandsetBrandM : HandsetBrand
    { 
        public override void run()
        {
            Console.WriteLine("手機品牌M,執行:");
            soft.run();
        }
    }

    class HandsetBrandN : HandsetBrand
    {       
        public override void run()
        {
            Console.WriteLine("手機品牌N,執行:");
            soft.run();
        }
    }
}

客戶端:

static void Main(string[] args)
        {
            HandsetBrand m = new HandsetBrandM();
            m.setHandsetSoft(new HandsetMail());
            m.run();
            m.setHandsetSoft(new HandsetGame());
            m.run();

            m = new HandsetBrandN();
            m.setHandsetSoft(new HandsetMail());
            m.run();
            m.setHandsetSoft(new HandsetGame());
            m.run();

            Console.Read();
        }

輸出:
這裡寫圖片描述

命令模式

型別:行為型
使用場景:將一個請求封裝成一個物件,從而可以用不同的請求對客戶進行引數化。

這裡寫圖片描述

命令模式的關鍵點是ConcreteCommand類中維護了一個Receiver的引用。

轉換成程式碼:
抽象命令:

namespace DesignModeDemo.command
{
    abstract class Command
    {
        protected Receiver receiver;
        public Command(Receiver receiver)
        {
            this.receiver = receiver;
        }

        abstract public void execute();
    }
}

具體命令:

namespace DesignModeDemo.command
{
    class ConcerteCommand : Command
    {
        public ConcerteCommand(Receiver receiver) : base(receiver)
        {
        }

        public override void execute()
        {
            receiver.action();
        }
    }
}

接收命令:

namespace DesignModeDemo.command
{
    class Receiver
    {
        public void action()
        {
            Console.WriteLine("執行請求!");
        }
    }
}

執行命令類:

namespace DesignModeDemo.command
{
    class Invoker
    {
        private Command command;

        public void setCommand(Command command)
        {
            this.command = command;
        }

        public void executeCommand()
        {
            command.execute();
        }
    }
}

客戶端:

static void Main(string[] args)
        {
            Command com = new ConcerteCommand(new Receiver());

            Invoker invoke = new Invoker();
            invoke.setCommand(com);

            com.execute();
            Console.Read();
        }

Story:
烤燒烤功能就是常見的命令模式,顧客說怎麼烤,攤主就怎麼烤。

職責鏈模式

型別:行為型
使用場景:為請求建立了一個接收者物件的鏈,這種模式給予請求的型別,對請求的傳送者和接收者進行解耦。典型的如JS中的事件冒泡。

這裡寫圖片描述

這個模式的關鍵是有點像C語言中的鏈式結構,有一個指向下一個結構的指標。當然,這裡是一個引用。

轉換成程式碼:

namespace DesignModeDemo.responschain
{
    abstract class Handler
    {
        protected Handler successor;//--下一個處理Handler

        public void setSuccessor(Handler successor)
        {
            this.successor = successor;
        }

        public abstract void handleRequest(int request);
    }
}
namespace DesignModeDemo.responschain
{
    class ConcerateHandler1 : Handler
    {       
        public override void handleRequest(int request)
        {
          if(request>=0 && request<10)
            {
                Console.WriteLine("{0} 處理請求 {1}",this.GetType().Name,request);
            }
            else if(successor!=null)
            {
                successor.handleRequest(request); //--向下轉移
            }  
        }
    }

    class ConcerateHandler2 : Handler
    {
        public override void handleRequest(int request)
        {
            if (request >= 10 && request <20)
            {
                Console.WriteLine("{0} 處理請求 {1}", this.GetType().Name, request);
            }
            else if (successor != null)
            {
                successor.handleRequest(request); //--向下轉移
            }
        }
    }

    class ConcerateHandler3 : Handler
    {
        public override void handleRequest(int request)
        {
            if (request >= 20)
            {
                Console.WriteLine("{0} 處理請求 {1}", this.GetType().Name, request);
            }
            else if (successor != null)
            {
                successor.handleRequest(request); //--向下轉移
            }
        }
    }
}

客戶端程式碼:

static void Main(string[] args)
        {
            ConcerateHandler1 h1 = new ConcerateHandler1();
            ConcerateHandler2 h2 = new ConcerateHandler2();
            ConcerateHandler3 h3 = new ConcerateHandler3();

            h1.setSuccessor(h2);
            h2.setSuccessor(h3);

            int[] requests = { 4, 9, 15, 25 };

            foreach (int i in requests)
            {
                h1.handleRequest(i);
            }

輸出:

這裡寫圖片描述

一點思考:
職責鏈模式與狀態模式有些相近,都有一個處理控制程式碼,不過一個是處理請求,一個是處理上下文。另外,狀態模式的狀態是相互獨立的,而職責鏈可理解為等級森嚴的一條鏈結構。

這裡寫圖片描述

中介者模式

型別:行為型
使用場景:用一箇中介物件來封裝一系列的物件互動。中介者使各物件不需要顯式的相互引用,從而使其耦合鬆散,而且可以獨立的改變它們之間的互動。

這裡寫圖片描述

這裡,Colleague(同事)與同事之間互不相識,但它們都認識中介者,中介者認識所有同事。

轉換成程式碼:
抽象同事類:

namespace DesignModeDemo.mediator
{
    abstract class Colleague
    {
        protected Mediator mediator; //--同事只認識中介者

        public Colleague(Mediator mediator)
        {
            this.mediator = mediator;
        }
    }
}

具體同事為類:

 namespace DesignModeDemo.mediator
{
    class ConcreteColleague1 : Colleague
    {
        public ConcreteColleague1(Mediator mediator) : base(mediator)
        {
        }

        public void send(string msg)
        {
            mediator.send(msg,this); //--通過中介者傳送訊息
        }

        public void notify(string msg)
        {
            Console.WriteLine("同事1得到訊息:"+msg);
        }
    }

    class ConcreteColleague2 : Colleague
    {
        public ConcreteColleague2(Mediator mediator) : base(mediator)
        {
        }

        public void send(string msg)
        {
            mediator.send(msg, this); //--通過中介者傳送訊息
        }

        public void notify(string msg)
        {
            Console.WriteLine("同事2得到訊息:" + msg);
        }
    }
}       

抽象中介者:

namespace DesignModeDemo.mediator
{
    abstract class Mediator
    {
        public abstract void send(string message,Colleague colleague); //--向某個同事發訊息

    }
}

具體中介者:

 namespace DesignModeDemo.mediator
{
    class ConcreteMediator : Mediator
    {
        private ConcreteColleague1 c1;
        private ConcreteColleague2 c2;

        internal ConcreteColleague1 C1
        {
            set
            {
                c1 = value;
            }
        }

        internal ConcreteColleague2 C2
        {         
            set
            {
                c2 = value;
            }
        }

        public override void send(string message, Colleague colleague)
        {
           if(colleague==c1)  //--如果訊息由c1發出,則通知c2
            {
                c2.notify(message);
            }else if(colleague == c2)
            {
                c1.notify(message);
            }
        }
    }

}

            Console.Read();
        }

客戶端程式碼:

 static void Main(string[] args)
        {
            ConcreteMediator media = new ConcreteMediator();

            ConcreteColleague1 c1 = new ConcreteColleague1(media);
            ConcreteColleague2 c2 = new ConcreteColleague2(media);

            media.C1 = c1;
            media.C2 = c2;

            c1.send("are you ok?");
            c2.send("no ok");

            Console.Read();
        }

輸出:
這裡寫圖片描述

中介者優缺點:
中介者的優點是集中控制,使物件與物件之間解耦了;缺點也是來自於集中控制,使得中介者可能會比較複雜。

享元模式

型別:結構型
使用場景:享元模式嘗試重用現有的同類物件,如果未找到匹配的物件,則建立新物件。它主要用於減少建立物件的數量,以減少記憶體佔用和提高效能。常結合HashMap使用。

這裡寫圖片描述

這個結構圖看起復雜,其實實際應用中不知不覺就用了。這裡的重點不是享元類的方法,而是享元工廠建立享元的時機,以及如何獲得享元物件。

轉換成程式碼:
抽象享元:

namespace DesignModeDemo.flyweight
{
    abstract class Flyweight
    {
        public abstract void operation(int state);
    }
}

具體享元:

namespace DesignModeDemo.flyweight
{
    class ConcreateFlyweight : Flyweight
    {
        public override void operation(int state)
        {
            Console.WriteLine("具體flyweight:"+state);
        }
    }
}

享元工廠:

namespace DesignModeDemo.flyweight
{
    class FlyweightFactory
    {
        private Hashtable flys=new Hashtable();

        public Flyweight getFlyweight(string key)
        {
            if(!flys.ContainsKey(key))
            {
                flys.Add(key,new ConcreateFlyweight());
            }

            return flys[key] as Flyweight;
        }
    }
}

客戶端程式碼:

 static void Main(string[] args)
        {
            int state = 20;

            FlyweightFactory ff = new FlyweightFactory();

            Flyweight fx = ff.getFlyweight("x");
            fx.operation(--state);

            Flyweight fy = ff.getFlyweight("y");
            fy.operation(--state);

            Console.Read();
        }

輸出:
這裡寫圖片描述

Story:
享元模式有內部狀態與外部狀態之分。在享元物件內部並且不會隨環境改變而改變的共享部分,稱為內部狀態;不可以共享部分則為外部狀態。通常是把外部狀態移到類例項的外面,在方法呼叫時將它們傳遞進來。

例如,對於給很多使用者開發的網站,都是同一套東西,但使用者的Id不同,結構圖為:

這裡寫圖片描述

抽象網站:

namespace DesignModeDemo.flyweight
{
    abstract class Website
    {
        public abstract void user(User user); //--外部狀態
    }
}

具體網站:

namespace DesignModeDemo.flyweight
{
    class ConcreateWebsite : Website
    {
        private string webName;

        public ConcreateWebsite(string webName)
        {
            this.webName = webName;
        }
        public override void user(User user)
        {
            Console.WriteLine("網站分類:"+webName+",使用者:"+user.Name);
        }
    }
}

外部狀態,使用者名稱:

namespace DesignModeDemo.flyweight
{
    class User
    {
        private string name;

        public User(string name)
        {
            this.name = name;
        }

        public string Name
        {
            get
            {
                return name;
            }
        }
    }
}

網站工廠:

namespace DesignModeDemo.flyweight
{
    class WebsiteFactory
    {
        private Hashtable website = new Hashtable();

        public Website getWebsite(string key)
        {
            if (!website.ContainsKey(key))
            {
                website.Add(key, new ConcreateWebsite(key));
            }

            return website[key] as Website;
        }
    }
}

客戶端程式碼:

static void Main(string[] args)
        {
            WebsiteFactory wf = new WebsiteFactory();

            Website wx = wf.getWebsite("足球");
            wx.user(new User("123"));

            Website wy = wf.getWebsite("籃球");
            wy.user(new User("456"));

            Console.Read();
        }

輸出:
這裡寫圖片描述

訪問者模式

型別:行為型
使用場景:表示一個作用於某物件結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用於這些元素的新操作。

這裡寫圖片描述

這個結構圖的要點:
Element類的具體繼承個數是定的,比如這裡只有兩個具體元素A和B。訪問者介面定義了訪問這兩個具體元素的函式,引數是要訪問的訪問者。而元素類有一個接受訪問者的函式。

轉換成程式碼:
抽象元素,以及兩個具體元素:

namespace DesignModeDemo.visitor
{
    abstract class Element
    {
        public abstract void accept(Visitor visitor); 
    }
   class ConcreateElementA:Element
    {
        public override void accept(Visitor visitor)
        {
            visitor.visitorConcreateElementA(this);
        }

    }

class ConcreateElementB : Element
    {
        public override void accept(Visitor visitor)
        {
            visitor.visitorConcreateElementB(this);  
        }
    }
}

抽象訪問者,及兩個具體訪問者:

namespace DesignModeDemo.visitor
{
     abstract class Visitor
    {
        public abstract void visitorConcreateElementA(ConcreateElementA elementA);
        public abstract void visitorConcreateElementB(ConcreateElementB elementB);
    }

    class ConcreateVisitor1 : Visitor
    {
        public override void visitorConcreateElementA(ConcreateElementA elementA)
        {
            Console.WriteLine("{0}被{1}訪問",elementA.GetType().Name,this.GetType().Name);
        }

        public override void visitorConcreateElementB(ConcreateElementB elementB)
        {
            Console.WriteLine("{0}被{1}訪問", elementB.GetType().Name, this.GetType().Name);
        }
    }

class ConcreateVisitor2:Visitor
    {
        public override void visitorConcreateElementA(ConcreateElementA elementA)
        {
            Console.WriteLine("{0}被{1}訪問", elementA.GetType().Name, this.GetType().Name);
        }

        public override void visitorConcreateElementB(ConcreateElementB elementB)
        {
            Console.WriteLine("{0}被{1}訪問", elementB.GetType().Name, this.GetType().Name);
        }
    }}

列舉所有元素物件:

namespace DesignModeDemo.visitor
{
    class ObjectStructure
    {
        private IList<Element> elements = new List<Element>();

        public void attach(Element e)
        {
            elements.Add(e);
        }

        public void detach(Element e)
        {
            elements.Remove(e);
        }

        public void accept(Visitor visitor)
        {
            foreach(Element e in elements)
            {
                e.accept(visitor);
            }
        }
    }
}

客戶端程式碼:

 static void Main(string[] args)
        {
            ObjectStructure elementList = new ObjectStructure();

            elementList.attach(new ConcreateElementA());
            elementList.attach(new ConcreateElementB());

            ConcreateVisitor1 visitor1 = new ConcreateVisitor1();
            ConcreateVisitor2 visitor2 = new ConcreateVisitor2();

            elementList.accept(visitor1);
            elementList.accept(visitor2);
            Console.Read();
        }

輸出:
這裡寫圖片描述

Story:
用訪問者模式實現這段話:

男人成功時,背後多半有一個偉大的女人。
女人成功時,背後大多有一個不成功的男人。
男人失敗時,悶頭喝酒,誰也不用勸。
女人失敗時,眼淚汪汪,誰也勸不了。
男人戀愛時,凡事不懂也要裝懂。
女人戀愛時,遇事懂也裝作不懂。

這裡面的男人、女人相當於兩個固定數量的元素,成功、失敗、戀愛這些狀態相當於訪問者,可以隨時增加。

男人、女人及抽象類:

namespace DesignModeDemo.visitor
{
    abstract class Person
    {
        public abstract void accept(Action visitor);
    }

class Man : Person
    {
        public override void accept(Action visitor)
        {
            visitor.getManConclustion(this);
        }
    }

class Womean : Person
    {
        public override void accept(Action visitor)
        {
            visitor.getWomeanConclustion(this);
        }
    }
}

成功、失敗、戀愛的狀態:

namespace DesignModeDemo.visitor
{
   abstract class Action
    {
        public abstract void getManConclustion(Man man); //--男人反應
        public abstract void getWomeanConclustion(Womean womean); //--女人反應
    }

class Fail:Action
    {
        public override void getManConclustion(Man man)
        {
            Console.WriteLine("{0} {1} 時,悶頭喝酒,誰也不用勸", man.GetType().Name, this.GetType().Name);
        }

        public override void getWomeanConclustion(Womean womean)
        {
            Console.WriteLine("{0} {1} 時,眼淚汪汪,誰也勸不了", womean.GetType().Name, this.GetType().Name);
        }
    }

class Success : Action
    {
        public override void getManConclustion(Man man)
        {
            Console.WriteLine("{0} {1} 時,背後有一個成功的女人",man.GetType().Name,this.GetType().Name);
        }

        public override void getWomeanConclustion(Womean womean)
        {
            Console.WriteLine("{0} {1} 時,背後有一個不成功的男人", womean.GetType().Name, this.GetType().Name);
        }
    }}

class Amativeness:Action
    {
        public override void getManConclustion(Man man)
        {
            Console.WriteLine("{0} {1} 時,凡事不懂也要裝懂", man.GetType().Name, this.GetType().Name);
        }

        public override void getWomeanConclustion(Womean womean)
        {
            Console.WriteLine("{0} {1} 時,遇事懂也裝作不懂", womean.GetType().Name, this.GetType().Name);
        }
    }

列舉Person:

namespace DesignModeDemo.visitor
{
    class PersonList
    {
        private IList<Person> persins = new List<Person>();

        public void attach(Person p)
        {
            persins.Add(p);
        }

        public void detach(Person p)
        {
            persins.Remove(p);
        }

        public void accept(Action visitor)
        {
            foreach (Person p in persins)
            {
                p.accept(visitor);
            }
        }
    }
}

客戶端程式碼:

static void Main(string[] args)
        {
            PersonList persons = new PersonList();

            persons.attach(new Man());
            persons.attach(new Womean());

            Success success = new Success();
            Fail fail = new Fail();
            Amativeness amativeness = new Amativeness();

            persons.accept(success);
            persons.accept(fail);
            persons.accept(amativeness);

            Console.Read();
        }

輸出:
這裡寫圖片描述

附上軟體開發的幾個原則:

依賴倒置原則

抽象不應該依賴於細節,細節應該依賴於抽象,即要針對介面程式設計,而不是對實現程式設計。

裡式代換原則

子類必須能夠替換掉它們的父型別,一個軟體實體如果使用的是一個父類的話,那麼一定適用於其子類,而且它察覺不出父類物件和子類物件的區別。也就是說,在軟體裡,把父類都替換成它的子類,程式的行為沒有變化。

迪米特法則

也叫最少知識原則,如果兩個類不必彼此直接通訊,那麼這兩個類就不應當發生直接的相互作用。如果其中一個類需要呼叫另一個類的某一個方法的話,可以通過第三者轉發這個呼叫。它的根本思想是強調了類之間的鬆耦合。類之間的耦合越弱,越有利於複用,一個處在弱耦合的類被修改,不會對有關係的類造成波及。

合成/聚合複用原則

儘量使用合成/聚合,儘量不使用類繼承。

相關文章