1.概念
裝飾模式指的是在不必改變原類文件和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的對象。
2.特點
(1) 裝飾對象和真實對象有相同的接口。這樣客戶端對象就能以和真實對象相同的方式和裝飾對象交互。
(2) 裝飾對象包含一個真實對象的引用(reference)
(3) 裝飾對象接受所有來自客戶端的請求。它把這些請求轉發給真實的對象。
(4) 裝飾對象可以在轉發這些請求以前或以後增加一些附加功能。這樣就確保了在運行時,不用修改給定對象的結構就可以在外部增加附加的功能。在面向對象的設計中,通常是通過繼承來實現對給定類的功能擴展。
3.適用條件
以下情況使用Decorator模式
- 需要擴展一個類的功能,或給一個類添加附加職責。
- 需要動態的給一個對象添加功能,這些功能可以再動態的撤銷。
- 需要增加由一些基本功能的排列組合而產生的非常大量的功能,從而使繼承關係變的不現實。
- 當不能採用生成子類的方法進行擴充時。一種情況是,可能有大量獨立的擴展,爲支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增長。另一種情況可能是因爲類定義被隱藏,或類定義不能用於生成子類。
4.優點
- Decorator模式與繼承關係的目的都是要擴展對象的功能,但是Decorator可以提供比繼承更多的靈活性。
- 通過使用不同的具體裝飾類以及這些裝飾類的排列組合,設計師可以創造出很多不同行爲的組合。
5.缺點
- 這種比繼承更加靈活機動的特性,也同時意味着更加多的複雜性。
- 裝飾模式會導致設計中出現許多小類,如果過度使用,會使程序變得很複雜。
- 裝飾模式是針對抽象組件(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)可以對它的客戶隱藏一個對象的具體信息。因此,當使用代理模式的時候,我們常常在一個代理類中創建一個對象的實例。並且,當我們使用裝飾器模 式的時候,我們通常的做法是將原始對象作爲一個參數傳給裝飾者的構造器。
我們可以用另外一句話來總結這些差別:使用代理模式,代理和真實對象之間的的關係通常在編譯時就已經確定了,而裝飾者能夠在運行時遞歸地被構造。