?
前言
平時寫代碼的時候可能為了完成某一個任務而只是應付性地編碼咽袜,然后寫完理直氣壯地來一句"又不是不能用!"枕稀,但如果要把編碼當作一項藝術來打造询刹,那就需要結合我們的設計模式了。設計模式可以運用在諸多方面萎坷,讓我們的代碼更加優(yōu)雅凹联。設計模式在Android中也是無處不在,動態(tài)代理哆档、建造者蔽挠、工廠、適配器....等等等等瓜浸,可見它對我們的重要性澳淑。最近在看Retrofit的源碼,剛好看到了動態(tài)代理插佛,想著總結下這個模式杠巡。
?
什么是裝飾器模式?
裝飾器模式(Decorator Pattern)允許向一個現(xiàn)有的對象添加新的功能雇寇,同時又不改變其結構氢拥。這種類型的設計模式屬于結構型模式蚌铜,相當于對現(xiàn)有的類的一個包裝。
你可能會反駁嫩海,繼承不就可以對現(xiàn)有對象進行拓展了嗎冬殃?繼承是可以實現(xiàn)同樣效果,但是由于Java是單繼承叁怪,如果你多級包裝造壮,子類會越來越多,層級越來越深骂束,而利用裝飾器模式則可以優(yōu)雅解決這個問題耳璧。它動態(tài)地給一個對象添加一些額外的職責。就增加功能來說展箱,裝飾器模式相比繼承更為靈活旨枯。
?
如何實現(xiàn)裝飾器模式?
以繪制形狀為例混驰,我們平時繪制的形狀五花八門攀隔,可能有圓形、矩形栖榨、三角形...等等昆汹,我們抽取出一個抽象的形狀接口:
/**
* 形狀接口
*/
public interface Shape {
//定義公用的形狀繪制方法
void onDraw();
}
接下來,假如我們要繪制一個圓形婴栽,會實現(xiàn)Shape接口满粗,定義具體繪制圓形的方法:
/**
* 圓形類
*/
public class Circle implements Shape{
@Override
public void onDraw() {
Log.d("Decorator", "繪制一個圓形");
}
}
假如某一天,有一個特殊的需求愚争,需要繪制一個帶邊框的圓形映皆,但又不允許更改Circle類,如果是按繼承寫法會是如下:
/**
* 帶邊界的圓形
*/
public class BorderCircle extends Circle{
@Override
public void onDraw() {
super.onDraw();
Log.d("Decorator","繪制一條邊界");
}
}
效果是實現(xiàn)了轰枝,也沒出現(xiàn)啥問題捅彻,等到某一天,又來了個需求鞍陨,要繪制一個帶邊界的漸變圓步淹,且依然不能修改原來的類,你也許會想诚撵,那就繼續(xù)繼承BorderCircle
就好了......如此反復循環(huán)缭裆,繼承的具體形狀類會越來越多谊迄,且可能會嵌套了很多層,不利于代碼的維護考传。
我們將以上改為裝飾器模式來實現(xiàn)的話殿托,可以先定義一個抽象裝飾者來裝飾Shape:
/**
* 抽象裝飾者類,對Shape接口進行包裝
*/
public class ShapeDecorator implements Shape{
private Shape shape;
public ShapeDecorator(Shape shape) {
this.shape = shape;
}
@Override
public void onDraw() {
shape.onDraw();
}
}
然后假如是實現(xiàn)一個帶邊界的圓形其弊,就是如下寫法:
/**
* 帶邊界的圓形
*/
public class BorderCircle extends ShapeDecorator{
public BorderCircle(Shape shape) {
//調用父類的構造方法霍骄,將參數(shù)傳給父類
super(shape);
}
@Override
public void onDraw() {
//調用父類的onDraw
super.onDraw();
Log.d("Decorator","繪制一條邊界");
}
}
假如想要加個漸變群嗤,就不需要再進一步繼承于BorderCircle
了畅铭,而是依舊繼承于ShapeDecorator
:
/**
* 帶邊界的漸變圓形
*/
public class GradientBorderCircle extends ShapeDecorator{
public GradientBorderCircle(Shape shape) {
super(shape);
}
@Override
public void onDraw() {
super.onDraw();
Log.d("Decorator", "繪制漸變");
}
}
最終客戶端調用姿勢如下:
Shape circle = new Circle();
//繪制一個帶邊界的圓形
ShapeDecorator borderCircle = new BorderCircle(circle);
borderCircle.onDraw();
//繪制一個帶邊界且漸變的圓形
ShapeDecorator gradientBorder = new GradientBorderCircle(borderCircle);
gradientBorder.onDraw();
?
Context中的裝飾器模式
Android源碼中也有很多地方用到了這種設計思想氏淑,最經典的Context就使用了這種模式,Context對于我們來說都不陌生硕噩,它代表了當前的上下文對象假残,它本身是一個抽象類:
它有一個子類
ContextWrapper
,可以看到它持有Context
對象炉擅,并且在構造方法中賦值辉懒,是不是類似于上面例子中的抽象裝飾者類:而ContextWrapper的子類就有我們最熟悉的Activity谍失、Service眶俩、Application等,它們都相當于Context的具體裝飾者快鱼,各自有各自的特點颠印。
那么問題來了,相比剛才的例子好像還缺一個角色沒有提到——具體被裝飾者類抹竹,剛才已經分析到Application
线罕、 Activity
這些都是具體裝飾者,那么它持有的這個Context對象是哪里傳進來的呢窃判?我們以Application為例钞楼,探究下Application中調用attachBaseConetxt
的地方:
/* package */
final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
在Application
的attach
方法中調用了attachBaseContext,我們繼續(xù)往上跟蹤袄琳,看下哪里調用了attach
:
static public Application newApplication(Class<?> clazz, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = (Application)clazz.newInstance();
app.attach(context);
return app;
}
Instrumentation
類創(chuàng)建了Application窿凤,并且將context綁定給它,那么這個newApplication
又是哪里調用的呢跨蟹?
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
Application app = null;
try {
//....忽略部分代碼
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
//....忽略部分代碼
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
//....忽略部分代碼
return app;
}
到這一步雳殊,可以看到原來傳遞進去的 Context
對象是一個 ContextImpl
類型,那么 ContextImpl
極有可能就是我們的具體被裝飾者窗轩,我們驗證一下夯秃,點擊去可以看到 ContextImpl
確實是繼承于 Context
的:
public final class ContextImpl extends Context {
它們的關系圖如下:
ContextWrapper中實現(xiàn)Context的方法全是通過它持有的mBase對象來實現(xiàn)的。這樣它(ContextWrapper)派生出的子類就可以在不改變原始context(mBase對象)的情況下擴展Context的行為痢艺。 無論是
Activity
仓洼、Service
還是Application
,它們本質都是一個 ContextWrapper
堤舒,都是Context
的一個具體裝飾者的角色色建,都是通過持有 ContextImpl
對象,在 ContextImpl
的基礎上進一步拓展各自的特色功能和邏輯舌缤。?
總結
裝飾模式降低了系統(tǒng)的耦合度箕戳,可以動態(tài)增加或刪除對象的職責某残,并使得需要裝飾的具體構件類和具體裝飾類可以獨立變化,以便增加新的具體構件類和具體裝飾類陵吸。裝飾模式應用較為廣泛玻墅,不止Android,例如在 JavaIO 中的輸入流和輸出流的設計也同樣運用了該模式壮虫。當不能采用繼承的方式對系統(tǒng)進行擴展或者采用繼承不利于系統(tǒng)擴展和維護時可以使用裝飾器模式澳厢。
?