实例探索Java模式之路——策略模式

来源:互联网 发布:淘宝卖家版怎么登陆 编辑:程序博客网 时间:2024/05/22 14:55
策略模式


1、策略模式属于对象的行为模式。是针对的一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得他们可以互相替换。
策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式是对算法的包装。
把使用算法的责任和算法本身分割开,委派给不同的对象管理。
简单来说就是,准备一组算法,并将每一个算法封装起来,使得他们可以互相互换。



3、结构:


环境:持有一个抽象策略的引用。
抽象策略:给出所有的具体策略所需的接口
具体策略:包装了相关的算法或行为。


4、一个简单的实例:


//环境类,持有抽象策略的引用
public class Context {


private Strategy strategy;


public Context(Strategy strategy) {
this.strategy = strategy;
}


// 策略方法
public void contextInterface() {
strategy.strategyInterface();
}
}


//抽象策略类
public abstract class Strategy {
// 策略方法
public abstract void strategyInterface();
}




//具体策略1
public class ConcreteStrategy1 extends Strategy {


@Override
public void strategyInterface() {
System.out.println("我是具体策略算法1");
}


}


//具体策略2
public class ConcreteStrategy2 extends Strategy {


@Override
public void strategyInterface() {
System.out.println("我是具体策略算法2");
}


}


//客户端
public class client {


public static void main(String[] args) {


// 决定使用什么策略
Strategy strategy = new ConcreteStrategy2();
Context c = new Context(strategy);
c.contextInterface();
}
}


5、策略模式应该注意的地方:


1、所有的具体策略都有一些公有的行为,应当把这些公有的行为放在抽象策略角色Strategy类里面。这时时候抽象角色必须用java抽象类实现,而不使用Java接口。
2、策略模式每一个时刻只能使用一个策略对象,有时候一个应用程序同时与几个策略对象相联系。在应用程序启动时,所有的策略对象就已经被创立出来,而应用程序可以在几个策略对象之间调换。

6、
一个计算图书折扣的例子。客户端通过不同算法得到相应的折扣金额。
算法1:有些图书没有折扣,折扣对象返回0作为折扣值。
算法2:有些图书固定值为5元的折扣。
算法3:图书提供一个百分比的折扣。


//图书折扣抽象策略类
public abstract class DiscountStrategy {


protected float price = 0;// 书本单价
protected int copies = 0;// 书本数量


// 策略方法
public abstract float calculateDiscount();


// 构造方法
public DiscountStrategy(float price, int copies) {
this.price = price;
this.copies = copies;
}
}


//具体折扣类,没有折扣的图书
public class NoDiscountStrategy extends DiscountStrategy {


// 构造方法
public NoDiscountStrategy(float price, int copies) {
super(price, copies);
}


// 策略方法,没有折扣
@Override
public float calculateDiscount() {
return 0;
}


}




//具体折扣类,没有折扣的图书
public class FlatRateStrategy extends DiscountStrategy {


private float amount = 0.0f;// 固定折扣金额


// 构造方法
public FlatRateStrategy(float price, int copies) {
super(price, copies);
}


public float getAmount() {
return amount;
}


public void setAmount(int amount) {
this.amount = amount;
}


// 策略方法,固定折扣金额书本水量*固定折扣金额
@Override
public float calculateDiscount() {
return copies * amount;
}


}


//具体折扣类,一个百分比的折扣
public class PercentageStrategy extends DiscountStrategy {


private float percent = 0.0f;// 折扣百分比


// 构造方法
public PercentageStrategy(float price, int copies) {
super(price, copies);
}


public float getPercent() {
return percent;
}


public void setPercent(float percent) {
this.percent = percent;
}


@Override
public float calculateDiscount() {
return copies * (1.0f - percent) * price;
}


}




import java.text.NumberFormat;


//客户端,策略模式并不负责使用哪一种策略,应当由客户端自己决定在什么情况下使用哪一种具体策略
public class client extends Thread {


public static void main(String[] args) {


// 没有折扣的图书
NoDiscountStrategy nodiscountStrategy = new NoDiscountStrategy(10.f, 10);
// 折扣总额
System.out.println("折扣的折扣金额"
+ formateCurrency(nodiscountStrategy.calculateDiscount()));


// 固定折扣的图书
FlatRateStrategy flatRateStrategy = new FlatRateStrategy(20.0f, 5);
flatRateStrategy.setAmount(5);
// 折扣总额
System.out.println("折扣的折扣金额"
+ formateCurrency(flatRateStrategy.calculateDiscount()));


// 百分比折扣
PercentageStrategy percentageStrategy = new PercentageStrategy(50f, 10);
percentageStrategy.setPercent(0.8f);// 8折
System.out.println("折扣的折扣金额"
+ formateCurrency(percentageStrategy.calculateDiscount()));// 折扣总额


}


// 金额格式化
private static String formateCurrency(double amnt) {
return NumberFormat.getCurrencyInstance().format(amnt);
}
}


什么图书使用什么折扣并不是策略模式决定的,而是由客户端决定的。
策略模式仅仅是封装算法,不决定何时使用何种算法。


折扣的折扣金额¥0.00
折扣的折扣金额¥25.00
折扣的折扣金额¥100.00


我们会发现上面的代码缺少一个环境角色(环境:持有一个抽象策略的引用)


//环境类,持有抽象策略的引用
public class Context {


private DiscountStrategy strategy;


public Context(DiscountStrategy strategy) {
this.strategy = strategy;
}


// 策略方法
public float contextInterface() {
return strategy.calculateDiscount();
}
}

import java.text.NumberFormat;


public class client extends Thread {


// 金额格式化
private static String formateCurrency(double amnt) {
return NumberFormat.getCurrencyInstance().format(amnt);
}
public static void main(String[] args) {


// 没有折扣的图书
NoDiscountStrategy nodiscountStrategy = new NoDiscountStrategy(10.f, 10);
Context cn = new Context(nodiscountStrategy);
System.out.println(formateCurrency(cn.contextInterface()));
// 固定折扣的图书
FlatRateStrategy flatRateStrategy = new FlatRateStrategy(20.0f, 5);
flatRateStrategy.setAmount(5);
Context cf = new Context(flatRateStrategy);
System.out.println(formateCurrency(cf.contextInterface()));


PercentageStrategy percentageStrategy = new PercentageStrategy(50f, 10);
percentageStrategy.setPercent(0.8f);// 8折
Context cp = new Context(percentageStrategy);
System.out.println(formateCurrency(cp.contextInterface()));


}
}



7、策略模式的使用场景:
要达到某一个目的,根据具体的实际情况,选择合适的方法。适合于实现某一个功能有多种方案可以选择的情景。


8、策略模式优缺点:


优点:
(1)策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。
(2)使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件语句里面,比使用继承的办法还要原始和落后。
缺点:
(1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。
  (2)由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。