裝飾模式

1.概念

裝飾模式指的是在不必改變原類文件和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的對象。

2.特點

(1) 裝飾對象和真實對象有相同的接口。這樣客戶端對象就能以和真實對象相同的方式和裝飾對象交互。
(2) 裝飾對象包含一個真實對象的引用(reference)
(3) 裝飾對象接受所有來自客戶端的請求。它把這些請求轉發給真實的對象。
(4) 裝飾對象可以在轉發這些請求以前或以後增加一些附加功能。這樣就確保了在運行時,不用修改給定對象的結構就可以在外部增加附加的功能。在面向對象的設計中,通常是通過繼承來實現對給定類的功能擴展。

3.適用條件

以下情況使用Decorator模式

  1. 需要擴展一個類的功能,或給一個類添加附加職責。
  2. 需要動態的給一個對象添加功能,這些功能可以再動態的撤銷。
  3. 需要增加由一些基本功能的排列組合而產生的非常大量的功能,從而使繼承關係變的不現實。
  4. 當不能採用生成子類的方法進行擴充時。一種情況是,可能有大量獨立的擴展,爲支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增長。另一種情況可能是因爲類定義被隱藏,或類定義不能用於生成子類。

4.優點

  1. Decorator模式與繼承關係的目的都是要擴展對象的功能,但是Decorator可以提供比繼承更多的靈活性。
  2. 通過使用不同的具體裝飾類以及這些裝飾類的排列組合,設計師可以創造出很多不同行爲的組合。

5.缺點

  1. 這種比繼承更加靈活機動的特性,也同時意味着更加多的複雜性。
  2. 裝飾模式會導致設計中出現許多小類,如果過度使用,會使程序變得很複雜。
  3. 裝飾模式是針對抽象組件(Component)類型編程。但是,如果你要針對具體組件編程時,就應該重新思考你的應用架構,以及裝飾者是否合適。當然也可以改變Component接口,增加新的公開的行爲,實現“半透明”的裝飾者模式。在實際項目中要做出最佳選擇。

6.代碼中的應用

IO流的操作就是典型的裝飾模式

7.應用舉例

這裏用kobe老大來說明一下裝飾模式:

  • 首先對kobe做抽象,即拿到裝飾的公共接口,kobe是人類,人分三六九等,對於一個人別人會有對他的描述:

 

package decoration;

/**
 * Created by bitsino-001 on 2019/6/20.
 * 裝飾模式公共接口
 */
public interface Person {
    void desc();
}

  • 創建被裝飾類,這裏就是老大kobe了,需要實現人類的接口,每個人出生後都是沒有太大的區別,老大也是如此,開始只有一個簡單的描述:人類。

 

package decoration;

/**
 * Created by bitsino-001 on 2019/6/20.
 * 被裝飾類
 */
public class Kobe implements Person {
    @Override
    public void desc() {
        System.out.println("kobe是一個人");
    }
}
  • 接下來創建裝飾器,通過裝飾器來裝飾kobe老大。裝飾器和被裝飾類要實現同一個公共接口,同時裝飾器中需要持有被裝飾類,被裝飾類以公共接口的方式接收,這樣的好處是可以通過不同的裝飾組合出現不同的對象。

 

package decoration;

/**
 * Created by bitsino-001 on 2019/6/20.
 * 裝飾器
 */
public class PersonDecorator implements Person {
    /**
     * 持有被裝飾類,以公共接口接收
     */
    Person person;
    public PersonDecorator(Person person) {
        this.person = person;
    }

    @Override
    public void desc() {
        person.desc();
    }
}
  • 裝飾器已經有了,並在裝飾器中調用了被裝飾類的方法,現在需要創建不同的裝飾子類,真正的裝飾內容在裝飾子類中實現,首先用身高來裝飾一下老大,話說,老大從小受父親的影響,開始接觸籃球,不斷的鍛鍊中,他的身高達到接近兩米,變成了一個高人。

 

package decoration;

/**
 * Created by bitsino-001 on 2019/6/20.
 */
public class HighPerson extends PersonDecorator {
    public HighPerson(Person person) {
        super(person);
    }

    @Override
    public void desc() {
        super.desc();
        System.out.println("是一個高人");
    }
}

裝飾完後我們來測試一下裝飾的效果

 

package decoration;

import org.junit.Test;

/**
 * Created by bitsino-001 on 2019/6/20.
 * 裝飾模式測試
 */
public class DecorationTest {
    @Test
    public void test1(){
        System.out.println("被裝飾類(kobe)******************************");
        Person person = new Kobe();
        person.desc();
        System.out.println("用身高來裝飾被裝飾類(將近兩米高的kobe)******************************");
        person = new HighPerson(new Kobe());
        person.desc();

    }
}

裝飾結果

 

圖片.png

  • 和上面的裝飾方式類似,我們再用富和帥來裝飾一下老大,因爲方式類似,這裏直接給出代碼

 

package decoration;

/**
 * Created by bitsino-001 on 2019/6/20.
 */
public class RichPerson extends PersonDecorator {
    public RichPerson(Person person) {
        super(person);
    }

    @Override
    public void desc() {
        super.desc();
        System.out.println("是一個富人");
    }
}

 

package decoration;

/**
 * Created by bitsino-001 on 2019/6/20.
 */
public class PrettyPerson extends PersonDecorator {
    public PrettyPerson(Person person) {
        super(person);
    }

    @Override
    public void desc() {
        super.desc();
        System.out.println("是一個帥人");
    }
}

測試結果

 

package decoration;

import org.junit.Test;

/**
 * Created by bitsino-001 on 2019/6/20.
 * 裝飾模式測試
 */
public class DecorationTest {
    @Test
    public void test1(){
        System.out.println("被裝飾類(kobe)******************************");
        Person person = new Kobe();
        person.desc();
        System.out.println("用身高來裝飾被裝飾類(將近兩米高的kobe)******************************");
        person = new HighPerson(new Kobe());
        person.desc();
        System.out.println("用富有來裝飾被裝飾類(富有的kobe)******************************");
        person = new RichPerson(new Kobe());
        person.desc();
        System.out.println("用顏值來裝飾被裝飾類(顏值在線的kobe)******************************");
        person = new RichPerson(new Kobe());
        person.desc();
       

    }
}

圖片.png

  • 演示完單一的裝飾後,再來看一下通過組合方式的裝飾效果

 

package decoration;

import org.junit.Test;

/**
 * Created by bitsino-001 on 2019/6/20.
 * 裝飾模式測試
 */
public class DecorationTest {
    @Test
    public void test1(){
        System.out.println("被裝飾類(kobe)******************************");
        Person person = new Kobe();
        person.desc();
        System.out.println("用身高來裝飾被裝飾類(將近兩米高的kobe)******************************");
        person = new HighPerson(new Kobe());
        person.desc();
        System.out.println("用富有來裝飾被裝飾類(富有的kobe)******************************");
        person = new RichPerson(new Kobe());
        person.desc();
        System.out.println("用顏值來裝飾被裝飾類(顏值在線的kobe)******************************");
        person = new RichPerson(new Kobe());
        person.desc();
        System.out.println("高富帥的kobe******************************");
        person = new PrettyPerson(new RichPerson(new HighPerson(new Kobe())));
        person.desc();

    }
}

通過組合裝飾子類的方式,我們裝飾出了新的對象:高富帥的kobe!
上面的person = new PrettyPerson(new RichPerson(new HighPerson(new Kobe())));
創建方式,是不是在IO流的使用中覺得眼熟呢?

 



作者:慵懶的陽光丶
鏈接:https://www.jianshu.com/p/ff308c759f0a
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

8.和代理模式的區別:

這兩個設計模式看起來很像。對裝飾器模式來說,裝飾者(decorator)和被裝飾者(decoratee)都實現同一個 接口。對代理模式來說,代理類(proxy class)和真實處理的類(real class)都實現同一個接口。此外,不論我們使用哪一個模式,都可以很容易地在真實對象的方法前面或者後面加上自定義的方法。

        然而,實際上,在裝飾器模式和代理模式之間還是有很多差別的。裝飾器模式關注於在一個對象上動態的添加方法,然而代理模式關注於控制對對象的訪問。換句話 說,用代理模式,代理類(proxy class)可以對它的客戶隱藏一個對象的具體信息。因此,當使用代理模式的時候,我們常常在一個代理類中創建一個對象的實例。並且,當我們使用裝飾器模 式的時候,我們通常的做法是將原始對象作爲一個參數傳給裝飾者的構造器

        我們可以用另外一句話來總結這些差別:使用代理模式,代理和真實對象之間的的關係通常在編譯時就已經確定了,而裝飾者能夠在運行時遞歸地被構造。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章