装饰者模式及其应用

217 阅读10分钟
原文链接: mp.weixin.qq.com

本篇博客主要讲解一下几个问题

  1. 什么 是装饰者模式

  2. 怎样实现装饰者模式

  3. 装饰者模式的优缺点

  4. 装饰者模式在Android中的应用


什么是装饰者模式

应用场景

咖啡店里咖啡中可以加不同的配料--摩卡、牛奶、糖、奶泡;不同的饮品加上不同的配料有不同的价钱,怎样实现呢?

可能你的第一印象会想到使用继承,

  1. 首先定义一个咖啡基类;

  2. 对于加糖的,加牛奶的,加摩卡的 ,加奶泡的,分别写一个子类继承;

  3. 对于加糖,又加奶的写一个类,对于对于加糖,又摩卡的写一个类,对于对于加糖、又奶泡的写一个类,对于加糖,又加奶、摩卡的写一个类…

说到这里,你会发现这里四种配料就要写十几种实现类了,那如果我们的配料是二十几种或者三十几种呢,那么使用继承这种 方式肯定会使我们的子类爆炸,那要怎样解决你,答案就是使用装饰者模式

定义

我觉得装饰者模式是在已有功能的基础之上,动态地添加更多 功能的一种方式,这些新加的代码装饰了原有类的 核心职责或主要行为。

类UML图

效果图


怎样实现装饰者模式呢?

首先我们先来看一下我们的设计类图

1) 首先我们定义一个Coffce基类

 1/** 2 * @ explain:这里Coffee相当于我们的Component, 3 * 是要装饰的类 4 * 5 * @ author:xujun on 2016/7/10 23:16 6 * @ email:gdutxiaoxu@163.com 7 */ 8public abstract class Coffee { 910    /**11     *12     * @return 返回价格13     */14    public abstract int getPrice();1516    /**17     * 返回名字18     * @return19     */20    public abstract String getName();21}

2) 接着 我们定义一个Decorator类继承 我们的Coffice基类

 1/** 2 * @ explain: 3 * @ author:xujun on 2016/7/10 23:21 4 * @ email:gdutxiaoxu@163.com 5 */ 6public abstract class Decorator extends Coffee{ 7 8    protected Coffee mCoffee; 910    /**11     * 通过组合的方式把Coffee对象传递进来12     * @param coffee13     */14    public Decorator(Coffee coffee){15        mCoffee=coffee;16    }17}

3)接下来我们来看我们的子类是怎样实现的

 1public class MilkDecorator extends Decorator { 2 3    /** 4     * 通过组合的方式把Coffee对象传递进来 5     * 6     * @param coffee 7     */ 8    public MilkDecorator(Coffee coffee) { 9        super(coffee);10    }1112    @Override13    public int getPrice() {14        return mCoffee.getPrice()+10;15    }1617    @Override18    public String getName() {19        return "addMilk";20    }21}

其实核心代码就下面一行,在原来的价格加上 加牛奶的价格

1return mCoffee.getPrice()+10

4)接下来不难想象加糖,就奶泡。就摩卡的操作,都是在原来的之上加上配料的价格

1return mCoffee.getPrice()+2;2return mCoffee.getPrice()+15;3return mCoffee.getPrice()+20;

总结

以后你想要计算加糖,就牛奶,加奶泡的咖啡的价格,只需要这样

1mCoffee = new SimpleCoffee();2mCoffee = new SugarDecorator(mCoffee);3mCoffee = new MilkDecorator(mCoffee);4mCoffee = new MilkFoamDecorator(mCoffee);5int price1 = mCoffee.getPrice();6System.out.println("price1="+price1);

以后你想要计算加糖,就牛奶咖啡的价格,只需要这样

1mCoffee = new SimpleCoffee();2mCoffee = new SugarDecorator(mCoffee);3mCoffee = new MilkDecorator(mCoffee);45int price1 = mCoffee.getPrice();6System.out.println("price1="+price1);

装饰者模式的优缺点

优点

  • 把类中的装饰功能从类中搬除,可以简化原来的类

  • 可以把类的 核心职责和装饰功能区分开来,结构清晰 明了并且可以去除相关类的重复的装饰逻辑。


装饰者模式在Android中的应用

效果图

前面已经说到,之所以学习装饰者设计模式,是因为看到 鸿洋大神的 博客Android 优雅的为RecyclerView添加HeaderView和FooterView

下面我们来看一下我们是如何 优雅的为RecyclerView添加HeaderView和FooterView

  1public class HeaderAndFooterWrapper<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {  2    private static final int BASE_ITEM_TYPE_HEADER = 100000;  3    private static final int BASE_ITEM_TYPE_FOOTER = 200000;  4  5    private SparseArrayCompat<View> mHeaderViews = new SparseArrayCompat<>();  6    private SparseArrayCompat<View> mFootViews = new SparseArrayCompat<>();  7  8    private RecyclerView.Adapter mInnerAdapter;  9 10    public HeaderAndFooterWrapper(RecyclerView.Adapter adapter) { 11        mInnerAdapter = adapter; 12    } 13 14    @Override 15    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 16        if (mHeaderViews.get(viewType) != null) { 17            ViewHolder holder = ViewHolder.createViewHolder(parent.getContext(), mHeaderViews.get 18                    (viewType)); 19            return holder; 20 21        } else if (mFootViews.get(viewType) != null) { 22            ViewHolder holder = ViewHolder.createViewHolder(parent.getContext(), mFootViews.get 23                    (viewType)); 24            return holder; 25        } 26        return mInnerAdapter.onCreateViewHolder(parent, viewType); 27    } 28 29    @Override 30    public int getItemViewType(int position) { 31        if (isHeaderViewPos(position)) { 32            return mHeaderViews.keyAt(position); 33        } else if (isFooterViewPos(position)) { 34            return mFootViews.keyAt(position - getHeadersCount() - getRealItemCount()); 35        } 36        return mInnerAdapter.getItemViewType(position - getHeadersCount()); 37    } 38 39    private int getRealItemCount() { 40        return mInnerAdapter.getItemCount(); 41    } 42 43    @Override 44    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 45        if (isHeaderViewPos(position)) { 46            return; 47        } 48        if (isFooterViewPos(position)) { 49            return; 50        } 51        mInnerAdapter.onBindViewHolder(holder, position - getHeadersCount()); 52    } 53 54    @Override 55    public int getItemCount() { 56        return getHeadersCount() + getFootersCount() + getRealItemCount(); 57    } 58 59    @Override 60    public void onAttachedToRecyclerView(RecyclerView recyclerView) { 61        WrapperUtils.onAttachedToRecyclerView(mInnerAdapter, recyclerView, new WrapperUtils 62                .SpanSizeCallback() { 63            @Override 64            public int getSpanSize(GridLayoutManager layoutManager, GridLayoutManager 65                    .SpanSizeLookup oldLookup, int position) { 66                int viewType = getItemViewType(position); 67                if (mHeaderViews.get(viewType) != null) { 68                    return layoutManager.getSpanCount(); 69                } else if (mFootViews.get(viewType) != null) { 70                    return layoutManager.getSpanCount(); 71                } 72                if (oldLookup != null) 73                    return oldLookup.getSpanSize(position); 74                return 1; 75            } 76        }); 77    } 78 79    @Override 80    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { 81        mInnerAdapter.onViewAttachedToWindow(holder); 82        int position = holder.getLayoutPosition(); 83        if (isHeaderViewPos(position) || isFooterViewPos(position)) { 84            WrapperUtils.setFullSpan(holder); 85        } 86    } 87 88    private boolean isHeaderViewPos(int position) { 89        return position < getHeadersCount(); 90    } 91 92    private boolean isFooterViewPos(int position) { 93        return position >= getHeadersCount() + getRealItemCount(); 94    } 95 96    public void addHeaderView(View view) { 97        mHeaderViews.put(mHeaderViews.size() + BASE_ITEM_TYPE_HEADER, view); 98    } 99100    public void addFootView(View view) {101        mFootViews.put(mFootViews.size() + BASE_ITEM_TYPE_FOOTER, view);102    }103104    public int getHeadersCount() {105        return mHeaderViews.size();106    }107108    public int getFootersCount() {109        return mFootViews.size();110    }111112}

接着我们来看一下我们是如何使用它的?

 1mAdapter = new SinglePersonAdapter(this, mDatas, R.layout.main_chat_from_msg); 2mHeaderAndFooterWrapper=new HeaderAndFooterWrapper(mAdapter); 3 4TextView t1 = new TextView(this); 5t1.setPadding(10,10,10,10); 6t1.setBackgroundColor(Color.GRAY); 7t1.setText("Header 1"); 8TextView t2 = new TextView(this); 9t2.setText("Header 2");10t2.setPadding(10,10,10,10);11t2.setBackgroundColor(Color.GRAY);12mHeaderAndFooterWrapper.addHeaderView(t1);13mHeaderAndFooterWrapper.addHeaderView(t2);14mRecyclerView.setAdapter(mHeaderAndFooterWrapper);

是不是很简单,只需要简单的几行代码,就能在原有Adapter的基础之上添加headerView或者Foot而View,具体的代码分析请见鸿洋大神的 博客Android 优雅的为RecyclerView添加HeaderView和FooterView

参考文章

http://blog.csdn.net/lmj623565791/article/details/51854533

Demo下载地址

https://github.com/gdutxiaoxu/Sample_BaseRecyclerAdapter.git

推荐阅读

Android 进阶免费资料

java 源码系列 - 带你读懂 Reference 和 ReferenceQueue

自定义 behavior - 完美仿 QQ 浏览器首页,美团商家详情页

2019.03 随笔

Android 技术人

扫一扫,欢迎关注我的公众号。

如果你有好的文章,也欢迎你的投稿。