一、基礎概念
????????橋接模式的本質是【分離抽象和實現】。
????????橋接模式的定義:將抽象部分與它的實現部分分離,使它們都可以獨立地變化。? ??
序號 | 認識橋接模式 | 說明 |
1 | 什么是橋接 | 通俗點說就是在不同的東西之間搭一個橋,讓它們能夠連接起來,可以相互通訊和使用。在橋接模式中是給什么東西搭橋呢?【是為被分離了的抽象部分和實現部分來搭橋】 ?????????注意:在橋接模式中橋接是單向的,也就是只能是抽象部分的對象去使用具體實現部分的對象,而不能反過來,這就是單向橋。 |
2 | 為何需要橋接 | 為了達到讓抽象部分和實現部分都可以獨立變化的目的,在橋接模式中,是把抽象部分和實現部分分離開來。 ????????雖然從程序結構上是分開了,但是抽象部分實現的時候,還是需要使用具體的實現,這可怎么辦?【抽象部分如何才能調用到具體實現部分的功能呢?】搭個橋就可以了,讓抽象部分通過這個橋就可以調用到實現部分的功能了,因此需要橋接。 |
3 | 如何橋接 | 只要讓抽象部分擁有實現部分的接口對象,就橋接上了,在抽象部分即可通過這個接口來調用具體實現部分的功能(即:橋接在程序上體現了在抽象部分擁有實現部分的接口對象,維護橋接就是維護這個關系)。 |
4 | 獨立變化 | 橋接模式的意圖是使得抽象和實現可以獨立變化,都可以分別擴充。也就是說抽象部分和實現部分是一種非常松散的關系。從某個角度來講,抽象部分和實現部分是可以完全分開的,獨立的,抽象部分不過是一個使用實現部分對外接口的程序罷了。 ?????????如果這么看橋接模式的話,就類似于策略模式了。抽象部分需要根據某個策略,來選擇真實的實現,也就是說橋接模式的抽象部分相當于策略模式的上下文,更原始的就直接類似于面向接口編程,通過接口分離的兩個部分而已。但是別忘了,橋接模式的抽象部分,是可以繼續擴展和變化的,而策略模式只有上下文,是不存在所謂抽象部分的。 ????????抽象和實現為什么還要組合在一起呢?原因是在抽象部分和實現部分還是存在內部聯系的,抽象部分的實現通常是需要調用實現部分的功能來實現的。 |
5 | 動態變換功能 | 由于橋接模式中的抽象部分和實現部分是完全分離的,因此可以在運行時動態組合具體的真實實現,從而達到動態變換功能的目的。 ????????從另外一個角度看,抽象部分和實現部分沒有固定的綁定關系,因此同一個真實實現可以被不同的抽象對象使用;反過來,同一個抽象也可以有多個不同的實現。 |
6 | 退化的橋接模式 | 如果接口僅有一個實現,那么就沒有必要創建接口了,這是一種橋接模式退化的情況(即:抽象類和接口是一對一的關系,雖然如此,但還是要保持它們的分離狀態,這樣,它們才不會相互影響,才可以分別擴展) |
7 | 橋接模式和繼承 | 繼承是擴展對象功能的一種常見手段,通常情況下,繼承擴展的功能變化緯度都是一緯的,也就是變化的因素只有一類。 ????????對于出現變化因素有兩類:也就是有兩個變化緯度的情況,繼承實現就會比較痛苦。從理論上來說,如果用繼承的方式來實現這種有兩個變化緯度的情況,最后實際的實現類應該是兩個維度上可變數量的乘積那么多個。如果要在任何一個緯度上進行擴展,都需要實現另外一個緯度上的可變數量那么多個實現類,這也是為何會感覺擴展起來很困難;且隨著程序規模的加大,會越來越難以擴展和維護。 ????????【橋接模式】就是用來解決這種有兩個變化緯度的情況下,如果靈活地擴展功能的一個很好的方案,其實,橋接模式主要是把繼承改成了使用對象組合,從而把兩個維度分開,讓每一個緯度單獨去變化,最后通過對象組合的方式,把兩個維度組合起來,每一種組合的方式就相當于原來繼承中的一種實現,這樣就有效地減少了實際實現的類的個數【理論上,如果使用橋接模式的方式來實現這種有兩種變化緯度的情況,最后實際的實現類應該是兩個緯度上可變數量的和】。? ? ? ?? |
序號 | 誰來橋接說明 |
1 | 由客戶端來負責創建接口對象,并在創建抽象類對象的時候,把它設置到抽象部分的對象中去。 |
2 | 可以在抽象部分對象構建的時候,由抽象部分的對象自己來創建相應的接口對象,也可以給它傳遞一些參數,根據參數來選擇并創建具體的接口對象。 |
3 | 可以在抽象類中選擇并創建一個默認的接口對象,然后子類可以根據需要改變這個實現。 |
4 | 也可以使用抽象工廠或者簡單工廠來選擇并創建具體的接口對象,抽象部分的類可以通過調用工廠的方法來獲取接口對象。 |
5 | 如果使用IOC/DI容器的話,還可以通過IOC/DI容器來創建具體的接口對象,并注入會到抽象類中。 |
序號 | 橋接模式的優點 |
1 | 分離抽象和實現部分:橋接模式分離了抽象部分和實現部分,從而及大地提高了系統的靈活性。讓抽象部分和實現部分獨立開來,分別定義接口,這有助于對系統進行分層,從而產生更好的結構化的系統。對于系統的高層部分,只需要知道抽象部分和實現部分的接口就可以了。 |
2 | 更好的擴展性:由于橋接模式把抽象部分和實現部分分離開了,而且分別定義接口,這就使得抽象部分和實現部分可以分別獨立地擴展,而不會相互影響,從而大大提高了系統的可擴展性。 |
3 | 可動態地切換實現:由于橋接模式把抽象部分和實現部分分離開了,所以在實現橋接的時候,就可以實現動態的選擇和使用具體的實現。也就是說一個實現不再是固定的綁定在一個抽象接口上了,可以實現在運行期間動態地切換。 |
4 | 可以減少子類的個數:對于兩個變化緯度的情況,如果采用繼承的實現方式,大約需要在兩個緯度上的可變化數量的乘積個子類;而采用橋接模式來實現,大約需要兩個緯度上的可變化數量的和個子類。可以明顯的減少子類的個數。 |
序號 | 說明 |
1 | 橋接模式的本質是:分離抽象和實現;橋接模式最重要的工作就是分離抽象部分和實現部分,這是解決問題的關鍵。只有把抽象部分和實現部分分離開了,才能夠讓它們獨立地變化;只有抽象部分和實現部分可以獨立地變化,系統才會有更好的可擴展性和可維護性(還有其他好處如:可以動態地切換實現、可以減少子類個數等)。 |
2 | 對設計原則的體現: ????????《1》橋接模式很好地實現了開閉原則(通常應用橋接模式的地方,抽象部分和實現部分都是可變化的,也就是應用會有兩個變化緯度,橋接模式就是找到這兩個變化,并分別封裝起來,從而合理地實現OCP)。在使用橋接模式的時候,通常情況下,頂層的抽象類和接口是不變的,而繼承抽象類的具體類是可變的。由于抽象類是通過接口來操作具體的實現類,因此具體的實現類是可以擴展的,并根據需要可以有多個具體的實現。 |
何時選用橋接模式?
?????????1、如果不希望在抽象部分和實現部分采用固定的綁定關系,可以采用橋接模式,來把抽象部分和實現部分分開,然后在程序運行期間來動態地設置抽象部分需要用到的具體的實現,還可以動態地切換具體的實現。
?????????2、如果出現抽象部分和實現部分都能夠擴展的情況,可以采用橋接模式,讓抽象部分和實現部分獨立地變化,從而靈活地進行單獨擴展,而不是攪在一起,擴展一邊會影響到另一邊。
????????3、如果希望實現部分的修改不會對客戶產生影響,可以采用橋接模式。由于客戶是面向抽象的接口在運行,實現部分的修改可以獨立于抽象部分,并不會對客戶產生影響,也可以說對客戶是透明的。
????????4、如果采用繼承的方案,會導致產生很多子類,對于這種情況,可以考慮采用橋接模式,分析功能變化的原因,看看能否分離不同的緯度,然后通過橋接模式來分離它們,從而減少子類的數目。
二、橋接模式示例
????????業務需求:發送提示消息(如某人有新的工作了,需要發送一條消息提示他)。從業務上看,消息由分為普通消息、加急消息和特急消息多種,不同的消息類型,業務功能處理是不一樣的。如:加急消息是在消息上添加加急,而特急消息除了添加特急外,還會做一條催促的記錄,多久不完成就會繼續催促;從發送消息的手段上看:有系統內消息、手機短信消息、郵件消息等。
?2.1、不使用模式的示例
? 2.1.1、實現簡化版本
????????我們先實現一個簡單版本(如:消息只是實現發送普通消息,發送方式只是實現系統內消息和郵件)由于發送普通消息會有兩種不同的實現方式,為了讓外部能夠統一操作,因此,把消息設計為接口,然后由兩個不同的實現類分別實現系統內消息方式和郵件發送消息方式。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.NoPattern
{/// <summary>/// 消息統一接口/// </summary>internal interface IMessage{/// <summary>/// 發送消息/// </summary>/// <param name="message">需要發生的消息</param>/// <param name="toUser">消息要發送給的人員</param>void Send(string message,string toUser);}//Interface_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.NoPattern
{/// <summary>/// 站內消息的方式發送普通消息/// </summary>internal class CommonMsgSMS : IMessage{public void Send(string message, string toUser){Console.WriteLine($"現在使用【站內消息方式】發送消息【{message}】給【{toUser}】");}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.NoPattern
{/// <summary>/// 使用Email的方式發送普通消息/// </summary>internal class CommonMsgEmail : IMessage{public void Send(string message, string toUser){Console.WriteLine($"現在使用【Email方式】發送消息【{message}】給【{toUser}】");}}//Class_end
}
? 2.1.2、實現加急發送消息
? ? ? ? 加急發送消息的實現不同于普通消息,需要在消息前加上加息,然后在發送消息;另外加急消息會提供監控的方法,讓客戶端可以隨時通過這個方法來了解對于加急消息的處理進度(如:相應的人員是否接收到這個消息,相應的工作是否已經開展等)因此加急消息需要擴展新的接口,除了實現基本的發送消息功能外,還需要添加監控功能:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.NoPattern
{/// <summary>/// 加急消息接口/// </summary>internal interface IUrgencyMessage:IMessage{/// <summary>/// 監控某消息的處理/// </summary>/// <param name="messageId">被監控消息的編號</param>/// <returns>返回監控到的數據對象</returns>object Watch(string messageId);}//Interface_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.NoPattern
{/// <summary>/// 站內消息的方式發送加急消息/// </summary>internal class UrgencyMsgSMS : IUrgencyMessage{public void Send(string message, string toUser){message = $"[加急] {message}";Console.WriteLine($"現在使用【站內消息方式】發送消息【{message}】給【{toUser}】");}public object Watch(string messageId){//獲取相應的數據,組織成為監控的數據對象,然后返回return null;}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.NoPattern
{/// <summary>/// 使用Email的方式發送加急消息/// </summary>internal class UrgencyMsgEmail : IUrgencyMessage{public void Send(string message, string toUser){message = $"[加急] {message}";Console.WriteLine($"現在使用【Email方式】發送消息【{message}】給【{toUser}】");}public object Watch(string messageId){//獲取相應的數據,組織成為監控的數據對象,然后返回return null;}}//Class_end
}
? 2.1.3、客戶端測試
namespace BridgePattern
{internal class Program{static void Main(string[] args){NoPatternTest();Console.ReadLine();}/// <summary>/// 不使用模式的示例/// </summary>private static void NoPatternTest(){Console.WriteLine("------不使用模式的示例------");//使用站內消息方式發送【普通】消息NoPattern.IMessage message = new NoPattern.CommonMsgSMS();message.Send("請你吃飯", "張三");//使用站內消息方式發送【加急】消息message = new NoPattern.UrgencyMsgSMS();message.Send("請你吃飯", "張三");Console.WriteLine();//使用郵件的方式發送【普通】消息message = new NoPattern.CommonMsgEmail();message.Send("請你吃飯", "張三");//使用郵件的方式發送【加急】消息message = new NoPattern.UrgencyMsgEmail();message.Send("請你吃飯", "張三");}}//Class_end
}
? 2.1.4、運行結果
這個示例是滿足了基本的功能要求,可是這么實現好不好呢?有沒有什么問題呢?
????????通過繼承來擴展的實現方式,有個明顯的缺點:擴展消息的種類不太容易;不同類型的消息具有不同的業務,也就是有不同的實現,在這種情況下,每個種類的消息,需要實現所有不同的消息發送方式。更可怕的是,如果要新加入一種消息的發送方式,那么會要求所有的消息種類都要加入這種新的發送方式的實現。要是考慮業務功能上再擴展一下呢?(如:群發消息,也就是一次可以發送多條消息)就意味著很多地方都要修改,這樣的實現很明顯是不靈活的。
?2.2、橋接模式示例
????????橋接模式就是用來解決上述問題的,將抽象部分與它的實現部分分離,使得它們都可以獨立的變化。仔細分析上面的示例要求,示例的變化具有兩個緯度,一個緯度是抽象的消息(包含普通消息、加急消息、特急消息);另一個緯度是具體的消息發送方式(包含:站內消息、Email消息、手機短信消息)這幾個方式是平等的,可被切換方式。這兩個緯度一共組合出9種可能性,如下圖所示:
????????出現問題的根本原因是:在與消息的抽象和實現是混合在一起的,這就導致了一個緯度的變化會引起另一個緯度進行相應的變化,從而使得程序擴展起來非常困難。要想解決這個問題,就必須把這兩個緯度分開(即:將抽象部分和實現部分分開,讓它們相互獨立,這樣就可以實現獨立的變化,使擴展變得簡單)。
? ? ? ? 橋接模式通過引入實現的接口,把實現部分從系統中分離出去。那么,抽象這邊如何使用具體的實現呢?肯定是用面向實現的接口來編程,為了讓抽象這邊能夠很方便地與實現結合起來,把頂層的抽象接口改成抽象類,在其中持有一個具體的實現部分的實例。這樣一來,對于需要發送消息的客戶端來說,只需要創建相應的消息對象,然后調用這個消息對象的方法就可以了,這個消息對象會調用持有的真正消息發送法師來把消息發送出去(也就是說:客戶端只是想要發送消息而已,并不想關心具體如何發送)。
? 2.2.1、實現簡單功能
我們先從簡單的功能開始,實現普通消息和加急消息功能。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.BridgeDemoOne
{/// <summary>/// 發送消息的統一接口/// </summary>internal interface IMessage{/// <summary>/// 發送消息/// </summary>/// <param name="message">要發送的消息內容</param>/// <param name="toUser">消息發送給的人員</param>void Send(string message,string toUser);}//Interface_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.BridgeDemoOne
{/// <summary>/// 抽象的消息對象/// </summary>internal class AbstractMessage{//持有一個實現消息的對象protected IMessage message;/// <summary>/// 構造函數/// </summary>/// <param name="message">實現消息的對象</param>public AbstractMessage(IMessage message){this.message = message;}/// <summary>/// 發送消息/// </summary>/// <param name="message">需要發送的消息內容</param>/// <param name="toUser">消息發送的給的人員</param>public virtual void SendMsg(string message,string toUser){this.message.Send(message, toUser);}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.BridgeDemoOne
{/// <summary>/// 站內消息/// </summary>internal class MsgSMS : IMessage{public void Send(string message, string toUser){Console.WriteLine($"使用站內消息的方式,發送消息【{message}】給【{toUser}】");}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.BridgeDemoOne
{/// <summary>/// 使用Email方式發送消息/// </summary>internal class MsgEmail : IMessage{public void Send(string message, string toUser){Console.WriteLine($"使用Email消息的方式,發送消息【{message}】給【{toUser}】");}}//Class_end
}
接下來就是擴展抽象消息接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.BridgeDemoOne
{/// <summary>/// 普通消息/// </summary>internal class CommonMsg : AbstractMessage{public CommonMsg(IMessage message) : base(message){}public override void SendMsg(string message, string toUser){//【普通消息】直接調用父類方法,把消息發送出去就可以了base.SendMsg(message, toUser);}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.BridgeDemoOne
{/// <summary>/// 加急消息/// </summary>internal class UrgencyMsg : AbstractMessage{public UrgencyMsg(IMessage message) : base(message){}public override void SendMsg(string message, string toUser){message = $"[加急] {message}";base.SendMsg(message, toUser);}/// <summary>/// 擴展新功能(監控消息的處理過程)/// </summary>/// <param name="messageId">被監控的消息的編號</param>/// <returns>返回被監控到的數據對象</returns>public object Watch(string messageId){//獲取相應的數據,組織成監控的數據對象,然后返回return null;}}//Class_end
}
? 2.2.2、添加新功能
????????上面已經使用橋接模式實現了2種消息發送方式和2種消息類型消息;現在來看一下能夠解決前面提出的問題,我們通過新添加還未實現的功能來看看(即:新添加特急消息處理;新增加使用手機發送消息的方式)該如何實現?
我們只需要在抽象部分新添加一個特急消息類,擴展抽象消息就可以把特急消息的處理功能加入系統中了;對于新增手機發送消息的方式也簡單,只需要在新增一個類實現手機發送消息的方式即可。
《1》新增特急消息處理類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.BridgeDemoOne
{/// <summary>/// 特急消息/// </summary>internal class SpecialUrgencyMsg : AbstractMessage{public SpecialUrgencyMsg(IMessage message) : base(message){}public override void SendMsg(string message, string toUser){message = $"[特急] {message}";base.SendMsg(message, toUser);}public void Hurry(string messageId){//執行催促的業務,發出催促消息}}//Class_end
}
《2》新增的手機發送消息方式功能
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.BridgeDemoOne
{/// <summary>/// 手機短信的方式發送消息/// </summary>internal class MsgMobile : IMessage{public void Send(string message, string toUser){Console.WriteLine($"使用手機短信的方式,發送消息【{message}】給【{toUser}】");}}//Class_end
}
? 2.2.3、客戶端測試
namespace BridgePattern
{internal class Program{static void Main(string[] args){BridgeDemoOneTest();Console.ReadLine();}/// <summary>/// 橋接模式示例1/// </summary>private static void BridgeDemoOneTest(){Console.WriteLine("------橋接模式示例1------");/*把發送消息實現方式切換為站內信*///創建具體的實現對象BridgeDemoOne.IMessage messageStyle = new BridgeDemoOne.MsgSMS();//創建一個普通的消息對象BridgeDemoOne.AbstractMessage abstarctMessage=new BridgeDemoOne.CommonMsg(messageStyle);abstarctMessage.SendMsg("請你吃飯","張三");//創建一個緊急消息對象abstarctMessage=new BridgeDemoOne.UrgencyMsg(messageStyle);abstarctMessage.SendMsg("請你吃飯","張三");//創建一個特急消息對象abstarctMessage = new BridgeDemoOne.SpecialUrgencyMsg(messageStyle);abstarctMessage.SendMsg("請你吃飯", "張三");Console.WriteLine();/*把發送消息實現方式切換為郵件*///創建具體的實現對象messageStyle=new BridgeDemoOne.MsgEmail();//創建一個普通的消息對象abstarctMessage = new BridgeDemoOne.CommonMsg(messageStyle);abstarctMessage.SendMsg("請你吃飯", "張三");//創建一個緊急消息對象abstarctMessage = new BridgeDemoOne.UrgencyMsg(messageStyle);abstarctMessage.SendMsg("請你吃飯", "張三");//創建一個特急消息對象abstarctMessage = new BridgeDemoOne.SpecialUrgencyMsg(messageStyle);abstarctMessage.SendMsg("請你吃飯", "張三");}}//Class_end
}
? 2.2.4、運行結果
? 2.2.5、誰來橋接
《1》由抽象部分的對象自己來創建相應的對象
這種情況又分為兩種實現:一種是需要外部傳入參數,另一種是不需要外部傳入參數:
①外部傳入參數:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.BridgeDemoOne
{/// <summary>/// 抽象的消息對象/// </summary>internal class AbstractMessage2{//持有一個實現部分的對象protected IMessage message;public AbstractMessage2(int type){switch (type){case 1:message = new MsgSMS();break;case 2:message = new MsgEmail();break;case 3:message = new MsgMobile();break;default:break;}}/// <summary>/// 發送消息/// </summary>/// <param name="message">需要發送的消息內容</param>/// <param name="toUser">消息發送的給的人員</param>public virtual void SendMsg(string message, string toUser){this.message.Send(message, toUser);}}//Class_end
}
②不需要外部傳入參數
????????這種不需要外部傳入參數的情況,那就說明在抽象類中,有可能在抽象類的構造函數中選擇;也有可能在具體的方法中選擇。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.BridgeDemoOne
{/// <summary>/// 抽象的消息對象/// </summary>internal class AbstractMessage3{//持有一個實現部分的對象protected IMessage message;public AbstractMessage3(){}/// <summary>/// 發送消息/// </summary>/// <param name="message">需要發送的消息內容</param>/// <param name="toUser">消息發送的給的人員</param>public virtual void SendMsg(string message, string toUser){this.message.Send(message, toUser);}//根據消息的長度來選擇合適的實現protected IMessage GetImpl(string message){IMessage msg = null;if (string.IsNullOrEmpty(message)){//若沒有任何消息則默認使用站內消息msg = new MsgSMS();}else if (message.Length < 100){//如消息長度在100以內,則使用手機短信msg = new MsgMobile();}else if (message.Length < 1000){//如消息長度在100-1000以內,則使用站內消息msg = new MsgSMS();}else{//如消息長度在1000以上,則使用Emailmsg = new MsgEmail();}return msg;}}//Class_end
}
《2》在抽象類的構造函數中創建默認實現對象
????????直接在抽象類的構造方法中,創建一個默認的實現對象,然后子類根據需要,可以選擇直接使用還是覆蓋掉。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BridgePattern.BridgeDemoOne
{internal class AbstractMessage4{//持有一個實現消息的對象protected IMessage message;/// <summary>/// 構造函數/// </summary>public AbstractMessage4(){//創建一個默認的實現this.message = new MsgSMS();}/// <summary>/// 發送消息/// </summary>/// <param name="message">需要發送的消息內容</param>/// <param name="toUser">消息發送的給的人員</param>public virtual void SendMsg(string message, string toUser){this.message.Send(message, toUser);}}//Class_end
}
三、項目源碼工程
kafeiweimei/Learning_DesignPattern: 這是一個關于C#語言編寫的基礎設計模式項目工程,方便學習理解常見的26種設計模式https://github.com/kafeiweimei/Learning_DesignPattern