策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
有一个商店,它有初级、中级、高级会员三种级别,未来也可能会添加新的会员种类。对于不同的会员,折扣模式是不一样的,传统的方法可能会进行if判断,不同的会员进行不同的折扣,但这明显的不利于折扣和定制,所以,我们可以使用策略模式,每种会员对应一个策略,这样对应不同的会员我们直接使用不同的策略就可以了,并且我们还可以随时的添加新的策略。
因为每个会员对应不要的策略,所以可以定义一个统一的策略接口,不同的会员可以有不同的实现。
public interface MemberStrategy {
public double calcPrice(double booksPrice);
}
初级会员直接是原价
public class PrimaryMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于初级会员的没有折扣");
return booksPrice;
}
}
中级会员九折
public class IntermediateMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于中级会员的折扣为10%");
return booksPrice * 0.9;
}
}
高级会员八折
public class AdvancedMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于高级会员的折扣为20%");
return booksPrice * 0.8;
}
}
可以看到,上面对应不要的会员定制了不同的策略,这样我们也可以随时扩展,例如我们想添加一个黄金VIP,很容易直接定义一个实现就可以了。
针对不同的会员,定义好了不同的策略之后,可以看到这个实现我们可以随意实现,它不会影响业务,下面就是根据需要来使用这些策略了。
下个写个Price类,它可以根据传进来的不同策略得到不同会员的价格,这就方便多了,需要什么样的价格,只有给它一个策略就可以了。并且策略跟Price类实现了低耦合。
public class Price {
//持有一个具体的策略对象
private MemberStrategy strategy;
/**
* 构造函数,传入一个具体的策略对象
* @param strategy 具体的策略对象
*/
public Price(MemberStrategy strategy){
this.strategy = strategy;
}
/**
* 计算图书的价格
* @param booksPrice 图书的原价
* @return 计算出打折后的价格
*/
public double quote(double booksPrice){
return this.strategy.calcPrice(booksPrice);
}
}
下面,在客户端中,我们就可以针对不同的会员,给它不同的策略得到不同的价格了。
public class Client {
public static void main(String[] args) {
//选择并创建需要使用的策略对象
MemberStrategy strategy = new AdvancedMemberStrategy();
//创建环境
Price price = new Price(strategy);
//计算价格
double quote = price.quote(300);
System.out.println("图书的最终价格为:" + quote);
}
}
这样我们就可以自己来指定不同的策略了,非常方便。
策略模式的结构
环境(Context)角色:持有一个Strategy的引用。
抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
比如排序,官方告诉大家我这里有一个排序的接口ISort的sort()方法,然后民间各尽其能,实现这个排序的方法:冒泡,快速,堆等等。
这些方法就是“不同的策略”。
在Java中的Comparator就是策略模式,Comparator是一个策略接口,具体的比较方法可以根据需求就行实现。这样就可以根据自己的比较方法来进行比较大小了。
比如:你想对整数采用绝对值大小来排序,Integer 是不符合要求的,你不需要去修改 Integer 类(实际上你也不能这么做)去改变它的排序行为,只要使用一个实现了 Comparator 接口的对象来实现控制它的排序就行了。
public class AbsComparator implements Comparator {
public int compare(Object o1, Object o2) {
int v1 = Math.abs(((Integer)o1).intValue());
int v2 = Math.abs(((Integer)o2).intValue());
return v1 > v2 ? 1 : (v1 == v2 ? 0 : -1);
}
}
public class Test {
public static void main(String[] args) {
//产生一个20个随机整数的数组(有正有负)
Random rnd = new Random();
Integer[] integers = newInteger[20];
for(int i = 0; i < integers.length; i++)
integers[i] = new Integer(rnd.nextInt(100) * (rnd.nextBoolean() ? 1 : -1));
System.out.println("用AbsComparator排序:");
Arrays.sort(integers,new AbsComparator());
System.out.println(Arrays.asList(integers));
}
}
在Android中,Animation不同动画的实现,主要是依靠Interpolator的不同实现而变。它使用的也是策略模式,可以根据需要实现Interpolator,得到不同的动画实现。
定义接口Interpolator:
public interface Interpolator {
float getInterpolation(float input);
}
我们以LinearInterpolator为例,实现具体的策略,代码如下:
public class LinearInterpolator implements Interpolator {
public LinearInterpolator() {
}
public LinearInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return input;
}
}
我们自定义插值器的时候,只需要重写getInterpolation方法,其中传入的input参数就是时间流逝的百分比,这个百分比就是当前时间的流逝除以设置的持续时间Duration来得到的。我们实现这个函数的时候,可以通过改变这个值来实现我们想要的效果。
public void startAnimation(View view)
{
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(3000);
//这个地方设置了变化值的类型
valueAnimator.setObjectValues(new PointF(0, 0));
//设置插值器
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.start();
valueAnimator.addUpdateListener(new AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
// 得到估值器里面的那个返回值
PointF point = (PointF) animation.getAnimatedValue();
//设置属性值
mBlueBall.setX(point.x);
mBlueBall.setY(point.y);
}
});
}
可以看到,我们可以为动画指定Interpolator策略来实现不同的效果,这个策略我们可以自己制定实现。
通过以上也可以看出策略模式的优缺点:
优点:
结构清晰明了、使用简单直观。
耦合度相对而言较低,扩展方便。
操作封装也更为彻底,数据更为安全。
缺点:
随着策略的增加,子类也会变得繁多。
参照文章: