對于開發人員來說,設計模式有時候就是一道坎,但是設計模式又非常有用,過了這道坎,它可以讓你水平提高一個檔次。而在android開發中,必要的了解一些設計模式又是必須的,因為設計模式在Android源碼中,可以說是無處不在。對于想系統的學習設計模式的同學,這里推薦一本書,《大話設計模式》。
Android常用設計模式系列:
面向對象的基礎特征
面向對象的設計原則
單例模式
模板模式
適配器模式
工廠模式
代理模式
原型模式
策略模式
Build模式
觀察者模式
裝飾者模式
中介模式
門面模式
代理模式
裝飾者模式是非常常見的設計模式之一,寫個筆記,記錄一下我的學習過程和心得。
首先了解一些裝飾模者式的定義。
動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾模式相比生成子類更為靈活。
- 裝飾者模式屬于結構型模式。
- 裝飾者模式在生活中應用實際上也非常廣泛,一如一間房,放上廚具,它就是廚房;放上床,就是臥室。
- 通常我們擴展類的功能是通過繼承的方式來實現,但是裝飾者模式是通過組合的方式來實現,這是繼承的替代方案之一。
涉及角色及說明:
Component(抽象組件):接口或者抽象類,被裝飾的最原始的對象。具體組件與抽象裝飾角色的父類。
ConcreteComponent(具體組件):實現抽象組件的接口。
Decorator(抽象裝飾角色):一般是抽象類,抽象組件的子類,同時持有一個被裝飾者的引用,用來調用被裝飾者的方法;同時可以給被裝飾者增加新的職責。
ConcreteDecorator(具體裝飾類):抽象裝飾角色的具體實現。
就以裝修房間為例子,講一下實現。
1 創建抽象組件
這里是一個抽象房子類,定義一個裝修的方法:
public abstract class Room {
public abstract void fitment();//裝修方法
}
2 創建具體組件
現在有一間新房子,已經裝上了電:
public class NewRoom extends Room {//繼承Room
@Override
public void fitment() {
System.out.println("這是一間新房:裝上電");
}
}
3 創建抽象裝飾角色
要為房子裝修,定義抽象的房間裝飾類:
public abstract class RoomDecorator extends Room {//繼承Room,擁有父類相同的方法
private Room mRoom;//持有被裝飾者的引用,這里是需要裝修的房間
public RoomDecorator(Room room) {
this.mRoom = room;
}
@Override
public void fitment() {
mRoom.fitment();//調用被裝飾者的方法
}
}
4 創建具體裝飾類
我們要將房間裝修成臥室和廚房,其具體實現是不同的:
public class Bedroom extends RoomDecorator {//臥室類,繼承自RoomDecorator
public Bedroom(Room room) {
super(room);
}
@Override
public void fitment() {
super.fitment();
addBedding();
}
private void addBedding() {
System.out.println("裝修成臥室:添加臥具");
}
}
public class Kitchen extends RoomDecorator {//廚房類,繼承自RoomDecorator
public Kitchen(Room room) {
super(room);
}
@Override
public void fitment() {
super.fitment();
addKitchenware();
}
private void addKitchenware() {
System.out.println("裝修成廚房:添加廚具");
}
}
5 客戶端測試:
public void test() {
Room newRoom = new NewRoom();//有一間新房間
RoomDecorator bedroom = new Bedroom(newRoom);
bedroom.fitment();//裝修成臥室
RoomDecorator kitchen = new Kitchen(newRoom);
kitchen.fitment();//裝修成廚房
}
輸出結果:
這是一間新房:裝上電
裝修成臥室:添加臥具
這是一間新房:裝上電
裝修成廚房:添加廚具
廣泛應用
我們都知道Activity
、Service
、Application
等都是一個Context
,這里面實際上就是通過裝飾者模式來實現的。下面以startActivity()
這個方法來簡單分析一下。
1 Context類
Context
實際上是個抽象類,里面定義了大量的抽象方法,其中就包含了startActivity()
方法:
public abstract class Context {//抽象類
public abstract void startActivity(@RequiresPermission Intent intent);//抽象方法
//其他代碼略
}
2 ContextImpl類
Context
類的具體實現實際上是在ContextImpl
類,里面具體實現了startActivity()
方法:
class ContextImpl extends Context {
@Override
public void startActivity(Intent intent) {
warnIfCallingFromSystemProcess();
startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, Bundle options) {//具體實現原理這里就不分析了
warnIfCallingFromSystemProcess();
// Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
// generally not allowed, except if the caller specifies the task id the activity should
// be launched in.
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
&& options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity) null, intent, -1, options);
}
//其他代碼略
}
3 ContextWrapper類
通常我們在Activity
、Service
里面調用startActivity()
方法,實際上是調用他們的父類ContextWrapper
里面的startActivity()
方法,我們先來看下Activity
、Service
的繼承關系:
可以看到Activity
、Service
都是繼承自ContextWrapper
,再來看看ContextWrapper
的代碼:
public class ContextWrapper extends Context {//Context包裝類
Context mBase;//持有Context引用
public ContextWrapper(Context base) {//這里的base實際上就是ContextImpl
mBase = base;
}
@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent);//調用ContextImpl的startActivity()方法
}
//其他代碼略
}
4 總結
Context
類在這里就充當了抽象組件的角色,ContextImpl
類則是具體的組件,而ContextWrapper
就是具體的裝飾角色,通過擴展ContextWrapper
增加不同的功能,就形成了Activity
、Service
等子類。最后,放一張總的UML類圖幫助理解:
總結
裝飾模式 VS 繼承
- 裝飾模式:對于一個給定的對象,同時可能有不同的裝飾對象,客戶端可以通過它的需要選擇合適的裝飾對象發送消息。
- 繼承:對于所有可能的聯合,客戶期望很容易增加任何的 困難
裝飾模式 | 繼承 |
---|---|
用來擴展特定對象的功能 | 用來擴展一類對象的功能 |
不需要子類 | 需要子類 |
動態地 | 靜態地 |
運行時分配職責 | 編譯時分派職責 |
防止由于子類而導致的復雜和混亂 | 導致很多子類產生,在一些場合,報漏類的層次 |
更多的靈活性 | 缺乏靈活性 |
總結一下裝飾者模式的優缺點
優點
采用組合的方式,可以動態的擴展功能,同時也可以在運行時選擇不同的裝飾器,來實現不同的功能。
有效避免了使用繼承的方式擴展對象功能而帶來的靈活性差,子類無限制擴張的問題。
被裝飾者與裝飾者解偶,被裝飾者可以不知道裝飾者的存在,同時新增功能時原有代碼也無需改變,符合開放封閉原則。
缺點
裝飾層過多的話,維護起來比較困難。
如果要修改抽象組件這個基類的話,后面的一些子類可能也需跟著修改,較容易出錯。
適用場景
需要擴展一個類的功能,或給一個類增加附加功能時
需要動態的給一個對象增加功能,這些功能可以再動態的撤銷
當不能采用繼承的方式對系統進行擴充或者采用繼承不利于系統擴展和維護時。