装饰
意图
动态给一个对象添加额外的职责,就增加功能来说,装饰(Decorator)模式优于继承。装饰属于对象适配器模式的一种。
别名
包装器Wrapper
动机
当我们希望给某个对象而不是整个类添加功能时,例如,一个图形用户界面工具箱允许你对任何一个用户界面组件增加特性,例如为TextView增加边框。
使用继承机制是添加功能的有效途径,子类可以持续拥有边框的特性。但这种方法不够灵活,用户不能控制对组件加框的方式和时机。
一种较为灵活的方式就是将对象嵌入另一个对象中,由这个对象添加边框,我们称这个嵌入的对象为装饰。
适用性
在以下情景可以使用装饰模式
在不影响其他对象的情况下。
当不便于采用生成子类进行扩充的情形,例如父类的子类已经非常多了,从程序设计的角度不便于扩充。
参与者
Component
- 定义一个对象接口,可以给这些对象动态增加职责。
ConcreteComponent
- 定义一个对象,可以给对象增加职责
Decorator
- 抽象装饰者,继承Component接口,从外类来狂战Componet功能
ConcreteDecorator
- 实现抽象装饰着,向ConcreteComponent对象增加职责,一般构造方法接受ConcreteComponent对象。
实现
例如我们的充电器,有些充电器就降压后的5V电压充电,有些充电器带有过流保护的功能。那么我们假设不带过流保护功能的为ConcreteComponent,带过流保护的为ConcreteDecorator。
Component
public interface Component
{
public void Voltage5V();
}
ConcreteConponent
public class ConcreteComponent implements Component
{
@Override
public void Voltage5V ()
{
System.out.println("输出5V电压");
}
}
Decorator
public abstract class Decorator extends ConcreteComponent
{
private ConcreteComponent mConcreteComponent;
public Decorator ( ConcreteComponent concreteComponent )
{
mConcreteComponent = concreteComponent;
}
protected abstract void addProtect ();
}
ConcreteDecorator
public class ConcreteDecorator extends Decorator
{
public ConcreteDecorator ( ConcreteComponent concreteComponent )
{
super(concreteComponent);
}
@Override
protected void addProtect ()
{
super.Voltage5V();
System.out.println("增加过流保护\n");
}
}
Test
public class Test
{
public static void main(String[] args)
{
ConcreteComponent component = new ConcreteComponent();
ConcreteDecorator decorator = new ConcreteDecorator(component);
decorator.addProtect(); //out add protect.
component.Voltage5V(); //out not protect.
}
}
协作
1.得到ConcreteComponet的对象,然后将对象赋入ConcreteDecorator构造器创建对象。
2.ConcreteDecorator对象附加职责。
Android中的Decorator
当我们使用RecyclerView的时候,你可能希望你的RecyclerView有以下功能添加提示无数据的空界面、添加顶部和底部的View、添加下拉监听事件,而因为界面的不同只是需要其中1~3项的功能。这时候装饰模式便派上用场了。下面进行解析。
注:Java类库中的java.io.InputStream、OutputStream、Reader、Writer这些类的所有子类,它们都有一个接受相同类型作为参数的构造函数,返回增加额外方法的子类。
RecyclerView.Adapter相当于Decorator的共同抽象接口。
RecyclerView.Adapter
public static abstract class Adapter<VH extends ViewHolder>
{
......
}
EmptyWrapper相当ConcreteDecorator,Decorator的构造器都是同样接受共同抽象接口的实例。
EmptyWrapper
public class EmptyWrapper<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder>
{
public static final int ITEM_TYPE_EMPTY = Integer.MAX_VALUE - 1;
private RecyclerView.Adapter mInnerAdapter;
private View mEmptyView;
private int mEmptyLayoutId;
/**
* Receive same abstract class as parameter.
* @param adapter RecyclerView.Adapter
*/
public EmptyWrapper(RecyclerView.Adapter adapter)
{
mInnerAdapter = adapter;
}
/**
* Add function.
*/
private boolean isEmpty()
{
return (mEmptyView != null || mEmptyLayoutId != 0) && mInnerAdapter.getItemCount() == 0;
}
/**
* Add function.
* @param emptyView view
*/
public void setEmptyView(View emptyView)
{
mEmptyView = emptyView;
}
/**
* Add function.
* @param layoutId id
*/
public void setEmptyView(int layoutId)
{
mEmptyLayoutId = layoutId;
}
.....
}
同理
HeaderAndFooterWrapper
public class HeaderAndFooterWrapper<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder>
{
private static final int BASE_ITEM_TYPE_HEADER = 100000;
private static final int BASE_ITEM_TYPE_FOOTER = 200000;
private SparseArrayCompat<View> mHeaderViews = new SparseArrayCompat<>();
private SparseArrayCompat<View> mFootViews = new SparseArrayCompat<>();
private RecyclerView.Adapter mInnerAdapter;
public HeaderAndFooterWrapper(RecyclerView.Adapter adapter)
{
mInnerAdapter = adapter;
}
/**
* add header's View.
* @param view
*/
public void addHeaderView(View view)
{
mHeaderViews.put(mHeaderViews.size() + BASE_ITEM_TYPE_HEADER, view);
}
public void addFootView(View view)
{
mFootViews.put(mFootViews.size() + BASE_ITEM_TYPE_FOOTER, view);
}
.......
Test
private void initRecycleView ()
{
mAdapter = new CommonAdapter< Scenery.ResultBean >(getActivity(), R.layout.item_scenery, mDatas)
{
@Override
protected void convert ( ViewHolder holder, Scenery.ResultBean info, int position )
{
ImageLoader.loadURLImage(getActivity(), info.imgurl, holder.getView(R.id.view_iv_scenery));
holder.setText(R.id.title_tv_scenery, info.title);
}
};
mEmptyWrapper = new EmptyWrapper< Scenery.ResultBean >(mAdapter);
mEmptyWrapper.setEmptyView(LayoutInflater.from(mContext).inflate(R.layout.view_empty, mRecyclerView, false));
mRecyclerView.setAdapter(mEmptyWrapper);
}