策略模式的使用情景:
有一系列平行的解决某一问题的算法,为这些算法抽象出一个接口,所有算法都是该接口的具体实现。实现一个上下文(Context),在上下文中有算法接口的引用。使用算法的客户不直接使用具体算法类,而是和上下文交互,这样实现了客户端和算法的解耦。
假设现在有这样的需求:商家对新客户的报价是原价,老客户打9折,大客户打8.5折。简单的实现可以用多个 if else 判断,但当增加新的定价方案时不容易扩展,需要修改现有代码。这种情况下,使用策略模式较合适。
示例代码
public class Strategy_Model {
public static void main(String[] args) {
NewCustomerStrategy ns = new NewCustomerStrategy();
Price p = new Price(ns);
p.quote(100);
OldCustomerStrategy os = new OldCustomerStrategy();
Price p2 = new Price(os);
p2.quote(100);
BigCustomerStrategy bs = new BigCustomerStrategy();
Price p3 = new Price(bs);
p3.quote(100);
}
}
class Price {
private Strategy strategy;
public Price(Strategy strategy){
this.strategy = strategy;
}
public void quote(double goodPrice){
this.strategy.calculatePrice(goodPrice);
}
}
interface Strategy{
public void calculatePrice(double goodPrice);
}
class NewCustomerStrategy implements Strategy{
@Override
public void calculatePrice(double goodPrice) {
// TODO Auto-generated method stub
System.out.println("新客户原价");
}
}
class OldCustomerStrategy implements Strategy{
@Override
public void calculatePrice(double goodPrice) {
// TODO Auto-generated method stub
System.out.println("老客户9折 "+goodPrice*0.9);
}
}
class BigCustomerStrategy implements Strategy{
@Override
public void calculatePrice(double goodPrice) {
// TODO Auto-generated method stub
System.out.println("大客户8.5折 "+goodPrice*0.85);
}
}
策略模式将具体的算法从业务中独立出来,形成一系列单独的算法类,算法类间可以替换,策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,可维护可扩展。
具体策略的选择,选择权在客户端。上下文(比如上例中的Price类)是客户端和具体策略间的媒介。
策略的实现,一般就像上例抽象一个策略的接口,然后实现不同的策略实现类。如果有公共功能需要实现,那就把接口改成抽象类,公共功能在抽象类中实现。
Strategy 和 Context 的关系:Context中有Strategy的引用,Strategy也可以从Context中获取数据,将Context对象作为参数传递给Strategy,Strategy回调Context的方法获取数据,这种情况下,Context可以作为Strategy的公共功能的实现类。
假设现在有这样的需求:有不同的工资结算方式,比如,美元,人民币,银行转账。下面用策略模式,将上下文对象传递给策略实现对象的方式实现。
示例代码:
public class Strategy_Salary_Model {
public static void main(String[] args) {
SalaryStrategy rmb = new RMBStrategy();
Salary s1 = new Salary("小张", 12000, rmb);
s1.paySalary();
SalaryStrategy dollar = new DollarStrtegy();
Salary s2 = new Salary("Jim", 8000, dollar);
s2.paySalary();
}
}
interface SalaryStrategy{
public void pay(Salary s);
}
class Salary{
private SalaryStrategy strategy;
private double salary;
private String userName;
public SalaryStrategy getStrategy() {
return strategy;
}
public double getSalary() {
return salary;
}
public String getUserName() {
return userName;
}
public Salary(String userName,double salary,SalaryStrategy strategy){
this.userName = userName;
this.salary = salary;
this.strategy = strategy;
}
public void paySalary(){
this.strategy.pay(this);
}
}
class RMBStrategy implements SalaryStrategy{
@Override
public void pay(Salary s) {
// TODO Auto-generated method stub
System.out.println("付给 "+s.getUserName()+" RMB "+s.getSalary());
}
}
class DollarStrtegy implements SalaryStrategy{
@Override
public void pay(Salary s) {
// TODO Auto-generated method stub
System.out.println("付给 "+s.getUserName()+" Dollar "+s.getSalary());
}
}
结果:
付给 小张 RMB 12000.0
付给 Jim Dollar 8000.0
假设现在要扩展一张支付方式:银行转账。因为需要银行账号,故重写一个上下文SalaryBank继承Salary.添加一个新的银行转账的策略。
class SallaryBank extends Salary{
private int account;
public int getAccount() {
return account;
}
public SallaryBank(String userName, double salary,int bankAccount, SalaryStrategy strategy) {
super(userName, salary, strategy);
this.account = bankAccount;
}
}
class BankStrtegy implements SalaryStrategy{
@Override
public void pay(Salary s) {
// TODO Auto-generated method stub
System.out.println("付给 "+s.getUserName()+" "+s.getSalary()+",通过银行转账,账户:"+((SallaryBank)s).getAccount());
}
}
public class Strategy_Salary_Model {
public static void main(String[] args) {
SalaryStrategy rmb = new RMBStrategy();
Salary s1 = new Salary("小张", 12000, rmb);
s1.paySalary();
SalaryStrategy dollar = new DollarStrtegy();
Salary s2 = new Salary("Jim", 8000, dollar);
s2.paySalary();
SalaryStrategy bank = new BankStrtegy();
SallaryBank s3 = new SallaryBank("王经理", 8000, 123456,bank);
s3.paySalary();
}
}
结果:
付给 小张 RMB 12000.0
付给 Jim Dollar 8000.0
付给 王经理 8000.0,通过银行转账,账户:123456
上面的例子通过改变上下文,通过上下文来获取数据。还可以直接修改策略,通过具体策略实现类的构造函数传参。
class BankStrategy2 implements SalaryStrategy{
private int account;
public BankStrategy2(int account){
this.account = account;
}
@Override
public void pay(Salary s) {
// TODO Auto-generated method stub
System.out.println("付给 "+s.getUserName()+" "+s.getSalary()+",通过银行转账,账户:"+this.account);
}
}
SalaryStrategy bank = new BankStrategy2(789456);
Salary s3 = new Salary("王经理", 10000,bank);
s3.paySalary();
运行结果:
付给 王经理 10000.0,通过银行转账,账户:789456
两种方式,看情况选用。
对应策略算法在实现上有公共功能的情况,实现方式有:
1),将接口写成抽象类,把公共功能在抽象类中实现。
2),将公共功能放在Context里实现,具体策略对象回调上下文对象。
3),为所有策略算法抽象一个父类,该父类去实现策略接口,在该父类中实现公共功能。
策略模式的本质“分离算法,选择实现” 。