設計模式之 面向對象的養豬廠的故事,C#演示(二)

(三) 優先使用聚合,而不是繼承編程

有一段時間,養豬場的老闆僱用了清潔工人來打掃豬舍。但有一天,老闆突然對本身說"不對啊,既然我有機器人,爲何還要僱人來作這件事情?應該讓機器人來打掃宿舍!"
因而,這個需求被提交到了機器人的研發小組。看到這個需求,咱們敏感地意識到,這是一個潛藏了更多變化的需求,將來機器人的功能還可能會不斷增長,因而,咱們提取出了一個抽象的機器人接口,並實現了兩個具體的機器人類一-餵豬機器人和清潔機器人。系統的結構如圖V8-1所示。設計模式

 

                              圖V8-1this

 

這樣一來,老闆但願機器人工做時,能夠調用機器人接口的"工做"方法。因爲這也是針對接口編程,當老闆須要新的機器人時,只要添加具體的機器人類便可,其餘代碼無需變化。這彷佛己經解決了咱們遇到的問題。
但這裏還存在另一個問題:圖v8-1 中的繼承結構是在編譯期間就肯定了的,在運行期不能發生任何變化。所以,若是養豬場須要一個餵豬機器人和→個清潔機器人,那麼咱們必須在養豬場中放進這兩個具體的機器人。依此類推,若是將來養豬場還須要獸醫機器人、屠宰機器人等等,養豬場中不就擠滿了機器人嗎?更爲重要的是,每添加一種機器人的類型,主是們就必須改動代碼中的某一個地方,以便把這個機器人放進養豬場中,這就又會違反開閉原則了。在這種狀況下,使用聚合的機制能很好地解決問題,由於基於聚合的結構能夠在運行期間發生變化。
使用聚合機制的養豬場如圖v10-1 所示。咱們把機器人接口改爲了功能接口,而清潔功能和餵豬功能實現了這個功能接口。真正的機器人類中聚合了一個功能接口的引用,這樣,咱們只須要在養豬場中放進一個機器人,該機器人中聚合了一個餵豬功能,這時它是一個餵豬機器人。當咱們須要打掃養豬場時,老闆只須要調用機器人中的"變形"方法,並傳遞一個"清潔功能"對象給機器人,機器人就會像《變形金剛》中的"擎天柱"同樣,大吼一聲"汽車人,變形"就變成了-個清潔機器人了。spa

                                    圖v10-1設計

 

面向對象養豬廠V10版本實現代碼以下:3d

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Windows.Forms;
  9 
 10 namespace PigFactoryV10
 11 {
 12     /*
 13      *  豬悟能的博客
 14      * http://www.cnblogs.com/hackpig/
 15      * 
 16      有一段時間,老闆僱用清潔工人打掃豬廠,但有一天,老闆對本身說「我有了機器人,爲何還要僱用人打掃豬場?」
 17      因而,軟件團隊必須開發新的清潔機器人品種
 18      這裏,機器人的類型成了變化點
 19      
 20      下面的代碼使用了聚合方式,把機器人功能變成接口,只要操做員調用「變形」功能,並傳入一個「清潔功能」,機器人就會
 21      由餵豬機器人變身爲清潔機器人。
 22      若是將來要增長屠宰機器人,原有代碼也不用修改。
 23      
 24      這裏反映出設計模式的第三個核心設計原則:
 25      
 26      優先使用聚合而不是繼承。
 27      */
 28 
 29     public partial class Form1 : Form
 30     {
 31         public Form1()
 32         {
 33             InitializeComponent();
 34             btn_clean.Click += new EventHandler(btn_clean_Click);
 35             btn_feed.Click += new EventHandler(btn_feed_Click);
 36         }
 37 
 38         void btn_feed_Click(object sender, EventArgs e)
 39         {
 40             adminMan man1 = new adminMan(1, "大潤發養豬場");
 41             this.rtbMsgInfo.Text = man1.Msg;
 42         }
 43 
 44         void btn_clean_Click(object sender, EventArgs e)
 45         {
 46             adminMan man1 = new adminMan(2, "大潤發養豬場");
 47             this.rtbMsgInfo.Text = man1.Msg;
 48         }
 49     }
 50 
 51 
 52     public class adminMan:feedPigFactory
 53     {
 54         private string _msg;
 55 
 56         public string Msg
 57         {
 58             get { return _msg; }
 59             set { _msg = value; }
 60         }
 61 
 62         public adminMan(int funId,string factoryname)
 63         {
 64             base.FactoryName = factoryname;
 65             robot robot1 = null;
 66             switch (funId)
 67             {
 68                 case 1:     //餵食
 69                     robot1= new robot();
 70                     IList<Ipig> list1=new List<Ipig>();
 71                     list1.Add(new dbPig(1));
 72                     list1.Add(new dbPig(2));
 73                     list1.Add(new dbPig(3));
 74                     robot1.transformation(new feedPig(list1));
 75                     this._msg = robot1.work();
 76                     break;
 77                 case 2:     //清潔
 78                     robot1= new robot();
 79                     robot1.transformation(new clean());
 80                     this._msg= robot1.work();
 81                     break;
 82                 default:
 83                     break;
 84             }
 85         }
 86 
 87 
 88     }
 89 
 90 
 91     public interface IfunInterface
 92     {
 93         string work();
 94     }
 95 
 96     public class robot
 97     {
 98         private IfunInterface _robotFun;
 99 
100         public IfunInterface RobotFun
101         {
102             get { return _robotFun; }
103             set { _robotFun = value; }
104         }
105 
106         public void transformation(IfunInterface robotFun)
107         {
108             this._robotFun = robotFun;
109         }
110         public string work()
111         {
112            return this._robotFun.work();
113         }
114     }
115 
116 
117 
118 
119 
120     public class clean : IfunInterface
121     {
122         public string work()
123         {
124             return "正在打掃清潔..."+Environment.NewLine;
125         }
126     }
127 
128     public class feedPig : IfunInterface
129     {
130         IList<Ipig> pigList = new List<Ipig>();
131 
132         public feedPig(IList<Ipig> plist)
133         {
134             foreach (Ipig m in plist)
135                 pigList.Add(m);
136         }
137 
138         public feedPig()
139         {
140         }
141 
142         public void Attack(Ipig pig)
143         {
144             pigList.Add(pig);
145         }
146 
147         public string work()
148         {
149             string msgstr = string.Empty;
150             foreach (Ipig m in pigList)
151             {
152                 msgstr += m.eat() + Environment.NewLine;
153             }
154 
155             return string.Format("{0}{1}{2}",
156                 "大潤發養豬場" + Environment.NewLine,
157                 "餵豬機器人開始工做...." + Environment.NewLine + Environment.NewLine,
158                 msgstr);
159         }
160     }
161 
162 
163 
164     public abstract class feedPigFactory
165     {
166         private string _factoryName;
167 
168         public string FactoryName
169         {
170             get { return _factoryName; }
171             set { _factoryName = value; }
172         }
173 
174     }
175 
176 
177 
178     public interface Ipig
179     {
180 
181         int PigIndex
182         {
183             get;
184             set;
185         }
186 
187         string eat();
188 
189     }
190 
191 
192     public class cbPig : Ipig
193     {
194         private int _pigIndex;
195 
196         public int PigIndex
197         {
198             get { return _pigIndex; }
199             set { _pigIndex = value; }
200         }
201         public cbPig(int pignum)
202         {
203             this._pigIndex = pignum;
204         }
205 
206         public string eat()
207         {
208             return string.Format("{0}[{1}]開始吃.", "長白豬", _pigIndex);
209         }
210     }
211 
212 
213 
214     public class dbPig : Ipig
215     {
216         private int _pigIndex;
217 
218         public int PigIndex
219         {
220             get { return _pigIndex; }
221             set { _pigIndex = value; }
222         }
223         public dbPig(int pignum)
224         {
225             this._pigIndex = pignum;
226         }
227 
228         public string eat()
229         {
230             return string.Format("{0}[{1}]開始吃.", "大白豬", _pigIndex);
231         }
232     }
233 
234 
235 
236 }

 

運行結果如上圖所示, 老闆能夠下達指令在餵食和清潔機器人之間切換了.指針

 

代碼說明:code

 在這裏,餵豬機器人類是把原來直接調用Work() 餵食方法, 變成了先由transformation()指定功能類型, 再來執行Work().orm

public class robot
    {
        private IfunInterface _robotFun;

        public IfunInterface RobotFun
        {
            get { return _robotFun; }
            set { _robotFun = value; }
        }

        public void transformation(IfunInterface robotFun)
        {
            this._robotFun = robotFun;
        }
        public string work()
        {
           return this._robotFun.work();
        }
    }

而功能類型就是個IfunInterface接口, 而clearn(打掃清潔功能), feedPig(餵豬功能), 都是承繼這個接口的.對象

public interface IfunInterface
    {
        string work();
    }
public class clean : IfunInterface
public class feedPig : IfunInterface

最後利用工廠方法, 決定了機器人在餵食,仍是清潔兩種功能之間切換.

 public adminMan(int funId,string factoryname)
        {
            base.FactoryName = factoryname;
            robot robot1 = null;
            switch (funId)
            {
                case 1:     //餵食
                    robot1= new robot();
                    IList<Ipig> list1=new List<Ipig>();
                    list1.Add(new dbPig(1));
                    list1.Add(new dbPig(2));
                    list1.Add(new dbPig(3));
                    robot1.transformation(new feedPig(list1));
                    this._msg = robot1.work();
                    break;
                case 2:     //清潔
                    robot1= new robot();
                    robot1.transformation(new clean());
                    this._msg= robot1.work();
                    break;
                default:
                    break;
            }
        }

實際上咱們是聚合IfunInterface這個抽象接口,即經過指向接口類的引用來訪問對象, 這種實現方法實際上是綜合了聚合與繼承兩種機制的方式

 

此後,當咱們添加一個新的機器人種類(如獸醫機器人)時,只須要添加一個獸醫功能的派生類,老闆就能夠根據本身的須要,在任什麼時候刻命令機器人在三個種類之間隨意變形。能夠看出,添加一個機器人類型時,須要改動的代碼都在系統外部,系統內已有的代碼不須要發生變化。這裏的聚合機制使咱們很好地知足了開閉原則。

總之,繼承和聚合是兩種各不相同也各有優缺點的機制:

  • 繼承反映的是類之間"……是一個……"這樣的關係,它在編譯期間靜態定義。繼承的優勢是使用起來比較簡單(由於面向對象的語言直接支持繼承機制),對設計

人員來講比較容易理解。但繼承也有缺點:

首先,你不能在運行期間改變繼承樹的結構,由於繼承是在編譯期間定義的:

其次,基類中每每定義了部分的實現,基類的實現暴露給派生類後,繼承機制就會破壞數據和操做的封裝,使派生類對基類產生較強的依賴O

 

  • 聚合反映的是類之間"有-個……"或"……包含一個……"的關係,它是在運行期間動態定義的,所以,被聚合對象的類型能夠很容易地在運行期間發生變化,只要咱們保證它們的接口相同,知足徹底替換原則便可。並且,使用聚合能夠更好地封裝對象,使每個類集中在單個職能上,類的繼承層次也會保持較小的規模,不會形成類數量的爆炸。聚合的缺點是它並非面嚮對象語言直接支持的一個特性,用戶必須編寫一些代碼來完成聚合功能。例如,上面機器人類中的"工做"方法就必須把消息轉發給內部聚合的功能對象,即調用功能對象的"工做"方法。被聚合對象的接口必須聽從聚合類的要求,這種消息轉發的方式又被稱爲"委託( Delegation ) "。通常來講,聚合的結構比繼承更難理解一些。

從上面的分析能夠看出,聚合在某些方面比繼承更爲優越。但咱們強調聚合的做用毫不是否認繼承的優勢。使用聚合時,咱們必須遵循針對接口編程的設計原則,不能聚合某一個具體的派生類對象,而應該聚合該類的抽象接口,即經過指向接口類的引用或指針來訪問對象----這種實現方法實際上是綜合了聚合與繼承兩種機制的方式。


由此,咱們能夠總結出設計模式的第三個核心設計原則
繼承反映的是類之間的"……是一個…"的關係,聚合反映的是類之間"…有一個……"或包含一個……"的關係。在不違反這個關係前提下,應該
優先使用聚合而不是繼承, 同時,聚合也必須和接口及相關的繼承結構協同使用。

 

全文完.

 

本文源代碼下載

包括面向對象養豬廠的各類版本實現代碼(C#示例), 和VS2010繪製的UML類圖.

原創文章,出處 : http://www.cnblogs.com/hackpig/

相關文章
相關標籤/搜索