《大話設計模式》——學習筆記之"結構型模式"(適配器&裝飾&橋接&組合&亨元&代理&外觀)

《大話設計模式》——學習筆記之”結構型模式”(適配器&裝飾&橋接&組合&亨元&代理&外觀)

適配器模式

定義:適配器模式(Adapter),將一個類的接口轉換成客戶希望的另外一個接口。Adapter模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作

<img da_017>

Target,客戶所期望的接口

class Target{
    public virtual void Request(){
        Console.WriteLine("普通請求!");
    }
}

Adaptee,需要適配的類

class Adaptee{
    public void SpecificRequest(){
        Console.WriteLine("特殊請求!");
    }
}

Adapter,通過在內部包裝一個Adapter對象,把源接口轉換成目標接口

class Adapter : Target{
    //建立一個私有的Adaptee對象
    private Adaptee adaptee = new Adaptee();

    public override void Request(){
        //把表面上調用Request()方法變成實際調用SpecificRequest()
        adaptee.SpecificRequest();
    }
}

客戶端代碼:

static void Main(string[] args){
    Target target = new Adapter();
    //對客戶端來說,調用的就是Target的Request()
    target.Request();

    Console.Read();
}

優點:

  • 系統的數據和行爲都正確,但接口不符時,應該考慮用適配器,目的是使控制範圍之外的一個原有對象與某個接口匹配,適配器模式主要應用於希望複用一些現存的類,但是接口又與複用環境要求不一致的情況
  • 兩個類所做的事情相同或相似,但是具有不同的接口時要使用它,讓接口不同的類通過適配後,協同工作
  • 適配器不需要虛構出一個代表者,只需要爲應付特定使用目的,將原來的類進行一些組合
  • 主要是爲了解決已有接口之間不匹配的問題,不需要考慮這些接口是怎樣實現的,也不考慮它們各自可能會如何演化,不需要對兩個獨立設計的類中任一個進行重新設計,就能夠使它們協同工作

裝飾模式

定義: 動態地給一個對象添加一些額外的職責,就增加功能來說,裝飾模式比生成子類更爲靈活

<img da_007>

Component是定義一個對象接口,可以給這些對象動態地添加職責。ConcreteComponent是定義了一個具體的對象,也可以給這個對象添加一些職責。Decorator,裝飾抽象類,繼承了Component,從外類來擴展Component類的功能,但對於Component來說,是無需知道Decorator的存在的,至於ConcreteDecorator就是具體的裝飾對象,起到給Component添加職責的功能

Component類

abstract class Component{
    public abstract void Operation();
}

ConcreteComponent類

class ConcreteComponent : Component{
    public override void Operation(){
        Console.WriteLine("具體對象的操作");
    }
}

Decorator類

abstract class Decorator : Component{
    protected Component component;

    //裝飾模式是利用SetComponent來對對象進行包裝的,每個裝飾對象的實現就和如何使用這個對象分離開了,每個裝飾對象只關心自己的功能,不需要關係如何被添加到對象鏈當中
    public void SetComponent(Component component){
        this.component = component;//設置Component
    }

    //重寫Operation(),實際執行的是Component的Operation()
    public override void Operation(){
        if(component != null){
            component.Operation();
        }
    }
}

//如果只有一個ConcreteComponent類而沒有抽象的Component類,那麼Decorator類可以是ConcreteComponent的一個子類。同樣道理,如果只有一個ConcreteDecorator類,那麼就沒有必要建立一個單獨的Decorator類,而可以把Decorator和ConcreteDecorator的責任合併成一個類

ConcreteDecoratorA 類

class ConcreteDecoratorA : Decorator{
    //本類的獨有功能,以區別於ConcreteDecoratorB
    private string addedState;
    public override void Operation(){
        //首先運行原Component的Operation(),再執行本類的功能,如addState,相當於對原Component進行了裝飾
        base.Operation();
        addedState = "New State";
        Console.WriteLine("具體裝飾對象A的操作");
    }
}

ConcreteDecoratorB 類

class ConcreteDecoratorB : Decorator{
    private string addedState;
    public override void Operation(){
        //首先運行原Component的Operation(),再執行本類的功能,如addState,相當於對原Component進行了裝飾
        base.Operation();
        AddedBehavior();
        Console.WriteLine("具體裝飾對象B的操作");
    }

    private void AddedBehavior(){
        //本類獨有的方法,以區別於ConcreteDecoratorA
    }

}

客戶端代碼

static void Main(string[] args){
    ConcreteComponent c = new ConcreteComponent();
    ConcreteDecoratorA d1 = new ConcreteDecoratorA();
    ConcreteDecoratorB d2 = new ConcreteDecoratorB()

    d1.SetComponent(c);
    d2.SetComponent(d1);
    //裝飾的方法是:首先用ConcreteComponent實例化對象c,然後用ConcreteDecoratorA的實例化對象d1來包裝c,再用ConcreteDecoratorB的對象d2來包裝d1,最終執行d2的Operation()
    d2.Operation();

    Console.Read();
}

優點:

  • 動態地給一個對象添加一些額外的職責,就增加功能來說,裝飾模式相比生成子類更加靈活
  • 子類多半只是爲了某個對象增加一些職責,此時通過裝飾的方式,可以更加靈活、以動態、透明的方式給單個對象添加職責,並在不需要時,撤銷相應的職責
  • 裝飾模式是爲已有功能動態地添加更多功能的一種方式
  • 當系統需要新功能的時候,是向舊的類中添加新的代碼。這些新加的代碼通常裝飾了原有類的核心職責或主要行爲,在主類中加入了新的字段,新的方法和新的邏輯,從而增加了主類的複雜度,而這些新加入的東西僅僅是爲了滿足一些只在某種特定情況下才會執行的特殊行爲的需要時,裝飾模式提供了一個非常好的解決方案,它把每個要裝飾的功能放在單獨的類中,並讓這個類包裝它所要裝飾的對象,因此,當需要執行特殊行爲時,客戶代碼就可以在運行時根據需要有選擇地、按順序地使用裝飾功能包裝對象了
  • 裝飾模式的優點就是把類中的裝飾功能從類中搬移去除,有效地把類的核心職責和裝飾功能區分開了,而且可以去除相關類中重複的裝飾邏輯

橋接模式

定義: 將抽象部分與它的實現部分分離,使它們都可以獨立地變化

<img 022>

Implementor類

abstract class Implementor{
    public abstract void Operation();
}

ConcreteImplementorA和ConcreteImplementorB等派生類

class ConcreteImplementorA : Implementor{
    public override void Operation(){
        Console.WriteLine("具體實現A的方法執行");
    }
}

class ConcreteImplementorB : Implementor{
    public override void Operation(){
        Console.WriteLine("具體實現B的方法執行");
    }
}

Abstraction類

class Abstraction{
    protected Implementor implementor;

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

    public virtual void Operation(){
        implementor.Operation();
    }
}

RefinedAbstraction類

class RefinedAbstraction : Abstraction{
    public override void Operation(){
        implementor.Operation();
    }
}

客戶端實現

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

    ab.SetImplementor(new ConcreteImplementorA());
    ab.Operation();

    ab.SetImplementor(new ConcreteImplementorB());
    ab.Operation();

    Console.Read();
}

優點:

  • 實現系統可能有多角度分類,每一種分類都有可能變化,那麼就把這種多角度分離出來讓它們獨立變化,減少它們之間的耦合
  • 將抽象部分與它的實現部分分離,使它們都可以獨立地變化
  • 用聚合來代替繼承,在繼承體系中,有兩個甚至多個方向的變化,就解耦這些不同方向的變化,通過對象組合的方式,把兩個角色之間的繼承關係改爲組合的關係,從而使這兩者可以應對各自獨立的變化
  • 在設計之初,就對抽象接口與它的實現部分進行橋接,讓抽象與實現兩者可以獨立演化

組合模式

定義: 組合模式(Composite),將對象組合成樹形結構以表示’部分-整體’的層次結構,組合模式使得用戶對單個對象和組合對象的使用具有一致性

<img da_019>

Component爲組合中的對象聲明接口,在適當情況下,實現所有類共有接口的默認行爲,聲明一個接口用於訪問和管理Component的子部件

abstract class Component{
    protected string name;

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

    //通常用Add和Remove方法來提供增加或移除樹葉或樹枝的功能
    public abstract void Add(Component c);
    public abstract void Remove(Component c);
    public abstract void Display(int depth);
}

Leaf在組合中表示葉節點對象,葉節點沒有子節點

class Leaf : Component{
    public Leaf(string name):base(name){

    }

    public override void Add(Component c){
        Console.WriteLine("Cannot add to a leaf");
    }

    public override void Remove(Component c){
        Console.WriteLine("Cannot remove from a leaf");
    }

    public override void Display(int depth){
        //葉節點的具體方法
        Console.WriteLine(new String("-", depth) + name);
    }
}

Composite定義有枝節點行爲,用來存儲子部件,在Component接口中實現與子部件有關的操作,比如增加Add和刪除Remove

class Composite : Component{

    //一個子對象集合用來存儲其下屬的枝節點和葉節點
    private List<Component> children = new List<Component>();

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

    }

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

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

    //顯示其枝節點名稱,並對其下級進行遍歷
    public override void Display(int depth){
        Console.WriteLine(new String('-', depth) + name);
        foreach(Component component in children){
            component.Display(depth + 2);
        }
    }
}

客戶端代碼,通過Component接口操作組合部件的對象

static void Main(string[] args){
    //生成樹根root,根上長出兩葉LeafA和LeafB
    Composite root = new Composite("root");
    root.Add(new Leaf("Leaf A"));
    root.Add(new Leaf("Leaf B"));

    //根上長出分枝Composite X,分枝上也有兩葉Leaf XA和Leaf XB
    Composite comp = new Composite("Composite X");
    comp.Add(new Leaf("Leaf XA"));
    comp.Add(new Leaf("Leaf XB"));
    root.Add(comp);

    //根上長出分枝Composite XY,分枝上也有兩葉Leaf XYA和Leaf XYB
    Composite comp2 = new Composite("Composite XY");
    comp.Add(new Leaf("Leaf XYA"));
    comp.Add(new Leaf("Leaf XYB"));
    root.Add(comp2);

    //顯示
    root.Display(1);

    Console.Read();
}

優點:

  • 忽略組合對象與單個對象的不同,統一地使用組合結構中的所有對象時,就應該考慮使用組合模式
  • 將對象組合成樹形結構以表示”部分-整體”的層次結構,組合模式使得用戶對單個對象和組合對象的使用具有一致性
  • 客戶可以一致地使用組合結構和單個對象,任何用到基本對象的地方都可以使用組合對象(用戶使用組合類接口與組合結構中的對象進行交互,如果接收者是一個葉節點,則直接處理請求,如果接收者是組合對象,通常將請求發它的子部件,並在轉發請求之前或之後可能執行一些輔助操作)

亨元模式

定義: 運用共享技術有效地支持大量細粒度的對象

<da_026>

Flyweight類,它是所有具體亨元類的超類或接口,通過這個接口,Flyweight可以接受並作用於外部狀態

abstract class Flyweight{
    public abstract void Operation(int extrinsicstate);
}

ConcreteFlyweight是繼承Flyweight超類或實現Flyweight接口,併爲內部狀態增加存儲空間

class ConcreteFlyweight : Flyweight{
    public override void Operation(int extrinsicstate){
        Console.WriteLine("具體extrinsicstate:" + extrinsicstate);
    }
}

UnsharedConcreteFlyweight是指那些不需要共享的Flyweight子類,因爲Flyweight接口共享成爲可能,但它並不強制共享

class UnsharedConcreteFlyweight : Flyweight{
    public override void Operation(int extrinsicstate){
        Console.WriteLine("不共享的具體Flyweight:" + extrinsicstate);
    }
}

FlyweightFactory是一個亨元工廠,用來創建並管理Flyweight對象,它主要是用來確保合理地共享Flyweight,當用戶請求一個Flyweight時,FlyweightFactory對象提供一個已創建的實例或者創建一個(如果不存在的話)

class FlyweightFactory{
    private Hashtable flyweights = new Hashtable();

    //初始化工廠時,先生成三個實例
    public FlyweightFactory(){
        flyweights.Add("X", new ConcreteFlyweight());
        flyweights.Add("Y", new ConcreteFlyweight());
        flyweights.Add("Z", new ConcreteFlyweight());
    }

    public Flyweight GetFlyweight(string key){
        //根據客戶端請求,獲得已生成的實例
        return ((Flyweight)flyweights[key]);
    }
}

客戶端代碼

static void Main(string[] args){
    //代碼外部狀態
    int extrinsicstate = 22;

    FlyweightFactory f = new FlyweightFactory();

    Flyweight fx = f.GetFlyweight("X");
    fx.Operation(--extrinsicstate);

    Flyweight fy = f.GetFlyweight("Y");
    fy.Operation(--extrinsicstate);

    Flyweight fz = f.GetFlyweight("Z");
    fz.Operation(--extrinsicstate);

    UnsharedConcreteFlyweight uf = new UnsharedConcreteFlyweight();

    uf.Operation(--extrinsicstate);

    Console.Read();
}

優點:

  • 如果一個應用程序使用了大量的對象,而大量的這些對象造成了很大的存儲開銷時就應該考慮使用亨元模式,大量重複的對象是對資源的極大浪費
  • 亨元模式可以避免大量非常相似類的開銷,在程序設計中,有時需要生成大量細粒度的類實例來表示數據。如果能發現這些實例除了幾個參數外基本上都是相同的,有時就能夠大幅度地減少需要實例化的類的數量,如果能把那些參數移到類實例的外面,在方法調用時將它們傳遞進來,就可以通過共享大幅度地減少單個實例的數目
  • 亨元模式Flyweight執行時所需的狀態是有內部的,也可能有外部的,內部狀態存儲於ConcreteFlyweight對象中,而外部對象則應該考慮由客戶端對象存儲或計算,當調用Flyweight對象的操作時,將該狀態傳遞給它
  • 當對象的大多數狀態是外部狀態,如果刪除對象的外部狀態,那麼可以用相對較少的共享對象取代很多組對象,此時可以考慮使用亨元模式

代理模式

定義: 爲其他對象提供一種代理以控制對這個對象的訪問

<img da_008>

Subject類,定義了RealSubject和Proxy的共用接口,這樣就在任何使用RealSubject的地方都可以使用Proxy

abstract class Subject{
    public abstract void Request();
}

RealSubject類,定義Proxy所代表的真實實體

class RealSubject : Subject{
    public override void Request(){
        Console.WriteLine("真實的請求");
    }
}

Proxy類,保存一個引用使得代理可以訪問實體,並提供一個與Subject的接口相同的接口,這樣代理就可以用來替代實體

class Proxy : Subject{
    RealSubject realSubject;
    public override void Request(){
        if(realSubject == null){
            realSubject = new RealSubject();
        }
        realSubject.Request();
    }
}

客戶端代碼

static void Main(string[] args){
    Proxy proxy = new Proxy();
    proxy.Request();
    Console.Read();
}

優點:

  • 爲其他對象提供一種代理以控制對這個對象的訪問
  • 代理對象代表一個單一對象,代理的客戶對象無法直接訪問目標對象,由代理提供對單獨的目標對象的訪問控制
  • 遠程代理:爲一個對象在不同的地址空間提供局部代表,這樣可以隱藏一個對象存在於不同地址空間的事實
  • 虛擬代理:根據需要創建開銷很大的對象,通過它來存放實例化需要很長時間的真實對象(如HTML網頁通過虛擬代理來替代了真實的圖片,此時代理存儲了真實圖片的路徑和尺寸)
  • 安全代理:用來控制真實對象訪問時的權限
  • 智能指引:指當調用真實的對象時,代理處理另外一些事(如計算真實對象的引用次數)

外觀模式

定義: 爲子系統中的一組接口提供一個一致的界面,此模式定義了一個高層接口,這個接口使得這一子系統更加容易使用

<da_012>

子系統類

class SubSystemOne{
    public void MethodOne(){
        Console.WriteLine("子系統方法一");
    }
}

class SubSystemTwo{
    public void MethodTwo(){
        Console.WriteLine("子系統方法二");
    }
}
...

外觀類

class Facade{
    SubSystemOne one;
    SubSystemTwo two;

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

    public void MethodA(){
        Console.WriteLine("\n方法組A() --- ");
        one.MethodOne();
        two.MethodTwo();
    }

    public void MethodB(){
        Console.WriteLine("\n方法組B() --- ");
        two.MethodTwo();
    }
}

客戶端調用

static void Main(string[] args){
    Facade facade = new Facade();
    //由於Facade的作用,客戶端可以根本不知三個子系統類的存在
    facade.MethodA();
    facade.MethodB();
    Console.Read();
}

優點:

  • 如果兩個類不必彼此直接通信,那麼就不要讓這兩個類發生直接的相互作用,如果實在需要調用,可以通過第三者來轉發調用,類之間的耦合越弱,越有利於複用
  • 引入一個外觀對象,爲子系統間提供了一個單一而簡單的屏障,可以讓子系統間的通信和相互依賴關係達到最小(軟件架構中,層與層之間地分離其實就是外觀模式的體現)
  • 外觀對象代表一個子系統,提供對子系統各元件功能的簡化的共同層次的調用接口
  • 在設計初期階段,應該要有意識的將不同的兩個層分離,層與層之間建立外觀,爲複雜的子系統提供一個簡單的接口,使得耦合大大降低
  • 其次,在開發階段,子系統往往因爲不斷的重構演化而變得越來越複雜(很多很小的類),增加外觀Facade可以提供一個簡單的接口,減少它們之間的依賴
  • 第三,在維護一個遺留的大型系統時,可能這個系統已經非常難以維護和擴展了,爲新系統開發一個外觀Facade類,來提供設計粗糙或高度複雜的遺留代碼的比較清晰簡單的接口,讓新系統與Facade對象交互,Facade與遺留代碼交互所有複雜的工作
發佈了84 篇原創文章 · 獲贊 135 · 訪問量 41萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章