本篇博客主要讲解一下几个问题
-
什么 是装饰者模式
-
怎样实现装饰者模式
-
装饰者模式的优缺点
-
装饰者模式在Android中的应用
什么是装饰者模式
应用场景
咖啡店里咖啡中可以加不同的配料--摩卡、牛奶、糖、奶泡;不同的饮品加上不同的配料有不同的价钱,怎样实现呢?
可能你的第一印象会想到使用继承,
-
首先定义一个咖啡基类;
-
对于加糖的,加牛奶的,加摩卡的 ,加奶泡的,分别写一个子类继承;
-
对于加糖,又加奶的写一个类,对于对于加糖,又摩卡的写一个类,对于对于加糖、又奶泡的写一个类,对于加糖,又加奶、摩卡的写一个类…
说到这里,你会发现这里四种配料就要写十几种实现类了,那如果我们的配料是二十几种或者三十几种呢,那么使用继承这种 方式肯定会使我们的子类爆炸,那要怎样解决你,答案就是使用装饰者模式
定义
我觉得装饰者模式是在已有功能的基础之上,动态地添加更多 功能的一种方式,这些新加的代码装饰了原有类的 核心职责或主要行为。
类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
推荐阅读
java 源码系列 - 带你读懂 Reference 和 ReferenceQueue
自定义 behavior - 完美仿 QQ 浏览器首页,美团商家详情页
扫一扫,欢迎关注我的公众号。
如果你有好的文章,也欢迎你的投稿。