一、定义
策略模式定义了一系列算法,并将每一个算法封装起来,而且使他们之间可以相互替换,策略模式让算法独立于使它的客户独立而变化。
策略模式的重点不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。
二、角色
2.1 环境(Context)角色
该角色持有一个Stragtegy的引用,可以执行Stragtegy的方法
2.2 抽象策略(Stragtegy)角色
这是一个抽象角色,通常由一个接口或者抽象类实现,此角色给出所有的具体策略角色共同的接口,部分不需要子类实现的部分可在该角色中实现。
2.3 具体策略(ConcreteStragtegy)角色
包装了相关的算法或者行为。
三、需求
假设现在要一个上商店个搞活动。 对所有的高级会员打20%的促销折扣;对中级会员打10%的促销折扣;对初级会员没有折扣。
得知算法:
- 算法一:对初级会员没有折扣。
- 算法二:对中级会员提供10%的促销折扣。
- 算法三:对高级会员提供20%的促销折扣。
四、代码实现
4.1 抽象策略角色实现
/**
* 抽象策略角色,定义接口
*
* Created by rytong on 2017/11/2.
*/
public interface Stragtegy {
/**
* 获取真实价格
*
* @param price
* @return
*/
public double getPrice(double price);
}
4.2 具体策略角色实现
/**
* 普通策略角色:高级会员打8折
*
* Created by rytong on 2017/11/2.
*/
public class Stragtegy3 implements Stragtegy {
@Override
public double getPrice(double price) {
return price*0.8;
}
}
4.3 上下文角色实现
/**
* 上下文角色,持有Stragtegy角色
*
* Created by rytong on 2017/11/2.
*/
public class StragtegyContext {
//具体的策略角色
Stragtegy mStragtegy;
public StragtegyContext(Stragtegy mStragtegy) {
this.mStragtegy = mStragtegy;
}
/**
* 计算实际价格
* @param price
* @return
*/
public double calcutatePrice(double price){
return mStragtegy.getPrice(price);
}
}
4.4 客户端调用
//高级会员
Stragtegy stragtegy3 = new Stragtegy3();
StragtegyContext stragtegyContext = new StragtegyContext(stragtegy3);
Log.e(TAG,"高级会员打折后的价格:"+stragtegyContext.calcutatePrice(1000));
// 改为中级会员
stragtegyContext = new StragtegyContext(new Stragtegy2());
Log.e(TAG,"中级会员打折后的价格:"+stragtegyContext.calcutatePrice(1000));
执行结果:
11-02 15:57:12.958 1812-1812/? E/MainActivity: 高级会员打折后的价格:800.0
11-02 15:57:12.959 1812-1812/? E/MainActivity: 中级会员打折后的价格:900.0
五、安卓中的策略模式
5.1 ListView的Adapter
5.1.1 客户端调用
给每个listView设置adapter的时候需要继承BaseAdapter,并重写各方法来完成,这个地方可以替换为任何的
ListAdapter
接口的子类都可以。
ListView listView = new ListView(this);
listView.setAdapter(new BaseAdapter() {
@Override
public int getCount() {
return 0;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
});
5.1.2 源码查看
从调用点查看:在AbsListView中声明一个ListAdapter,在调用setAdapter的时候赋值。ListAdapter就是抽象策略角色,我们实现的Adapter就是具体策略角色。ListView就是上下文角色。
public void setAdapter(ListAdapter adapter) { if (mAdapter != null && mDataSetObserver != null) { mAdapter.unregisterDataSetObserver(mDataSetObserver); } resetList(); mRecycler.clear(); if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) { mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter); } else { mAdapter = adapter; } ... }
mAdapter的使用:有很多地方都有,找一个简单的
public boolean removeHeaderView(View v) { if (mHeaderViewInfos.size() > 0) { boolean result = false; if (mAdapter != null && ((HeaderViewListAdapter) mAdapter).removeHeader(v)) { if (mDataSetObserver != null) { mDataSetObserver.onChanged(); } result = true; } removeFixedViewInfo(v, mHeaderViewInfos); return result; } return false; }
5.2 插值器
5.2.1 客户端调用
在客户端调用的时候指定插值器的类型,来指定动画的类型。这里ObjectAnimator类就是上下文角色,LinearInterpolator是具体策略角色,TimeInterpolator是抽象策略角色。
TextView textView = new TextView(this);
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(textView, View.SCALE_X, 0f, 1f);
objectAnimator.setDuration(2000);
objectAnimator.setRepeatMode(ValueAnimator.RESTART);
objectAnimator.setRepeatCount(-1);
//插值器
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
5.2.2 源码简单查看
public void setInterpolator(TimeInterpolator value) {
if (value != null) {
mInterpolator = value;
} else {
mInterpolator = new LinearInterpolator();
}
}
六、总结
6.1 优点
- 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或者行为族。恰当的继承可以把公共的代码移到父类里面,从而避免代码重复。
- 使用策略模式可以避免使用多重if-else类语句。把每个分支封装为一种行为单独封装。
6.2 缺点
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便选择恰当的算法使用。换言之,策略模式只适用于客户端知道算法或者行为的情况。
- 由于策略模式把每一个具体的策略封装成了类,所以类文件数量会比较可观。
6.3 特点
- 运行时策略唯一性:在运行时只能有一个生效。
- 平等性:在调用的地方可以使用任意一个具体的策略。他们之间是相互独立,没有依赖的。
6.3 使用场景
- 多个类只区别在表现行为不同,可以使用Stragtgy模式,在运行时动态选择具体要执行的行为
- 需要在不同的情况下使用不同的策略(算法、行为),或者策略还可能在未来用其他方式来实现(容易扩展)
- 对客户隐藏具体策略的算法实现细节,彼此完全独立。