裝飾模式是幹嘛的呢?它在專案中有哪些用途呢?裝飾模式在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
就在代理ContextImpl
的getSystemService
這個行為過程中,加上了返回LayoutInflater這個裝飾行為。因此,我們也可以認為裝飾模式其實是一種特殊的代理模式
裝飾模式的優缺點
優點:
-
裝飾類和被裝飾類可以獨立發展,而不會相互耦合。換句話說,Component類無須知道Decorator類,Decorator類是從外部來擴充套件Component類的功能,而Decorator也不用知道具體的構件。
-
裝飾模式是繼承關係的一個替代方案。我們看裝飾類Decorator,不管裝飾多少層,返回的物件還是Component,實現的還是is-a的關係。
-
裝飾模式可以動態地擴充套件一個實現類的功能,這不需要多說,裝飾模式的定義就是如此。
缺點:
- 裝飾太多層時會增加系統複雜度,有時出現問題可能無法快速定位。
當某個物件的行為需要加強,並且可能有多種加強的需求時,那麼裝飾模式有可能就能排上用場了。
好了,今天的《設計模式Android篇:裝飾模式》就到這裡,請繼續關注《Design Patterns in Android》(設計模式Android篇)系列博文,歡迎各位讀者朋友評論區拍磚交流,共同進步。