定義
裝飾模式 Decorator : 在不改變原有功能的基礎上, 動態的給一個對象添加一些額外的職責 ,非常符合開閉原則 (對修改關閉,對擴展開放)
結構圖
Component是定義了一個接口(抽象類 、接口都可以),可以給這些對象動態的添加職責。
ConcreteComponent 是定義了一個具體的對象,也可以給這個對象添加一些職責。
Decorator 裝飾抽象類, 繼承了Component , 從外類來擴展Component類的功能, 但對已Component來講,是無需知道Decorator的存在的。
至於ConcreteComponent 就是具體的裝飾對象,給Component添加職責的功能。
總結下,分工如下:
• Component:抽象構件,裝飾者和被裝飾者共同的父類,是一個接口或者抽象類,用來定義基本行爲
• ConcreteComponent:定義具體對象,即被裝飾者
• Decorator:抽象裝飾者,繼承自Component,從外類來擴展ConcreteComponent
。
• ConcreteDecorator:具體裝飾者,用於擴展ConcreteComponent
需求
【接口 】
package com.gof.decoratorPattern;
public interface Component {
void operation();
}
【實現類 】
public class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("拍照");
}
}
要求: 原有的功能不變,已有的代碼不允許修改, 但是要支持添加新的功能,功能不確定,可任意組合。
what ? 不讓改原來的代碼,還得添加新的功能?
不過人家說的也合理啊,開閉原則 (對修改關閉,對擴展開放) ,那再好好琢磨琢磨怎麼實現
裝飾者模式
怎麼辦呢? 這正是裝飾者模式要解決的問題
如果我們搞個抽象類Decorator , 在Decorator內部持有Component接口,那麼Component原來的接口那Decorator肯定也能夠重寫,既然能重寫,重寫的時候 添加新功能 是不是能支撐?
當然了 Decorator抽象類 也可以定義一些抽象方法,讓子類去重寫
走, 試一把
【抽象的裝飾類】
/**
* 抽象類 Decorator
*/
public abstract class Decorator implements Component {
// 裝飾對象 裝飾Component 目的是爲了在Component上添加新功能
Component delegate ;
// 構造函數 (用set方法也可以) 綁定Decorator和Component的關係
public Decorator(Component delegate) {
this.delegate = delegate;
}
}
【具體的裝飾類-----美顏功能 】
public class MeiYanDecorator extends Decorator {
public MeiYanDecorator(Component delegate) {
super(delegate);
}
@Override
public void operation() {
// 添加新的功能
System.out.println("添加美顏功能");
// 調用原來的功能
delegate.operation();
}
}
【具體的裝飾類-----瘦臉功能 】
public class SouLianDecorator extends Decorator {
public SouLianDecorator(Component delegate) {
super(delegate);
}
@Override
public void operation() {
// 添加新功能
System.out.println("添加瘦臉功能");
// 原有功能調用
delegate.operation();
}
}
【測試】
public class Test {
public static void main(String[] args) {
// 基礎功能 拍照
Component component = new ConcreteComponent();
component.operation();
System.out.println("");
// 先來個美顏
Component meiyan = new MeiYanDecorator(component);
meiyan.operation();
System.out.println("");
// 美顏瘦臉 一塊上
Component meiyansoulian = new SouLianDecorator(new MeiYanDecorator(component));
meiyansoulian.operation();
// TODO 後面如果還需要 大眼 、表情包 ..... 各種組合
// 構造函數的入參是 Component 接口,所以只要實現了Component接口的類都可以
}
}
裝飾者模式就是利用 構造函數 (Set方法也可以)
public Decorator(Component delegate) {
this.delegate = delegate;
}
來對對象進行包裝的。 這樣的話,每個裝飾對象的實現就和如何使用這個對象分開了,每個裝飾對象只關心自己的功能,無需關心如何被天際到對象鏈當中。
就比如
new SouLianDecorator(new MeiYanDecorator(component));
MeiYanDecorator包裝 component,
SouLianDecorator 包裝 MeiYanDecorator
當然了,我們學習這些東西要學會變通,不是說一定要有Componet 接口 ,一定要有Decorator抽象類 。
如果只有一ConcreteComponet類,沒有 Component , 那麼Decorator類可以是ConcreteComponet的一個子類。
反之,如果只有一個ConcreteDecorator類,那麼就沒有必要建立一個單獨的Decorator抽象類,可以把ConcreteDecorator和Decorator合併成一個類。
裝飾者模式在MyBatis中的應用
MyBatis 的二級緩存 CachingExecutor 是在 一級緩存的基礎上,動態增加了二級緩存的功能,是個標準的 裝飾者模式。
小結
裝飾者模式爲已有功能動態的添加更多功能提供了一種比繼承更加靈活的方式。
當系統中需要添加新的功能時,向舊的代碼中添加新的代碼,這些新加的代碼和原有類的代碼摻雜在一塊,引入了不可控因素。
而裝飾者模式提供了另外一個解決防範,它把要裝飾的新的功能放在一個單獨的類中,並讓這個類包裝它要裝飾的對象,因此,當需要執行特殊行爲時,我們就可以有選擇的使用裝飾功能包裝對象了。
總之呢,優點如下
- 不改變原有的代碼的情況下對一個對象提供擴展功能
- 使用不同的裝飾可以組合出不同的效果
- 符合開閉原則
非要說缺點的話,可能就是代碼變多了一點