Design Patterns in Android:裝飾模式

MichaelX發表於2019-02-27

裝飾模式是幹嘛的呢?它在專案中有哪些用途呢?裝飾模式在Android原始碼中有哪些應用呢?本文將和讀者朋友一起分享探討裝飾者模式在Android中的應用和實踐。

本文原創作者MichaelX。 
CSDN部落格:https://blog.csdn.net/xiong_it 
掘金主頁:https://juejin.im/user/56efe6461ea493005565dafd 
知乎專欄:https://zhuanlan.zhihu.com/c_144117654 
個人部落格:http://blog.michaelx.tech 
複製程式碼

轉載請註明出處。

裝飾模式定義

裝飾者模式:也叫wrapper模式。動態地給一個物件新增一些額外的職責,就增加功能來說,裝飾者模式相比生成子類更加靈活,提供了有別於繼承的另一種選擇。

裝飾模式的UML類圖

這裡寫圖片描述

有四個角色需要說明:

  • Component抽象構件
    Component是一個介面或者是抽象類,就是定義我們最核心的物件,也就是最原始的物件,如上面的成績單。

**注意:**在裝飾模式中,必然有一個最基本、最核心、最原始的介面或抽象類充當Component抽象構件。

  • ConcreteComponent 具體構件
    ConcreteComponent是最核心、最原始、最基本的介面或抽象類的實現,你要裝飾的就是它。

  • Decorator裝飾角色
    一般是一個抽象類,做什麼用呢?實現介面或者抽象方法,它裡面可不一定有抽象的方法呀,在它的屬性裡必然有一個private變數指向Component抽象構件。

  • 具體裝飾角色
    ConcreteDecoratorA和ConcreteDecoratorB是兩個具體的裝飾類,你要把你最核心的、最原始的、最基本的東西裝飾成其他東西。

裝飾模式示例程式碼

public abstract class Component {
     //抽象的方法
     public abstract void operate();
}
複製程式碼
public class ConcreteComponent extends Component {
     //具體實現
     @Override
     public void operate() {
             System.out.println("do Something");
     }
}
複製程式碼
public abstract class Decorator extends Component {
     private Component component = null;        
     //通過建構函式傳遞被修飾者
     public Decorator(Component _component){
             this.component = _component;
     }
     //委託給被修飾者執行
     @Override
     public void operate() {
             this.component.operate();
     }
}
複製程式碼
public class ConcreteDecorator1 extends Decorator {
     //定義被修飾者
     public ConcreteDecorator1(Component _component){
             super(_component);
     }
     //定義自己的修飾方法
     private void method1(){
             System.out.println("method1 修飾");
     }
     //重寫父類的Operation方法
     public void operate(){
             this.method1();
             super.operate();
     }
}
複製程式碼
public class ConcreteDecorator2 extends Decorator {
     //定義被修飾者
     public ConcreteDecorator2(Component _component){
             super(_component);
     }
     //定義自己的修飾方法
     private void method2(){
             System.out.println("method2修飾");
     }
     //重寫父類的Operation方法
     public void operate(){
             super.operate();
             this.method2();
     }
}
複製程式碼

使用場景類

public class Client {
     public static void main(String[] args) {
             Component component = new ConcreteComponent();
             //第一次修飾
             component = new ConcreteDecorator1(component);
             //第二次修飾
             component = new ConcreteDecorator2(component);
             //修飾後執行
             component.operate();
     }
}
複製程式碼

Android原始碼中的裝飾模式

案例一

Context是Android中一個幾乎無處不在的角色,ContextWrapper/ContextThemeWrapper就在繼承過程中承擔了ContextImpl的裝飾者角色。

這裡寫圖片描述

ContextThemeWrapper部分程式碼為例:

public class ContextThemeWrapper extends ContextWrapper {
    private int mThemeResource;
    private Resources.Theme mTheme;
    private LayoutInflater mInflater;
    private Configuration mOverrideConfiguration;
    private Resources mResources;

    @Override
    public AssetManager getAssets() {
        // Ensure we`re returning assets with the correct configuration.
        return getResourcesInternal().getAssets();
    }

    @Override
    public Resources getResources() {
        return getResourcesInternal();
    }

    private Resources getResourcesInternal() {
        if (mResources == null) {
            if (mOverrideConfiguration == null) {
                mResources = super.getResources();
            } else {
                final Context resContext = createConfigurationContext(mOverrideConfiguration);
                mResources = resContext.getResources();
            }
        }
        return mResources;
    }

    @Override
    public Resources.Theme getTheme() {
        if (mTheme != null) {
            return mTheme;
        }

        mThemeResource = Resources.selectDefaultTheme(mThemeResource,
                getApplicationInfo().targetSdkVersion);
        initializeTheme();

        return mTheme;
    }

    @Override
    public Object getSystemService(String name) {
        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
            if (mInflater == null) {
                mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
            }
            return mInflater;
        }
        return getBaseContext().getSystemService(name);
    }

    protected void onApplyThemeResource(Resources.Theme theme, int resId, boolean first) {
        theme.applyStyle(resId, true);
    }

    private void initializeTheme() {
        final boolean first = mTheme == null;
        if (first) {
            mTheme = getResources().newTheme();
            final Resources.Theme theme = getBaseContext().getTheme();
            if (theme != null) {
                mTheme.setTo(theme);
            }
        }
        onApplyThemeResource(mTheme, mThemeResource, first);
    }
}
複製程式碼

案例二

還有一個比較典型的例子是RecyclerView通過RecyclerView.ItemDecorator來擴充套件樣式。
不過這個是一個變種的裝飾者,這個實踐比較另類的地方在於:我們通常是在裝飾者的的執行方法中擴充套件被代理物件的行為,而RecyclerView+ItemDecorator的實踐則恰恰相反,ItemDecorator反倒成了被代理物件,RecyclerView成了裝飾者。

Android開發中的裝飾模式實踐

說實話,筆者自己也沒有實踐過裝飾模式,但是有一個場景需求應該是可以應用裝飾模式的。比如一個直播場景,點選禮物時需要禮物飛出來,雙擊有一個愛心❤️飄出來,那麼禮物和愛心就可以看成是直播畫面的裝飾者,類關係如下:

這裡寫圖片描述

GirlView:主播畫面
GirlDecorator:主播畫面裝飾者
GiftView:禮物效果
LoveView:愛心效果

總結

特點:裝飾模式其實就是在代理某個物件過程中,給特定的代理行為前後加上不同的裝飾行為,比如文中的ContextThemeWrapper就在代理ContextImplgetSystemService這個行為過程中,加上了返回LayoutInflater這個裝飾行為。因此,我們也可以認為裝飾模式其實是一種特殊的代理模式

裝飾模式的優缺點
優點:

  • 裝飾類和被裝飾類可以獨立發展,而不會相互耦合。換句話說,Component類無須知道Decorator類,Decorator類是從外部來擴充套件Component類的功能,而Decorator也不用知道具體的構件。

  • 裝飾模式是繼承關係的一個替代方案。我們看裝飾類Decorator,不管裝飾多少層,返回的物件還是Component,實現的還是is-a的關係。

  • 裝飾模式可以動態地擴充套件一個實現類的功能,這不需要多說,裝飾模式的定義就是如此。

缺點:

  • 裝飾太多層時會增加系統複雜度,有時出現問題可能無法快速定位。

當某個物件的行為需要加強,並且可能有多種加強的需求時,那麼裝飾模式有可能就能排上用場了。

好了,今天的《設計模式Android篇:裝飾模式》就到這裡,請繼續關注《Design Patterns in Android》(設計模式Android篇)系列博文,歡迎各位讀者朋友評論區拍磚交流,共同進步。

相關文章