Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 設計模式-裝飾者模式(Decorator)理解和在Android中的應用

設計模式-裝飾者模式(Decorator)理解和在Android中的應用

編輯:關於Android編程

介紹

最近寫代碼沒有手感,就看看書找點寫代碼的靈感。看點高大上的設計模式談談自己的理解。我讀的是《研磨設計模式》看完之後真正的醍醐灌頂。借用一句話

一本值得反復研讀的書

讀了設計模式,其實我覺得看Android源碼不是一行一行的看,畢竟源碼的代碼太多,其實很多都是輸入檢查非空判斷異常處理等,如果只是關心的關鍵的實現步驟抓重點的話,就找到關鍵實現就好了。這話說得有點繞。我們怎麼知道什麼是重點。這就個人代碼功力還有就是設計模式的理解了。
個人功力就靠平常的開發經驗積累思維拓展。但是設計模式就可以看書學習啊。這裡強行安利,《研磨設計模式》真的神書看完醍醐灌頂。《Android源碼設計模式解析與實戰》個人覺得不如前者但是可以作為拓展,因為結合Android源碼分析更實際。

Android源碼和裝飾者模式

我在我的博客上-源碼分析-ListView組件addHeaderView()方法的源碼解析上分析了ListView的實現addHeadView的源碼。當時沒有看到裝飾者模式。只知道Google在對添加headview上包裝了一個類,而不是繼承一個類。覺得這樣寫很好實現代碼解耦。後來看完《研磨設計模式》的裝飾者模式篇才明白為什麼這寫。

裝飾者模式的定義:

動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾者模式比生成子類更靈活。

考慮到實際的代碼中。比如我們需要給listview添加一個能夠添加頭部視圖headView的功能。這就是需要動態地添加一些額外的職責。而且為了解耦和不影響現有代碼,最好是透明的。所謂透明就是給一個對象增加功能,但是不能讓這個對象知道,也就是不能去改動這個對象。
從代碼的角度看,給對象增加功能,就是拓展對象功能,就是繼承啊。這是網上大多數對RecyclerView添加headview時候的真實考慮和代碼邏輯實現。但是不透明。我們看到Android源碼ListView的addHeadView實現不是這樣的。因為繼承是非常不靈活的復用方式。這裡就需要看裝飾者模式是怎麼實現透明添加的。

裝飾者模式基本思路

在裝飾者模式的實現中,為了能夠實現和原來使用被裝飾對象的代碼無縫銜接,是通過定義一個抽象類,讓這個類實現與被裝飾對象相同的接口,然後在具體實現類中,轉調被裝飾的對象,在轉調前後添加新功能,這就實現了給被裝飾對象增加功能。上面這段話最關鍵的就是-轉調。而能轉調的實現是定義統一接口。

UML類圖描述

文字描述無力,用UML圖說明就清晰了
裝飾者模式的UML圖

說明:

Component:組件對象接口 ConcreteComponent:具體的組件對象,實現組件對象接口,通常就是被裝飾的原始對象。就對這個對象添加功能。 DecZ喎?/kf/ware/vc/" target="_blank" class="keylink">vcmF0b3Kjusv509DXsMrOxve1xLPpz/O4uMDgo6zQ6NKqtqjS5dK7uPbT69fpvP6907/a0rvWwrXEvdO/2qOsxNqyv7PW09DSu7j2Q29tcG9uZW50ttTP86Osvs3Kx7PW09DSu7j2sbvXsMrOtcS21M/zoaMNCjxwPkNvbnJldGVEZWNvcmF0b3JBL0NvbnJldGVEZWNvcmF0b3JCo7rKtbzKtcTXsMrOxve21M/zo6zKtc/Wvt/M5cztvNO5psTcoaM8L3A+DQo8cD7K7M+kVU1Mzby1xLv5sb6+zcP3sNfBy6OstavKx8/xztLV4tH5srvK7M+ktcS7ucrH0LTSu7XjztLDx8rsz6S1xLT6wuvD6Mr2sMmhozwvcD4NCjxoMiBpZD0="代碼描述">代碼描述

首先是組件對象接口的定義;

/**
 - Created by LiCola on  2016/04/22  15:58
 - 統一的組價對象接口
 */
public abstract class Component {
    //抽象方法 沒有具體操作
    public abstract void operation();
}

實現接口的對象

/**
 * Created by LiCola on  2016/04/22  16:00
 * 具體實現組件對象接口的對象 被裝飾的原始對象
 */
public class ConcreteComponent extends Component {

    @Override
    public void operation() {
        //重寫方法 做基本的操作  
        System.out.print(" ConcreteComponent operation\n");
    }
}

裝飾器的抽象父類

/**
 * Created by LiCola on  2016/04/22  16:04
 * 所有裝飾器的抽象父類 持有接口 轉發請求
 */
public abstract class Decorator extends Component {

    /**
     * 內部持有的組件接口對象
     */
    protected Component component;

    /**
     * 依賴注入
     * @param component 依賴的對象
     */
    public Decorator(Component component) {
        this.component = component;
    }

    /**
     * 轉發請求給組件對象 這裡可以做一些附加操作
     */
    @Override
    public void operation() {
        component.operation();
    }
}

某個具體實現例子

/**
 * Created by LiCola on  2016/04/22  16:08
 * 某個具體裝飾器實現對象,調用接口方法和具有自己的方法 並能夠附加上去
 */
public class ConcreteDecoratorA  extends Decorator{

    /**
     * 依賴注入
     *
     * @param component 依賴的對象
     */
    public ConcreteDecoratorA(Component component) {
        super(component);
    }

    public int operationA(){
        System.out.print(" ConcreteDecoratorA operationA\n");
        return 0;
    }

    @Override
    public void operation() {
        operationA();
        super.operation();
    }
}

以上代碼只說明層級關系和邏輯,沒有真實的操作。
客戶端調用代碼示例:

/**
 * Created by LiCola on  2016/04/22  16:21
 */
public class Client {
    public static void main(String[] args){
        Component component=new ConcreteComponent();

        Decorator decorator=new ConcreteDecoratorA(component);

        decorator.operation();
    }
}

輸出結果:
ConcreteDecoratorA operationA
ConcreteComponent operation

輸出結果只是打印兩行字符 但是意義重大。
要知道我們只是調用了Decorator的方法但是得到原來的基本操作ConcreteComponent的方法和新增的ConcreteDecoratorA的方法。就是給已經存在對象新增了功能而且沒有使用繼承,要知道Java的可是有單繼承限制。ConcreteDecoratorA只是一個具體實現,實際上我們可以定義很多的同樣繼承自Decorator類的子類添加功能方法,然後串聯起來。

裝飾者模式的功能

裝飾者模式實現的是從一個對象外部給對象添加功能,相當於改變了對象的外觀,裝飾過的對象,從外部系統來看已經不再是原來的對象,而是經過一系列裝飾器裝飾過的對象。
裝飾者模式最大的好處就是靈活,它能夠靈活的改變一個的對象的功能,並且是動態組合形式。另外好處就是代碼復用,因為每個裝飾器是獨立的,可以給一個對象多次增加同一個裝飾器,也可以同一個裝飾器裝飾不同對象。
在面向對象設計中:有一條基本規則

盡量使用對象組合,而不是對象繼承來擴展和復用功能。

另外說明:

各個裝飾器之間最好是完全獨立的功能,不要依賴,這樣在進行裝飾組合的時候,才沒有先後調用限制。否則會降低裝飾器組合的靈活性。

裝飾器模式的退化形式:
當僅僅只是添加一個功能,就沒有必要再設計裝飾器的抽象父類,直接在裝飾器中實現組件接口,然後實現相應的拓展功能。

Android源碼中存在的裝飾者模式

在Android源碼中,存在很多設計模式的實現。當理解了23個設計模式看源碼也會透徹得多。因為實際代碼有很多限制不會存在基本的設計模式,很多時候都是變形後的設計模式實現。

ListView的addHeadView

比如ListView的添加頭部和尾部視圖/addHeadView和addFootView功能就是裝飾者模式的運用。了解的MVC的都知道其實我們添加視圖影響的是adapter的方法調用。實際代碼的繼承關鍵非常復雜,我就簡略的畫出關鍵UML圖
ListView的UML

Adapter:接口對象,方法很多只畫一個關鍵的getView方法
MyBaseAdapter:是我們自動定義類繼承BaseAdapter(也就是Adapter省略無關繼承)用來綁定ListView顯示itemView的類,是具體的組件對象,被裝飾的原始對象。
HeaderViewListAdapter:退化形式的裝飾器,Decorator抽象父類和子類的集合,統一成實際的裝飾器對象,持有Adapter對象。
源碼解析在我的另外一篇博客就不詳細說明了。

Context的startActivity

Context類中也存在裝飾者模式的運用,大家看源碼中

abstract class Context是抽象類就是裝飾者模式的Component:組件對象接口。 而真正的實現是在ContextImpl中完成,它繼承自Context抽象類並實現抽象方法。是ConcreteComponent:具體的組件對象,實現組件對象接口,就是被裝飾的原始對象。 而Decorator所有裝飾器的抽象父類是ContextWrapper。從代碼上看是完全和裝飾者模式一樣的實現。內部持有一個Component對象,就是持有一個被裝飾的對象。
public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }

    //省略代碼 只看這個方法
   @Override
    public void startActivity(Intent intent) {
        mBase.startActivity(intent);
    }

最後誰是具體的裝飾器對象呢?其實就是我們的Activity、Service和Application這些能夠啟動Activity的實現類啊。只是我們一般沒有重寫startActivity()方法由系統調用了我們沒有注意到。

總結

設計模式在寫代碼中經常使用到,並且非常重要。有時只是我們使用了而沒有注意到它的存在。

我認為理解了設計模式再來看Android源碼才能透過現象看本質,沒有理解設計模式,看源碼時候會糾結在源碼的汪洋大海。理解了23種設計模式並能夠區分理解運用,看源碼就能從更高的層次理解。 全文是我在讀《研磨設計模式》之後結合Android實際代碼的寫的博客算是自己在學習的總結分析。之後還寫一些和設計模式有關的博客。 感謝《研磨設計模式》的作者,讀到他的書如醍醐灌頂,給我的編程之路指引了方向,解決了很多實際的問題和開拓的眼界。

最後附上豆瓣地址方便大家找書。UML圖的畫圖工具是Chrome的插件UML Diagram Editor。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved