[关闭]
@xujun94 2016-07-11T16:05:19.000000Z 字数 6263 阅读 1649

装饰者模式及其应用

设计模式 在java及Android的 应用


前几天看了鸿洋大神的 Android 优雅的为RecyclerView添加HeaderView和FooterView,发现装饰者模式 在某些情况下是设计得如此 优雅,现在总结如下:

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

  1. 什么 是装饰者模式
  2. 怎样实现装饰者模式
  3. 装饰者模式的优缺点
  4. 装饰者模式在Android中的应用

关于观察者设计模式的,可以参考我的这篇博客 观察者设计模式 Vs 事件委托(java)

转载请注明原博客地址: http://blog.csdn.net/gdutxiaoxu/article/details/51885105

Demo下载地址:https://github.com/gdutxiaoxu/Sample_BaseRecyclerAdapter.git


什么是装饰者模式

应用场景

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

可能你的第一印象会想到使用继承,
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. */
  8. public abstract class Coffee {
  9. /**
  10. *
  11. * @return 返回价格
  12. */
  13. public abstract int getPrice();
  14. /**
  15. * 返回名字
  16. * @return
  17. */
  18. public abstract String getName();
  19. }
  1. /**
  2. * @ explain:
  3. * @ author:xujun on 2016/7/10 23:21
  4. * @ email:gdutxiaoxu@163.com
  5. */
  6. public abstract class Decorator extends Coffee{
  7. protected Coffee mCoffee;
  8. /**
  9. * 通过组合的方式把Coffee对象传递进来
  10. * @param coffee
  11. */
  12. public Decorator(Coffee coffee){
  13. mCoffee=coffee;
  14. }
  15. }
  1. ```
  2. * 3. 接下来我们来看我们的子类是怎样实现的
  3. ```java
  4. public class MilkDecorator extends Decorator {
  5. /**
  6. * 通过组合的方式把Coffee对象传递进来
  7. *
  8. * @param coffee
  9. */
  10. public MilkDecorator(Coffee coffee) {
  11. super(coffee);
  12. }
  13. @Override
  14. public int getPrice() {
  15. return mCoffee.getPrice()+10;
  16. }
  17. @Override
  18. public String getName() {
  19. return "addMilk";
  20. }
  21. }
  22. <div class="md-section-divider"></div>

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

  1. return mCoffee.getPrice()+10
  2. <div class="md-section-divider"></div>
  1. return mCoffee.getPrice()+2;
  2. return mCoffee.getPrice()+15;
  3. return mCoffee.getPrice()+20;
  4. <div class="md-section-divider"></div>

总结

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

  1. mCoffee = new SimpleCoffee();
  2. mCoffee = new SugarDecorator(mCoffee);
  3. mCoffee = new MilkDecorator(mCoffee);
  4. mCoffee = new MilkFoamDecorator(mCoffee);
  5. int price1 = mCoffee.getPrice();
  6. System.out.println("price1="+price1);
  7. <div class="md-section-divider"></div>

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

  1. mCoffee = new SimpleCoffee();
  2. mCoffee = new SugarDecorator(mCoffee);
  3. mCoffee = new MilkDecorator(mCoffee);
  4. int price1 = mCoffee.getPrice();
  5. System.out.println("price1="+price1);
  6. <div class="md-section-divider"></div>

装饰者模式的优缺点

优点


装饰者模式在Android中的应用

效果图


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

  1. /**
  2. * 博客地址:http://blog.csdn.net/gdutxiaoxu
  3. * @author xujun
  4. * @time 2016/7/7 17:29.
  5. */
  6. public class HeaderAndFooterWrapper<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
  7. private static final int BASE_ITEM_TYPE_HEADER = 100000;
  8. private static final int BASE_ITEM_TYPE_FOOTER = 200000;
  9. private SparseArrayCompat<View> mHeaderViews = new SparseArrayCompat<>();
  10. private SparseArrayCompat<View> mFootViews = new SparseArrayCompat<>();
  11. private RecyclerView.Adapter mInnerAdapter;
  12. public HeaderAndFooterWrapper(RecyclerView.Adapter adapter) {
  13. mInnerAdapter = adapter;
  14. }
  15. @Override
  16. public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  17. if (mHeaderViews.get(viewType) != null) {
  18. ViewHolder holder = ViewHolder.createViewHolder(parent.getContext(), mHeaderViews.get
  19. (viewType));
  20. return holder;
  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. @Override
  29. public int getItemViewType(int position) {
  30. if (isHeaderViewPos(position)) {
  31. return mHeaderViews.keyAt(position);
  32. } else if (isFooterViewPos(position)) {
  33. return mFootViews.keyAt(position - getHeadersCount() - getRealItemCount());
  34. }
  35. return mInnerAdapter.getItemViewType(position - getHeadersCount());
  36. }
  37. private int getRealItemCount() {
  38. return mInnerAdapter.getItemCount();
  39. }
  40. @Override
  41. public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
  42. if (isHeaderViewPos(position)) {
  43. return;
  44. }
  45. if (isFooterViewPos(position)) {
  46. return;
  47. }
  48. mInnerAdapter.onBindViewHolder(holder, position - getHeadersCount());
  49. }
  50. @Override
  51. public int getItemCount() {
  52. return getHeadersCount() + getFootersCount() + getRealItemCount();
  53. }
  54. @Override
  55. public void onAttachedToRecyclerView(RecyclerView recyclerView) {
  56. WrapperUtils.onAttachedToRecyclerView(mInnerAdapter, recyclerView, new WrapperUtils
  57. .SpanSizeCallback() {
  58. @Override
  59. public int getSpanSize(GridLayoutManager layoutManager, GridLayoutManager
  60. .SpanSizeLookup oldLookup, int position) {
  61. int viewType = getItemViewType(position);
  62. if (mHeaderViews.get(viewType) != null) {
  63. return layoutManager.getSpanCount();
  64. } else if (mFootViews.get(viewType) != null) {
  65. return layoutManager.getSpanCount();
  66. }
  67. if (oldLookup != null)
  68. return oldLookup.getSpanSize(position);
  69. return 1;
  70. }
  71. });
  72. }
  73. @Override
  74. public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
  75. mInnerAdapter.onViewAttachedToWindow(holder);
  76. int position = holder.getLayoutPosition();
  77. if (isHeaderViewPos(position) || isFooterViewPos(position)) {
  78. WrapperUtils.setFullSpan(holder);
  79. }
  80. }
  81. private boolean isHeaderViewPos(int position) {
  82. return position < getHeadersCount();
  83. }
  84. private boolean isFooterViewPos(int position) {
  85. return position >= getHeadersCount() + getRealItemCount();
  86. }
  87. public void addHeaderView(View view) {
  88. mHeaderViews.put(mHeaderViews.size() + BASE_ITEM_TYPE_HEADER, view);
  89. }
  90. public void addFootView(View view) {
  91. mFootViews.put(mFootViews.size() + BASE_ITEM_TYPE_FOOTER, view);
  92. }
  93. public int getHeadersCount() {
  94. return mHeaderViews.size();
  95. }
  96. public int getFootersCount() {
  97. return mFootViews.size();
  98. }
  99. }
  100. <div class="md-section-divider"></div>
  1. mAdapter = new SinglePersonAdapter(this, mDatas, R.layout.main_chat_from_msg);
  2. mHeaderAndFooterWrapper=new HeaderAndFooterWrapper(mAdapter);
  3. TextView t1 = new TextView(this);
  4. t1.setPadding(10,10,10,10);
  5. t1.setBackgroundColor(Color.GRAY);
  6. t1.setText("Header 1");
  7. TextView t2 = new TextView(this);
  8. t2.setText("Header 2");
  9. t2.setPadding(10,10,10,10);
  10. t2.setBackgroundColor(Color.GRAY);
  11. mHeaderAndFooterWrapper.addHeaderView(t1);
  12. mHeaderAndFooterWrapper.addHeaderView(t2);
  13. mRecyclerView.setAdapter(mHeaderAndFooterWrapper);

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

参考文章Android 优雅的为RecyclerView添加HeaderView和FooterView

关于观察者设计模式的,可以参考我的这篇博客 观察者设计模式 Vs 事件委托(java)

转载请注明原博客地址: http://blog.csdn.net/gdutxiaoxu/article/details/51885105

Demo下载地址:https://github.com/gdutxiaoxu/Sample_BaseRecyclerAdapter.git

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注