1. 程式人生 > >C#常用設計模式

C#常用設計模式

角色:使用者,工廠,產品.

目的是使得使用者將產品的消費和生產分開.在程式設計中就是將類的建立和使用分開.從而達到責任分離,其實這也是所有建立模式的目的之一.做法是工廠類根據使用者的要求(引數)來返回不同的類的例項.

工廠實現:採用引數化的靜態方法為使用者提供類例項的建立,如下所示:

public static ProductType FactoryMethod(引數)

{

     根據引數返回需要類的例項.

}

簡單工廠有幾種情況:

1.產品只有一種,這種情況下方法沒必要帶引數.

2.產品有多種,且屬於同類型產品,則可以加入抽象產品.加入抽象產品的一個潛在好處是可以讓使用者可以不關心具體的產品,在設計和實現上可以做到一定程度上的延後聯編.

3.產品有多種,且型別各異,則可以考慮採用其它模式或者提供多個工廠方法來實現.但不鼓勵採用後者來實現(做事要專業).

4.在實際應用中產品角色,工廠角色以及使用者角色都可以根據實際情況合併.(模式變形)

簡單工廠模式的理解:

1.如果產品建立的時候需要使用者提供指導,則簡單工廠模式不適合,如果要適用就會使得工廠方法的引數過於複雜,因此本模式只適合使用者不關心產品構造邏輯,只關心使用的情況下(不關心構建,只關心使用).

2.大多數情況下使用者還是必須知道產品的具體細節,沒有減少對細節的依賴(特殊情況,有抽象產品的情況下,在某些應用中可以減少對具體產品的細節依賴).

3.採用靜態方法實現類的建立,好處是可以減少工廠類本身的建立,但缺點是失去了工廠類進行繼承擴充套件的優點.

4.簡單工廠模式的好處是實現了責任分離,對使用者來講同時也隱藏了構建產品的細節,而且實現起來比較簡單.

5.增加產品的時候,工廠類需要修改,也違背了面向物件的開放封閉原則.

6.只適合產品類比較少的情況,如果產品類太多,會使得靜態工廠方法過於龐大.

但現在有一種比較簡單的辦法,就是採用反射或者泛型來規避靜態方法過多而使得工廠類龐大的問題.


從簡單工廠模式我們可以看出,在簡單工廠模式中,增加新的產品類,是需要修改工廠類的,這不符合開放-封閉原則.而且在寫工廠類的時候必須知道具體產品類的細節.也不符合由上相下的設計原則,如果要延後關注細節,則必須利用繼承或者介面的技術來實現.將簡單工廠模式中的工廠抽象,增加抽象工廠角色,這樣使用者,抽象工廠,抽象產品,使用者就形成了最頂層的設計層面,具體的實現由具體的工廠來建立具體的產品.著就是工廠方法模式.

工廠方法模式角色:使用者,抽象工廠,抽象產品,具體工廠,具體產品.

工廠方法模式採用一個具體工廠生產一種具體產品的方式進行產品的建立.使用者具體要建立那種產品的選擇邏輯推給了使用者,使用者通過選擇不同的具體工廠來實現不同產品的建立選擇.

工廠方法的優點:

1.符合開放封閉原則,因為增加新的產品不需要修改原來已有的工廠類,只需要增加相應的工廠即可.

2.將使用和建立的責任分開.

3.符合設計的先抽象後具體的順序.

缺點:

1.如果產品類太多會導致增加很多的類,會使系統顯得很不簡潔.

2.如果將選擇邏輯交給使用者,則在一定程度上增加了使用者對細節的依賴;

3.與簡單工廠模式相比,增加了工廠本身的建立開銷.

4.使用者需要建立工廠類本身.(通過一定的方法可以將工廠類的建立委託給抽象類去完成.見例子.

抽象工廠方法是工廠方法更一般的表達,工廠方法可以針對產品族(如控制元件的Windows族控制元件,Unix族控制元件),如果只有一族產品(比如只有Windows族)則就退化成簡單工廠方法(如果除掉抽象工廠,將工廠方法變為靜態的方法,利用引數化減少工廠方法數量為1個,則就是簡單工廠方法).)(一個工廠類,每個工廠方法建立一種產品).而如果只有一類產品(如只有Button控制元件),則退化成標準的工廠方法類似

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//變形的工廠方法
namespace MyBaseStudy
{

    abstract class AbstractFactory
    {
        //這種方法也需使用者建立工廠類。
        public static AbstractProduct CreateProductM1(AbstractFactory bb)
        {
            return bb.CreateAProduct();
        }
        //利用模板方法將工廠類的建立委託給抽象工廠本身。
        public static AbstractProduct CreateProductM2<T>() where T : AbstractFactory,new()
        {
            T factory = new T();
            return factory.CreateAProduct();
        }
        public abstract AbstractProduct CreateAProduct();
    }
    abstract class AbstractProduct
    {
        public abstract double GetPrice();
        public abstract void  AddPrice(double p);
    }

    class ConcreteFactoryA : AbstractFactory
    {
        public override AbstractProduct CreateAProduct()
        {
            return new ConcreteProductA(100); 
        }
    }

    class ConcreteFactoryB : AbstractFactory
    {
        public override AbstractProduct CreateAProduct()
        {
            return new ConcreteProductB(200,0.9);
        }
    }
    class ConcreteProductA : AbstractProduct
    {
        double _price;
        public ConcreteProductA(double price)
        {
            this._price = price;

        }
        public override double GetPrice()
        {
            return this._price;
        }
        public override void  AddPrice(double p)
        {
            this._price += p + 1;
        }
    }
    class ConcreteProductB : AbstractProduct
    {
         double _price;
         double _rate;
        public ConcreteProductB(double price,double rate)
        {
            this._price = price;
            this._rate = rate;
        }
        public override double GetPrice()
        {
            return this._price;
        }
        public override void  AddPrice(double p)
        {
            this._price += p * this._rate;
        }

    }

   class Client
   {
       public static void Test()
       {
           //方法1,一般的使用方法,使用者需要自己建立工廠類,要建立產品A

           AbstractFactory fa2 = new ConcreteFactoryA();
           AbstractProduct pa2 = fa2.CreateAProduct();
           System.Windows.Forms.MessageBox.Show(pa2.GetPrice().ToString());

           //方法2,一般的使用方法,使用者需要自己建立工廠類,要建立產品A,這種方法跟1比沒什麼優勢,
           //只是如果將CreateAProduct弄成保護型別,則可以隱藏建立細節,同時也可以起到一定代理模式的作用
           AbstractFactory fa1 = new ConcreteFactoryA();
           AbstractProduct pa1 = AbstractFactory.CreateProductM1(fa1);
           System.Windows.Forms.MessageBox.Show(pa1.GetPrice().ToString());

           //方法3,使用泛型方法方法,使用者不需要自己建立工廠類,建立產品B,但這種方法的侷限是工廠類必須具有不需要引數化的建構函式。
           AbstractProduct pb = AbstractFactory.CreateProductM2<ConcreteFactoryB>();
           System.Windows.Forms.MessageBox.Show(pb.GetPrice().ToString());

       }
   }
}
工廠方法和簡單工廠一樣,如果產品類太多都會導致具體的工廠類或方法過多,改進的辦法是利用泛型,當然也可以利用反射,而且利用反射還可以進行引數化構造,但不利的地方是這同樣會導致使用者對構造細節的依賴.hibernate的構造方式其實就是這種方式,他將使用者對構造細節的依賴轉換成了可以動態配置的檔案形式,而不是直接依賴.採用這種方式還有的好處就是工廠可以創造多種產品.但這種方式的一個缺點是需要採用動態引數構造目標產品的時候,這種方式就很難辦到了.


建立模式的目的就是為了解決使用和生產責任分離,當然能使用這種模式的前提就是使用者並不關心"產品"的建立邏輯和過程,使用者只需要對建立的結果進行使用.在這種情況下,將使用和建立分離就增加了程式的靈活性,因為"產品"的構建對使用者來說是透明的,如果需要對建立過程增加功能(比如增加建立許可權檢查),不會影響到使用者的使用.

       模式的應用有兩個基本的目的,一是複用,二是適應變化.而且這兩個目的在很大程度上也是統一 的.工廠類模式將使用和建立責任分離,其實就是為了適應"建立"的變化.當然這種"適應變化"的代價就是需要增加工廠類,增加了系統的複雜度和開銷.因此選擇模式的一個重要的工作就是要權衡利弊,其實在建立模式中就有一個隱形假設:工廠角色的變化相對固定,工廠類本身的建立非常簡單.這個假設很重要,因為如果工廠本身的建立和可變性都很大的話,採用這種模式就得不償失了.

      簡單工廠模式的原理其實很簡單,就是將"產品"的建立工作委託給工廠類的靜態方法來完成,有抽象產品的情況下,使用者只需要用一個引數呼叫來獲得"產品",而不需要知道具體的產品的細節,比如,抽象產品為水果,使用者如果需要獲取產品"蘋果",則只要將"蘋果"作為引數來呼叫水果工廠的建立水果的方法,而不需要知道蘋果類本身的細節.這是簡單工廠模式的好處.跟工廠方法相比,簡單工廠模式邏輯簡單,額外開銷小(增加產品,不需要增加工廠類,因為是靜態方法,工廠類本身也不需要建立等).

     工廠方法模式的原理也是將"產品"的建立委託給工廠類進行,但由於工廠類的建立方法不是靜態的,因此可以利用繼承的好處,將具體的建立工作"延時"到子類進行.這種方式在設計和思考上是有非常大的好處的:將設計分層,每一層都只需要關心同一層面上的事情.工廠方法的做法是每個具體產品都用一個具體的工廠來進行建立.這樣做的好處是符合面向物件程式設計的開放封閉原則,增加新的產品,只需要增加新的工廠類即可(對擴充套件開放),對任何產品的增加或修改,不會影響其它工廠類和產品類(對修改封閉),但缺點也是非常明顯的,就是產品多的時候,系統中的類會很多.(成倍增加).跟簡單工廠模式相比,一是系統不簡潔,二是增加了工廠類的建立,三是使用者的選擇產品邏輯變成了對工廠類的選擇,降低了選擇的直觀性.

    抽象工廠方法可以看作是工廠方法的更一般的表達,抽象工廠針對的是產品族的概念(如果只有一種產品,就是工廠模式),如果按照抽象工廠的嚴格意義上來講,應用場景會比較少(多種資料庫支援,多種語言版本的情況可以用).能使用抽象工廠的應用場景一般可以表達為矩陣模型,同一行的是不同環境中的同一個產品的實現,同一列的產品都處於同一個應用環境下,系統每次只會用到一種環境的產品.典型的應用是Windows和Linux下的介面控制元件.如果只一族產品,則抽象工廠就可以退化成簡單工廠來實現.

     在實際使用中,簡單工廠模式和工廠方法模式用得比較多,而抽象工廠方法用得比較少.當然,抽象工廠在類似於資料庫連線方面應用還是比較好的,比如系統如果需要支撐多種資料庫系統,就可以採用抽象工廠來進行構建.

===============================

其實類的建立現在有了一些新的變化,例如,將建立依賴放在配置檔案,採用反射技術來構造例項,這種方法的好處是一時可以減少工廠類,極端的情況下一個工廠方法就可以了,同時工廠類與產品類也脫耦。現在有很多框架都採用這種方式(如Nhibernate等),就是所謂的依賴注入,如下所示:

   public interface IPersonDao
    {
        int SaveData();
    }

    public class PersonDao : IPersonDao
    {
    #region IPersonDao 成員

        public int SaveData()
        {
          return 100;  
        }
        
        public PersonDao()
        {
        }
   #endregion
    }
    public class ObjectFactory
    {
        private static Dictionary<string, string> _objects = null;
        static ObjectFactory()
        {
            string theFile = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "MyObjects.xml";
            XElement root = XElement.Load(theFile);
            var objs = from obj in root.Elements("object")
                       select obj;
            _objects = objs.ToDictionary(k => k.Attribute("id").Value, v => v.Attribute("value").Value);
        }
        public static TRetObj GetObject<TRetObj>(string objectcode)
        {
            string theClassType = _objects[objectcode];
           
            Type theType = Type.GetType(theClassType, false, true);
            return (TRetObj)Activator.CreateInstance(theType);
        }
    }

MyObjects.xml

<?xml version="1.0" encoding="utf-8" ?>
<objects>
  <object id="PersonDao" value="SpringNetStudy.PersonDao,SpringNetStudy"/>
</objects>

BC:20120528
依賴配置的建立方式,本質上可以看作是簡單工廠或者工廠方法,但這種方式的侷限性也非常大:將使用者選擇產品的邏輯放在了配置檔案裡,一是配置檔案的表達能力會比較弱,如果選擇邏輯比較複雜的情況下,就無法適用.二是選擇是在配置檔案中,導致邏輯相對靜態,無法實現動態的選擇邏輯,這個侷限根本原因也在於一.依賴配置建立的邏輯適合哪些建立邏輯簡單,使用者選擇產品邏輯相對靜態的,但需建立產品的數量比較大的情況下.


單例模式

單例模式的目的是保證類在系統中只被例項化一次,由該唯一的例項來為系統提供服務.單例模式主要用於保證服務的統一,比如獲取統一的編號服務,模仿Oracle的序列生成等.但單例的使用需要謹慎,特別是在需要作負載均衡的地方,因為這種程式級的單例模式實際上只能保證在一個應用中為單例.如果被多個應用載入,還是會被多次例項化.

     同時如果採用懶漢式還需要注意執行緒安全.

     多例模式跟單例模式類似,但在實現上要複雜很多,需要維護自己的例項池和分配使用策略,還包括使用狀態等的維護,多例模式主要用於有限個資源的管理,

比如資料庫連線池等。

下面是單例模式例子:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
//單例模式:就是保證系統中只例項化一個例項。
namespace MyBaseStudy
{
    //模式1
    class SingleTon_1
    {
        private double _value = 0;
        public double Value
        {
            get
            {
                return _value;
            }
            set
            {
                this._value = value;
            }
        }
        private static SingleTon_1 _instance = new SingleTon_1();
        //在單例模式中需要將建構函式設為Protected或者private,防止其它類來使用建構函式進行例項化。
        private SingleTon_1()
        {
        }
        public static SingleTon_1 GetInstance()
        {
            return _instance;
        }
    }

    //模式2 稱為懶漢式,需要保證執行緒安全
    class SingleTon_2
    {
        private double _value = 0;
        public double Value
        {
            get
            {
                return _value;
            }
            set
            {
                this._value = value;
            }
        }
        private static SingleTon_2 _instance = null;
        private static readonly object padlock = new object();
        //在單例模式中需要將建構函式設為Protected或者private,防止其它類來使用建構函式進行例項化。
        private SingleTon_2()
        {
             
        }
        //這種縮開銷比較大,因為每次進來都需要鎖。
        public static  SingleTon_2 GetInstanceA()
        {
            lock (padlock)
            {
                if (_instance == null)
                {
                    _instance = new SingleTon_2();
                }
                return _instance;
            }
        }
        //只有在沒有例項化的情況下才加鎖。
        public static SingleTon_2 GetInstanceB()
        {
            if (_instance == null)
            {
                lock (padlock)
                {
                    if (_instance == null)
                    {
                        _instance = new SingleTon_2();
                    }
                    
                }
            }
            return _instance;
        }
    }

    //一般類
    class CommClass
    {
        private double _value = 0;
        public double Value
        {
            get
            {
                return _value;
            }
            set
            {
                this._value = value;
            }
        }
    }
    //利用泛型來實現單例類的構建,好處是要使用單例模式的類本身可以是普通類,可用於複用。
    public sealed class SingleTon_3<T> where T : new()
    {
        
        public static T GetInstance()
        {
           
            return SingleTonCreator.Instance;
            
        }
        private class SingleTonCreator
        {
            internal static readonly T Instance = new T();
            static SingleTonCreator()
            {
            }

        }
    }

    public class SingleTonTest
    {
        public static void test()
        {
            SingleTon_1.GetInstance().Value++;
            MessageBox.Show(SingleTon_1.GetInstance().Value.ToString());

            SingleTon_1.GetInstance().Value++;
            MessageBox.Show(SingleTon_1.GetInstance().Value.ToString());

            SingleTon_2.GetInstanceA().Value++;
            MessageBox.Show(SingleTon_2.GetInstanceA().Value.ToString());

            SingleTon_2.GetInstanceA().Value++;
            MessageBox.Show(SingleTon_2.GetInstanceA().Value.ToString());

            SingleTon_2.GetInstanceB().Value++;
            MessageBox.Show(SingleTon_2.GetInstanceB().Value.ToString());

            SingleTon_2.GetInstanceB().Value++;
            MessageBox.Show(SingleTon_2.GetInstanceB().Value.ToString());

            SingleTon_3<CommClass>.GetInstance().Value++;

            MessageBox.Show(SingleTon_3<CommClass>.GetInstance().Value.ToString());

            SingleTon_3<CommClass>.GetInstance().Value++;

            MessageBox.Show(SingleTon_3<CommClass>.GetInstance().Value.ToString());
        }
    }
}


生成器模式

在產品結構比較複雜,構造過程比較繁瑣,一次性構造比較難的時候,我們可以採取分而治之的原則,將產品元件化,每個元件由專門的廠商來生產,最後的產品指派給制定的車間進行最後裝配.這種方式其實是現代製造業的一種典型的模式.比如汽車,飛機的製造等.這樣做的好處是:

    1.產品的部件由專門的生產廠商來生產,這樣分工更加明確,更加精細,生產專業化,可以降低成本;

    2.整個產品的生產更加簡單,也可增加零件的複用.

    3.轉換生產比較容易,比如產品的材質發生變化,只需要找相應提供該材質的廠商即可.

這其實就是典型的生成器模式.生成器模式有4個角色:

A.抽象生成器:提供生成器的抽象表達,具體的生成由子類完成.抽象生成器可保持一個產品的引用;

B.具體生成器:每個具體的生成器提供一個對產品各個部分的構造方法.同時提供一個對最終產品進行訪問的方法.

C.產品:需要構造的產品,該產品可以元件化來生產;

D.構造指導者:其實就是產品裝配車間,構造過程和順序都是固定的.

生成器模式的好處:

1.同其它建立型模式一樣實現了產品的使用和建立的責任分離;

2.延遲了產品的構建過程;

3.可以通過指定不同的生成器來生成不同形式的產品;

4.將產品的構造元件化,降低了產品的生產和管理的難度.

抽象模式也可以完成上述功能,但抽象模式並不為專門是來構造複雜物件的.生成器模式更加關注一種產品本身的構造過程.

================================================================================

生成器模式一個比較典型而非常有用的應用就是構造sql語句,特別做一些動態查詢業務的時候非常有用。比如:sql語句的構造可分為select部分構造,where條件構造,order by,group by 構造等部分。最後簡單連線起來就形成完整的sql語句.


從形式上來講,通過角色合併,方法功能的轉變,抽象工廠可以和生成器模式形式上取得一致(比如抽象工廠只處理一個產品族,工廠方法都處理同一個產品)。但注意,這僅僅是形式上的,實際上,抽象工廠和生成器模式有著本質的區別:

1、生成器模式是為了構造一個複雜的產品,而且購造這個產品遵循一定的規則(相同的過程),而抽象工廠則是為了建立成族的產品(系列產品),同族產品的構造在邏輯上並不存在必然的聯絡(唯一必然的聯絡就是大家都屬於一族)。

2、生成器模式的構造方法是為了構造同一個產品,因此必須有指導者來協調進行工作,構造方法之間存在必然的業務聯絡,而抽象工廠的構造方法都是獨立去構建自己的產品物件,因此他們不存在必然的聯絡。在生成器模式中客戶端不直接呼叫構建產品部分的方法來獲取最終產品,而抽象工廠中客戶端是通過呼叫不同的工廠方法獲取不同的產品。

3.在生成器模式中,那些用來構造產品不同部分的方法一般都實現為Protected形式,以防止客戶端通過呼叫這種方法活得不可預料的結果,而抽象工廠中的這些方法必須為Public形式。否則客戶無法呼叫來獲得產品結果;

4.生成器模式的角色有生成器,產品和指導者,而抽象工廠的角色有工廠和產品。無論角色和功能怎樣變換,但所含的業務邏輯角色都應該存在,這也是兩個模式的業務本質。


原型模式

原型提供了另外一種建立物件的方式:通過已有的物件的自身提供的克隆功能來建立新的物件。

這樣做的好處很明顯:

1.使用克隆來建立物件的使用者不需要知道物件的構造細節;

2.效能上有利。

但採用克隆來生成物件也要注意需要克隆的內容,如果採用全複製的話,有些業務上可能會產生邏輯錯誤,比如物件ID衝突等。

克隆分為深度克隆和淺度克隆。一般來講使用淺度克隆比較好,不會使得業務邏輯變得很複雜。

===============================================================

在實際開發中,對於實體類提供克隆方法有很多好處,比如,有的時候我們需要將獲取的一系列實體修改部分關鍵欄位,,然後插入到資料庫中,如果當前獲取的實體又不想改變,那麼採用實體克隆再插入就非常方便.


橋模式

顧名思義,橋的作用就是將兩條相互獨立的路橋接到一起,而兩條路可以保持各自的相對獨立。在程式設計中,“橋”模式中的橋只是一種形象的比喻。根據橋模式的定義,橋模式實現了一個主題(可以是一個類,也可以是一中設計上的概念)的抽象部分和實現部分的分離(有的地方定義為抽象與具體行為的分離)。抽象部分的基類和實現部分的基類充就當著橋(介面對接)的作用,而抽象部分的子類和實現部分的子類就是橋兩邊的路,可以各自相對獨立的發展(變化)(之所以是相對獨立,從實現上來講就是這種變化不能脫離最終能夠實現橋接這個目標)。   

 我們知道,對於物件的研究,我們可以用這樣的模型來表達物件:屬性和行為。行為和屬性都是物件的特徵,其中屬性描述物件的靜態特徵,而行為表達物件的動態特徵,一般來講,動態特徵往往跟靜態特徵密切相關,相互影響,靜態特徵會約束動態特徵的(行為),比如一個人如果是小孩(屬性),那麼他的行為很多都是受約束的,反過來,如果一個人經過吃(行為),又會長大,影響到身高,體重等(屬性)。因為物件的特徵都會發生變化,如果我們要表達這種變化,一是需要對這些物件根據我們的實際需要進行抽象,提取需要的特徵,而對那些次要或者不需要的特徵進行剔除。但這僅僅是研究物件的基礎--抽象,我們還是需要表達重要特徵的變化。

對於靜態特徵而言,變化的表達比較簡單,只需要修改特徵值即可,而且在實現上也比較容易,只要不同層次的物件都保持自己的特徵值即可。但對於行為特徵就不一樣了,因為行為特徵不僅受到靜態特徵這個約束,還會受到環境的影響。要用分類模式來表達所有靜態特徵和行為特徵的變化是很困難的,我們只考察一種靜態特徵和一種行為特徵的變化,這是最基本的一種情況,考慮到現有的技術體系,我們有兩種方法來實現:

   A)利用類的單繼承來實現(形成分類層次),比如人是抽象類,有個重要的特徵性別,一個重要的行為跑,性別不同跑的行為當然也不一樣,利用繼承,我們可以派生出兩個子類——男人和女人。這樣行為跑也可以分為男人的跑和女人的跑,當然,如果僅僅是研究跑與性別的關係,問題是可以解決。但前面說了,行為除了受到靜態特徵的約束外,還會受到環境的影響,比如在不同產地的跑。這種情況下,如果還按照繼承分類的方式去做,顯然是不可取的,一是類體系太複雜,二是用環境來分類物件顯然也不是一種可取的方式,因為環境是外在的。

   B)通過類的多繼承來實現,對於多個特徵的變化,這種方法可以減少類的數量,對於上述的例子,我們可以用一個物件的抽象和環境(比如產地)的抽象,人可以分為男人,女人,環境可以分為沙地,塑料跑道等。要描述男人在沙地跑,我們就從沙地和男人繼承形成一個新的子類。這種方式看起來是可以,但有很多問題,一是不太符合物件分類原則(研究物件的分類一般都是根據物件本身,而不是會根據環境),在類體系上物件與環境天然就應該分離。二是從技術實現上來講也比較麻煩,也違背了責任單一原則。

   那怎麼來解決這種問題呢?我們來分析一下類的靜態特徵和動態特徵,我們發現,對於靜態特徵的變化我們大多時候並不需要提供派生類來實現,只需要通過修改設定靜態特徵資料就可以了,只有那些對行為影響非常大,又屬於我們主要研究範疇的靜態特徵,我們才需要按此屬性進行派生,(比如,性別),而行為特徵的變化,除了受到靜態特徵的影響外,還受到外界環境的影響,同時也會影響外界環境,比如人的跑這個行為在不同場地上跑是不一樣的(比如沙地,水泥地),如果我們也採用繼承的方式來實現這種變化,一是上面說的弊端,二從邏輯上我們也很難接受根據行為的環境來進行類派生的做法(雖然技術上是可以的),很難將一個跑在沙地和跑在水泥地人視為不同的類(人),但我們可以將場地進行抽象,將人在某個產地跑的行為委託給產地來實現,我們在人這個類中保持一個對場地的引用,而在跑的方法中除了實現自己必要的邏輯外,可以呼叫場地中的跑來具體實現跑的行為。因為雙方各自可以採用繼承的方式實現自己的變化,我們就可以在實際使用時臨時指派具體場地。這樣既減少了類的數量,又使得邏輯更加簡單和靈活。下面是一個簡單的例子類圖:

從上面的例子我們可以看出,增加一個人的子類,或者增加一個產地,原來的結構都不需要動,符合設計的開閉原則。

總結如下:

A、橋模式實現了類中行為抽象與行為實現的分離,同時可以實現類本身和行為實現的各自發展。理論上對於任何特徵都可以通過這種委託來實現抽象與實現的分離,但我覺得不推薦這種氾濫的應用,對於主要受靜態特徵影響而變化的行為我們可以通過繼承來實現這種變化,只對受外在環境影響比較大的行為特徵才使用這種模式來實現。

B.對於橋模式中抽象類保持一個對實現類的引用並不是必要的,對於上述例子,完全可以在通過Run的引數化來完成這種組合,這樣人與產地的關係就可以從關聯關係弱化到簡單依賴。

C.橋模式提供了一種減少類的方法,為類沿著多維特徵各自變化的情況提供了一種解決方法。

後記:橋模式可以實現系統在多個維度上的獨立變化,這本身就要求系統在這些維度上的變化能夠獨立,如果這些維度相互之間存在著關聯或者依賴,就會使得體系中關係過於複雜,相互之間的通訊太多,甚至不能實現,比如人的行為跑,呼吸,說話,這幾種行為相互影響有聯絡,就很難使用橋模式來實現,而在這種情況下就需要用建立數學模型的方式來進行。 


策略模式

如果一個類(CA)的方法存在不同的演算法(F),而這些演算法使用者都可能用到,那麼就可以將這種演算法獨立出來自我演變,既提供一個演算法的抽象介面,再定義具體的演算法類,而CA保持對演算法的一個引用,CA中的方法不直接實現演算法,而是呼叫使用者所選的演算法類中的演算法來實現,這就是策略模式。也既定義一系列的演算法,把它們一個個封裝起來, 並且使它們可相互替換。本模式使得演算法可獨立於使用它的客戶而變化。

  *
  *                   | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|                                                  | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
  *                   |    Context                   |                                                  |    Strategy                 |
  *                   |-----------------------------|◇-strategy---聚合------------→|-----------------------------|
  *                   |    Request                  |                                                  |   Handle()                  |
  *                   |________________|                                                   |________________|
  *                                                                                                                            △
  *                                                                                                          |---------------|------------|
  *                                                                                                          |                                   |
  *                                                                     | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|           | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
  *                                                                     |  ConcreateStrategyA           |           |  ConcreateStrategyB      | 
  *                                                                     |---------------------------------- ----|           |-----------------------------------|
  *                                                                     |    Handle()                              |           |  Handle()                           |
  *                                                                     |_________________  ____|           |___________________|
  *                                                                      
  * 
    從上面UML圖可以看出,從結構上來講,策略模式跟橋模式很像,橋模式中抽象部分本身如果沒有子類,那麼橋模式就退化成了策略模式,相反如果策略模式中的Context類也可以獨立演化,策略模式就進化成了橋模式。但從業務邏輯上來講,這兩個模式的應用目的是不一樣的:橋模式側重類本身和類行為實現部分各自獨立發展,而策略模式則側重不同演算法的封裝與支援。(其實很多模式之間的外觀差異性很小,而且可以起到的作用也存在相似,這也是模式實際應用中很難區分到底是那個模式的原因)

    採用策略模式的時候需要注意幾點:一是策略演算法的實現要相對獨立,既不依賴或者少依賴策略呼叫者(本圖中的Context 類;二是不同策略之間要相互獨立,且能相互替代。這點很關鍵,如果不同的策略不能相互替代,就會造成呼叫者呼叫的不確定性(在這點上,橋模式不要求這點)。

    另外如果策略演算法依賴於呼叫者,則應該使用引數,而不要過多的暴露呼叫者本身的資料給策略類,因為暴露呼叫者的結構會造成策略類對呼叫者類的依賴,而相互依賴則增加了系統的耦合度。

    策略類的建立和Contex類的建立可使用其它建立型模式來完成。

    策略模式中的策略演算法可能只是Context類中某個方法中的一個部分,在將策略演算法中依賴的外界引數進行引數化後(由抽象策略提供標準的介面),策略演算法就可以獨立出來成為系統中的公共演算法。

    策略模式從某種意義上來見,側重於演算法,但實際上策略模式可以應用於各個方面,比較極端的例子是使用委託來實現演算法策略,這種方式更為靈活,一個比較典型的例子就是排序函式的比較策略,一般情況下實現幾種預設的比較演算法,但因為比較策略演算法實在是太多,則可以通過委託呼叫來讓使用者自己去實現。這種方式的擴充套件性和靈活性都比較好。C#的查詢擴充套件方法基本都是採用這種方式實現。而匿名函式和蘭姆達表示式的引入,也使得這種策略實現的方便性大大爭強.

   當然,利用委託充當策略抽象來實現策略演算法最好用於那些多且不確定但演算法簡單的情況,對於演算法複雜,演算法比較固定的方式還是建議使用上述模式的標準方式。


裝飾模式

裝飾的含義就是在原有的物件上增加飾品,使得物件能呈現出不同的特徵。當然在類設計中,採用裝飾模式,是用來為類增加新的特徵和行為。結構如下:

    *   
  *                   | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|                                
  *                   |     Component           |<-------------------------------------------------------------------|  
  *                   |----------------------- -----|                                                                                       |
  *                   | Operation()               |                                                                                       |                      
  *                   |________________|                                                                                        |
  *                                   △                                                                                                        |
  *                                     |                                                                                                          |
  *                     | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|                                                 |     
  * | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|           | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|◇-Component------|
  * |  ConcreateComponent       |           |     Decorator                          |
  * |--------------------------------------|           |---------------------------------------|      | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
  * |   Operation()                         |           |    Operation()  ○---------------|--→| Component-> Operation()  |
  * |_________________  ___|           |______________________|      |_____________________| 
  *                                                                                      △                                             
  *                                                                                       |                                             
  *                                                     | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|                                
  *                      | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|           | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
  *                      |  ConcreateDecoratorA       |           | ConcreateDecoratorB         |
  *                      |--------------------------------------|           |---------------------------------------|      | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
  *                      |   Operation()                         |           |    Operation()     ○-------------|-→| Decorator:: Operation()      |
  *                      |   addedState                        |           |   AddedBehavior()                |      | AddedBehavior()                | 
  *                      |_____________________|           |______________________|      |_________________ ___|
  * 
角色:

      抽象構件(Component):給出一個抽象介面,以規範準備接收附加責任的物件。
      具體構件(Concrete Component):定義一個將要接收附加責任的類。
      裝飾(Decorator):持有一個構件物件的例項,並定義一個與抽象構件介面一致的介面。
      具體裝飾(Concrete Decorator):負責給構件物件“貼上”附加的責任。

相關推薦

C++ 常用設計模式(學習筆記)

1、工廠模式:簡單工廠模式、工廠方法模式、抽象工廠模式  1)、簡單工廠模式:主要特點是需要在工廠類中做判斷,從而創造相應的產品,當增加新產品時,需要修改工廠類。typedef enum { T80 = 1, T99 }TankType; class Tan

Objective-C常用設計模式(一)--工廠方法模式

工廠方法模式:定義建立物件的介面,讓子類決定例項化哪一個類。工廠方法使得一個類的例項化延遲到其子類。幾乎所有面向物件語言寫的應用程式都能看到工廠方法。“專門定義一個類來負責建立其他類的例項,被建立的例項通常具有共同的父類。”工廠方法模式使用場景:編譯時無法準確預期要建立的物件

C#常用設計模式

角色:使用者,工廠,產品. 目的是使得使用者將產品的消費和生產分開.在程式設計中就是將類的建立和使用分開.從而達到責任分離,其實這也是所有建立模式的目的之一.做法是工廠類根據使用者的要求(引數)來返回不同的類的例項. 工廠實現:採用引數化的靜態方法

C#之常用設計模式(unity版本)

                                          &nb

23種常用設計模式C++)

Part One: Methods for constrcting a new object: 1. Factory method 我們把簡單工廠方法歸類到工廠方法中。工廠方法的目的是用來解決具有同一介面(基類)派生類物件的生成問題。儘管可以通過類的建構函式生成物件,

PHP常用設計模式匯總

設計模式 strac == 抽象 try 抽象類 設計 eal decorator 裝飾模式: <?php abstract class Tile { abstract function getWealthFactor(); } class Plains ext

C#.Net 設計模式學習筆記之創建型 (一)

應用 種類 單件 src nag abstract 子類 指定 相關 1、抽象工廠(Abstract Factory)模式 常規的對象創建方法: //創建一個Road對象 Road road =new Road(); new 的問題: 實現依賴,不能應對“具

JAVA常用設計模式

ipp targe aec atd jmh 設計模式 模式 java left 巴揭殼景枚嫡倚濫煽吠徑http://www.docin.com/vxuo069 蔚贍卣碩狼吠亢照某嶽魏徹http://www.docin.com/idj90593 黃濁偃由友兔兆凍寺敬踴酌沮持h

常用設計模式:適配器模式

targe apt aio 什麽是 兼容 概念 sdn void mage   相信適配器這個概念對於大夥來說都不陌生了吧?曾經買過港貨的通過都知道,港貨當中的充電器插線頭是很蛋疼的三插口的,但是我們平常的充電插座,一般都是兩插口的,那麽要解決這個問題,我們的適配器就登場了

常用設計模式之單例模式

serial 修改 反射 類型 tile 應用場景 ray 例子 flag 單例對象(Singleton)是一種常用的設計模式。在Java應用中,單例對象能保證在一個JVM中,該對象只 有一個實例存在。這樣的模式有幾個好處: 1、某些類創建比較頻繁,對於一些大型的對象,這是

常用設計模式之門面模式

facade int 子模塊 外觀模式 code 原來 外觀 設計模式 同方 門面模式也可以叫做外觀模式 為子系統或者模塊中的一組接口提供一個一致的訪問方式,此模式定義了一個高層接口,這個接口使得各個子系統/模塊中的功能 更加容易使用。 實際應用中,我們在對付一些老舊的代碼

C#模板設計模式使用和學習心得

傳統 oracle 設計模式 管理系統 邏輯 導致 www. 離開 不回 模板設計模式: 模版方法模式由一個抽象類和一個(或一組)實現類通過繼承結構組成,抽象類中的方法分為三種: 抽象方法:父類中只聲明但不加以實現,而是定義好規範,然後由它的子類去實現。

微信h5棋牌平臺搭建JS常用設計模式

nod .proto settime 工廠 修改 aca 相互 存儲 tel 微信h5棋牌平臺搭建(h5.ttkmwl.com QQ:2172243813)JS常用設計模式大型單頁應用裏,復雜度上升到一定程度時,沒有適當的設計模式進行降耦,後續的開發也難以下手。而設計模式正

算法常用設計模式(子博客)

最好 利用 步驟 ron 進行 加速 刪除 計算機 分析 1. 枚舉法   根據具體問題枚舉出各種可能,從中選出有用信息或者問題的解。   這種方法利用計算機的速度優勢,在解決簡單問題時十分有效。 2. 貪心法   如前所述,根據問題的信息盡可能做出部分的解,並基於部分

JavaScript常用設計模式

OS names his 方法 end nta imp androi 關系 單例模式:確保類只能被實例化一次。 var obj = {} 2、函數返回值 var func = function () {return {}} var obj = func();

Java常用設計模式——觀察者模式

ray stat param servers face oid println override 角色 觀察者模式又叫做發布-訂閱-模式、模型-視圖-模式、源-監聽器-模式或者從屬者模式。觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對

Java常用設計模式——策略模式

nts static 管理 選擇 span disco rri contex 由於 策略模式定義了一系列的算法,並將每一個算法封裝起來,而且使它們還可以相互替換,策略模式讓算法獨立於使用它的客戶而獨立變化。策略模式使這些算法在客戶端調用它們的時候能夠互不影響地變化

啃食c++(簡單設計模式)

簡單工廠模式 該設計模式是多型的一種應用例項,簡單工廠模式是有一個生產物件決定生產那一產品,例如接下來的例子中,將friut作為父類,其子類就是各種水果,在這裡就建立了兩個子類引入,然後將各種水果物件作為生產者garden的私有資料成員,通過出生產者來獲取想要的水果型別。 #includ

java常用設計模式--觀察者模式簡單例子

package com.ruanyun;import java.util.List;import java.util.Vector;/** * @Auther: maxw * @Date: 2018/11/10 16:14 * @Description:觀察者模式 * 基本概念: * 觀察者模式屬於行為型模式

java常用設計模式--單例模式簡單例子

package com.ruanyun;/** * @Auther: maxw * @Date: 2018/11/10 17:29 * @Description: */public class Test4 { public static void main(String args[]){