PS 轉載請註明出處
做者:TigerChain「公號 TigerChain」
地址:juejin.im/post/5a3655…
掘金:juejin.im/user/568bd2…java
教程簡介android
正文git
一、穿衣服程序員
這個模式每一個人都知道,咱們每天穿衣服「有的人每天換衣服」,這就是一個裝飾模式,裝飾誰呢?固然是本身了。男人讓本身變得更加有精氣神,女人讓本身變得更加靚麗有氣質,無形中給人一種附加的"能力"「吸引人」github
二、房子裝修app
咱們買的房子要裝修了,能夠裝成高端大氣上檔次的,也能夠裝成低調奢華有內涵的,還能夠裝成簡易風格、歐美風格等等,房子仍是原來的房子,可是經過裝修咱們賦予了房子不一樣的體現風格--這就是裝飾模式ide
三、禮品盒,包裝盒源碼分析
一樣的產品不一樣的包裝賣的價格不同,以蘋果爲例,通常的蘋果一斤斤的賣,禮品盒的蘋果按個數賣價格是沒有包裝的幾倍,包裝經濟下的利潤都是很是可觀的,其實咱們作爲程序員也是這個道理,若是本身有開源項目、有高仿問量的博客、有知名度「都是對本身的包裝」,那麼本身的身價也不低「學完裝飾模式趕忙給本身鍍金吧」post
四、孫悟空煉就火眼金睛學習
咱們都知道猴子被放到八卦爐裏煉了七七四十九天,沒有想到猴子沒有死,反到變成火眼金睛了,這無形之中又用到了裝飾模式「猴子仍是原來的猴子但是多了一項技能」
裝飾模式的定義
在不改變原有對象的基礎之下給原有對象擴展功能,是對繼承關係的一種替換方案,裝飾模式也叫包裝器模式「Wrapper Pattern」
裝飾模式的目的
定義裏說的很清楚了就是擴展原有對象的功能「不改變原有對象」 固然在對原有對象不改變的前提下擴展對象的功能可不止這一種作法:一般的作法會有
動態代理應該算是最好的一種解決方案,可是還有有區別的,具體下面會說
裝飾模式的結構
角色 | 類別 | 說明 |
---|---|---|
Component | 抽象角色「抽象類或接口」 | 抽象的構件對象,真實對象和裝飾對象都實現此相同的接口,不是必須的 |
ConcreteComponent | 具體的構件角色 | 即被裝飾的對象能夠有多個「由於被裝飾的對象能夠有多個」 |
Decorator | 裝飾角色 | 持有一個抽象構件的引用,而且把客戶端的請求轉發給真實的對象,起到擴展真實對象的功能,不是必須的 |
ConcreteDecorator | 具體的裝飾角色 | 負責給構件對象擴展功能 |
裝飾模式簡單的 UML
裝飾者類裝飾的對象是具體的構件類
一、人穿衣服
人靠衣裝馬靠鞍,說的就是包裝對一我的來講很是重要。固然男人有男人的裝扮風格,女人就女人的裝扮風格,若是是一個明星再穿個個性的衣服通常就會達到炒做的目的--結果可想而知身價倍增,那麼對於咱們日常人來講,穿着也很是重要,好比你面視的時候,穿着得體映象分就會大大提升,可見裝飾模式對咱們有多麼主要呀
人穿衣服簡單的 UML
根據 UML 擼碼
/** * Created by TigerChain * 抽象的構件--人 */
public interface Person {
// 取得搭配的衣服
String getCloths() ;
}
複製代碼
/** * Created by TigerChain * 具體的構件 -- TigerChain 即被裝飾的對象 */
public class TigerChain implements Person {
@Override
public String getCloths() {
String selectResult = "TigerChain 搭配衣服:" ;
return selectResult;
}
}
複製代碼
/** * Created by TigerChain * 上衣,抽象的裝飾者,被裝飾的對象是人 */
public interface IOuter extends Person{
String getOuter() ;
}
複製代碼
/** * Created by TigerChain * 上衣具體的裝飾者---西服 */
public class Suit implements IOuter {
private Person person ;
// 這裏體現出裝飾「即包裝,把被裝飾的對象包起來」
public Suit(Person person){
this.person = person ;
}
@Override
public String getCloths() {
return person.getCloths()+"西裝";
}
@Override
public String getOuter() {
return "---上衣 ";
}
}
複製代碼
/** * Created by TigerChain * 具體的上衣裝飾者,皮夾克 */
public class Jacket implements IOuter {
// 被包裝的抽象類
private Person person ;
public Jacket(Person person){
this.person = person ;
}
@Override
public String getCloths() {
return person.getCloths()+" 皮夾克";
}
@Override
public String getOuter() {
return "---上衣";
}
}
複製代碼
/** * Created by TigerChain * 抽象的裝飾者--褲子 */
public interface ITrousers {
String getCloths() ;
}
複製代碼
/** * Created by TigerChain * 褲子的具體裝飾者--西褲 */
public class Pants implements ITrousers {
private Person person ;
public Pants(Person person){
this.person = person ;
}
@Override
public String getCloths() {
return person.getCloths()+"西褲";
}
}
複製代碼
/** * Created by TigerChain * 具體的褲子裝飾者--牛仔褲 */
public class Jean implements ITrousers {
private Person person ;
public Jean(Person person){
this.person = person ;
}
@Override
public String getCloths() {
return person.getCloths()+"牛仔褲";
}
}
複製代碼
/** * Created by TigerChain * 測試類 */
public class Test {
public static void main(String args[]){
Person person = new TigerChain() ;
System.out.println("方式一:全身西裝");
// 上衣西裝
IOuter suit = new Suit(person) ;
System.out.println(suit.getCloths()+suit.getOuter());
// 褲子西褲
ITrousers pants = new Pants(person) ;
System.out.println(pants.getCloths());
System.out.println("方式二:皮夾克+牛仔褲");
// 上衣皮夾克
IOuter jacket = new Jacket(person) ;
System.out.println(jacket.getCloths()+jacket.getOuter());
// 褲子牛仔褲
ITrousers jean = new Jean(person) ;
System.out.println(jean.getCloths());
}
}
複製代碼
怎麼樣,我仍是我沒有改變,只是換個皮而已,沒什麼大不了的。從結果來看,我我的仍是比較土的「不會搭配衣服 ^_^」
二、裝修房子
當初我裝修房子的時候--裝修公司給咱們介紹他們有兩種裝修風格:簡約和歐美風格,問我要什麼風格的,做爲屌絲的我選擇了簡約風格「其實不喜歡歐美風格,感受越簡單越好,主要仍是手頭沒有銀子」
這裏被裝飾的對象就是房子、裝修風格就是裝飾者,咱們來看看 UML 吧
裝修房子簡單的 UML
根據 UML 擼碼
/** * Created by TigerChain * 抽象的構件房子 */
public abstract class AbstractHouse {
// 取得房子的風格
abstract String getCategory() ;
}
複製代碼
/** * Created by TigerChain * 具體的構件1 簡易房子 */
public class JianYi extends AbstractHouse {
@Override
String getCategory() {
return "簡易風格的房子";
}
}
複製代碼
/** * Created by TigerChain * 具體的構件2 歐美風格房子 */
public class OuMei extends AbstractHouse {
@Override
String getCategory() {
return "歐美風格的房子";
}
}
複製代碼
/** * Created by TigerChain * 抽象的裝飾者 */
public abstract class AbstractDecorator extends AbstractHouse{
// 返回裝修價格
abstract String getCost() ;
}
複製代碼
/** * Created by TigerChain * 具體的裝飾者之一:簡易裝修 */
public class DiDiaoDecorator extends AbstractDecorator {
private AbstractHouse abstractHouse ;
public DiDiaoDecorator(AbstractHouse abstractHouse){
this.abstractHouse = abstractHouse ;
}
@Override
String getCategory() {
return this.abstractHouse.getCategory() + " 低調奢華有內涵";
}
@Override
String getCost() {
return " 8 萬元裝修";
}
}
複製代碼
/** * Created by TigerChain * 具體的裝飾者--高端裝修 */
public class GaoDuanDecorator extends AbstractDecorator {
private AbstractHouse abstractHouse ;
public GaoDuanDecorator(AbstractHouse abstractHouse){
this.abstractHouse = abstractHouse ;
}
@Override
String getCategory() {
return abstractHouse.getCategory()+" 高端大氣上檔次";
}
@Override
String getCost() {
return " 10 萬元裝修";
}
}
複製代碼
public class Test {
public static void main(String args[]){
AbstractHouse jianYiHouse = new JianYi() ;
AbstractDecorator diDiaoDecorator = new DiDiaoDecorator(jianYiHouse);
System.out.println(diDiaoDecorator.getCategory()+diDiaoDecorator.getCost());
AbstractHouse ouMei = new OuMei() ;
AbstractDecorator gaoDuanDecorator = new GaoDuanDecorator(ouMei) ;
System.out.print(gaoDuanDecorator.getCategory()+gaoDuanDecorator.getCost());
}
}
複製代碼
其實這裏的 AbstractHouse 不是必須的,咱們能夠定義一個 House 類,而後包裝它便可「這裏爲了演示包裝器模式的所有角色,因此在實際中能夠靈活設計」
三、懸浮的 ExpandableListView
咱們都知道 ExpandableListView 是實現分組列表的,它自己是不支持組頭懸浮功能的,爲了達到這一目的,咱們要重寫 adapter 功能 ExpandableListView 的滾動功能。實現相關功能的三方類庫很是多,我這裏選擇一個叫 FloatingGroupExpandableListView 的三方庫,它使用裝飾模式來實現懸浮組頭的功能,咱們來看看吧,咱們直接以 FloatingGroupExpandableListView 的 demo 爲例來講
先看看 本Demo 地運行結果
WrapperExpandableListAdapter 簡單的 UML
FloatingGroupExpandableListView 的使用方式
<com.diegocarloslima.fgelv.lib.FloatingGroupExpandableListView android:id="@+id/my_list" android:layout_width="match_parent" android:layout_height="match_parent"/>
複製代碼
FloatingGroupExpandableListView myList = (FloatingGroupExpandableListView) findViewById(R.id.my_list);
// 本身定義的 BaseExpandableListAdapter
BaseExpandableListAdapter myAdapter = new MyAdapter();
//看到了吧,從名字能夠看到這是一個包裝器,用來包裝 ExpandableListAdapter 的
WrapperExpandableListAdapter wrapperAdapter = new WrapperExpandableListAdapter(myAdapter);
// 這裏把包裝 adapter 傳進去就達到了懸浮組頭的目的
myList.setAdapter(wrapperAdapter);
複製代碼
怎麼樣,原來的 BaseExpandableListAdapter 壓根不用修改就達到擴展的目的,很爽吧,固然當配合 FloatingGroupExpandableListView「擴展瞭解ExpandableListView 」 來使用
具體代碼
具體的代碼我就不貼了,我上傳到 github 上了,你們可自行下載去查看,Demo 地址:github.com/githubchen0… 的 wrapperExpandableListAdapter 部分
若是想看 FloatingGroupExpandableListView 的源碼,包括 WrapperExpandableListAdapter 是如何實現的,咱們能夠扒扒它的源碼:地址是:github.com/diegocarlos… 建議親看扒扒這部分源碼來學習一下
一、ContextWrapper
Context 咱們作 Android 的基本上每天和它打交道,啓動 Activity ,發送廣播,啓動服務等,Content 就用到了包裝器模式,那是一個叫作 ContentWrapper 的東東
ContentWrapper 簡單的 UML
簡單的源碼分析
咱們能夠看到 ContextWrapper
就是對 Context
的一個包裝,沒有什麼好說的,其實咱們看 UML 就知道 ContextWrapper
實際上是持有 ContextImpl
一個引用的,因爲 ContextWrapper
就上面兩個方法來接收 Context
的,因此接收的確定是 ContextImpl
,咱們接着看 ContextImpl
是如何被傳遞到 ContentWrapper
的,咱們看看 ActivityThread
部分源碼
ContextImpl
傳遞給 mBase 了,因此 Activity 就能夠任性的調用 Context 的相關方法了
Why?
爲何 Andorid 系統要把 Content 使用包裝器包裝一下呢?咱們試想一下,Activity、Application、Service 等等都要使用 Context ,假使沒有 ContentWrapper ,那麼咱們要使用 Context 只能是繼承自 ContextImpl「或者直接繼承 Context」,那麼若是 Context 要修改「或者有一個新的 ContextImplB 方式是更好的」,會致使 Activity、Application、Service 都要修改「或從新繼承 ContextImplB」,這無疑是噁心的。而有了 ContextWrapper ,假若有一個新的 Context 變種,咱們直接將所包裝的對象替換掉便可就是這麼方便和任性
二、 android.hardware.camera2.utils.Decorator
感興趣的能夠扒扒這部分源碼,一樣使用的裝飾模式
/*** This is an implementation of the 'decorator' design pattern using Java's proxy mechanism. See also: newInstance(java.lang.Object,android.hardware.camera2.utils.Decorator.DecoratorListener) Hide: ** */
public class Decorator<T> implements InvocationHandler {
public interface DecoratorListener {
void onBeforeInvocation(Method m, Object[] args);
... 省略若干代碼
}
... 省略若干代碼
@SuppressWarnings("unchecked")
public static<T> T newInstance(T obj, DecoratorListener listener) {
return (T)java.lang.reflect.Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
new Decorator<T>(obj, listener));
}
... 省略若干代碼
}
複製代碼
從註釋就能夠看出使用的裝飾模式
一、 透明
具體構件角色、裝飾角色的接口和抽象構件的接口徹底一致,則稱爲透明模式,這是一種極端的例子不太常見,因爲裝飾模式就是爲了在不改變接口的前提下---擴展對象,擴展確定就要對外公開一些本身的方法,因此不可能接口徹底一致
二、 半透明
若是裝飾角色的接口與抽象構件角色不一致,通常狀況下裝飾角色擴展一些本身的方法,也就是上面咱們廣泛的例子
優勢
缺點
適配器模式:把一個類的接口變成客戶所指望的另外一個接口,從而使本來因接口不匹配的而沒法運行的兩個類一塊兒工做,基本上把原的接口都重寫成知足本身的接口了,而裝飾模式只是對對象行爲功能的增長「不改變接口」
動態代理:是使用代理控制對對象仿問「是控制仿問」
裝飾模式:動態的新增或組合對象的行爲「是爲對象的行爲的增強」
PS: 文中的 Android 源碼均到自 Android Api 26
到此爲止,咱們的裝飾模式「包裝器模式」就介紹完了,伸出你的小手給個贊吧
之後文章會第一時間發在公號,請你們添加博文的公號,掃描添加便可關注 公衆號:TigerChain