Java设计模式之策略模式

 

一、策略模式的定义:

定义一系列的算法,把它们一个个封装起来,并且可以使他们互相替换。本模式可以使得算法可独立与使用它的客户而变化。

 

二、策略模式出现背景举例:

当我们给人支付工资的时候可以使用不同的支付方式:

1、使用人民币现金

2、使用美元现金

3、使用信用卡

4、使用微信支付

5、使用支付宝

...

等等,有很多种支付方式,那到底最终要选择哪一种支付方式呢,其实是不确定的,针对不同的人,需要使用不同的支付方式【比如在国外可能就需要使用到美元支付等】;另外在不同的时间、地点也可能使用不同的支付方式【比如支付宝没钱了,就可能选择微信支付等】。

如果要写一个总的支付系统类,那么我们是否通通使用if elseif...来完成呢。显然是很不好的:

1、首先该类中包含了所有的支付类型的算法,使得这个类会非常庞大。

2、违背了“开--闭原则”,当不同的支付手段需要调整的时候,总会去修改该类,非常容易产生问题。

 

三、策略模式的功能:

在如上的举例中,策略模式应运而生,策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更加灵活,具有更好的维护性和扩展性。

 

四、策略模式代码示例:

1、定义支付的策略接口


/**
 *
 * @author zhongyao
 * @date 2019-11-03
 * 定义支付的策略接口
 */
public interface IPaymentStrategy {

    void pay(PaymentContext context);
}

2、策略上下文实现(可以看作是某个业务的实例)

/**
 * @author zhongyao
 * @date 2019-11-03
 * 支付策略的上下文
 */
public class PaymentContext {

    /**
     * 应被支付人员的用户名
     */
    private String mUserName;

    /**
     * 应被支付的工资金额
     */
    private double mMoney;

    /**
     * 支付方式的策略接口
     */
    private IPaymentStrategy mIPaymentStrategy;

    public PaymentContext(String userName, double money, IPaymentStrategy paymentStrategy) {
        mUserName = userName;
        mMoney = money;
        mIPaymentStrategy = paymentStrategy;
    }


    public String getUserName() {
        return mUserName;
    }

    public double getMoney() {
        return mMoney;
    }

    public void payNow(){
        mIPaymentStrategy.pay(this);
    }

}

 

3、定义具体的策略实现类

/**
 * 人民币现金支付策略实现类
 *
 * @author zhongyao
 * @date 2019-11-03
 */
public class RMBCashStrategy implements IPaymentStrategy {
    @Override
    public void pay(PaymentContext context) {
        System.out.println(
            "支付方式:人民币现金支付 " + "收款人---姓名:" + context.getUserName() + " 工资:" + context.getMoney()
                + "\n");
    }
}
/**
 * 美元现金支付策略实现类
 *
 * @author zhongyao
 * @date 2019-11-03
 */
public class DollarCashStrategy implements IPaymentStrategy {
    @Override
    public void pay(PaymentContext context) {
        System.out.println(
            "支付方式:美元现金支付 " + "收款人---姓名:" + context.getUserName() + " 工资:" + context.getMoney()
                + "\n");
    }
}

 

4、客户端调用:

        IPaymentStrategy rmbCashStrategy = new RMBCashStrategy();
        PaymentContext paymentContext1 = new PaymentContext("张三", 6000.0, rmbCashStrategy);
        paymentContext1.payNow();

        IPaymentStrategy dollarCashStrategy = new DollarCashStrategy();
        PaymentContext paymentContext2 = new PaymentContext("Jack", 8000.0, dollarCashStrategy);
        paymentContext2.payNow();

如上,最简单的策略模式使用完毕,就是针对不同的情况,使用不同的策略来完成支付任务。

 

 

扩展:

当然,如果此时已经用到了很多现金支付的策略,此时支持了银行卡支付,需要扩展一个银行卡支付策略,那么怎么扩展呢,有如下两种方式:

扩展方式一:扩展上下文的方式

1、新增一个继承于PaymentContext的子类NewPaymentContext,生成一个新的构造方法,构造参数中包含账户account参数。

2、新增一个支付策略类,其pay中,需要将PaymentContext转换成所需的NewPaymentContext实例,然后进行后续操作。

import com.hongri.designpattern.strategy.IPaymentStrategy;
import com.hongri.designpattern.strategy.PaymentContext;

/**
 * 新扩展的支付策略的上下文
 * @author zhongyao
 * @date 2019-11-03
 */
public class NewPaymentContext extends PaymentContext {
    private String mAccount;

     /**
     * 构造方法,新增了一个账户(account)参数
     * @param userName
     * @param money
     * @param account
     * @param paymentStrategy
     */
    public NewPaymentContext(String userName, double money, String account,
                             IPaymentStrategy paymentStrategy) {
        super(userName, money, paymentStrategy);
        mAccount = account;
    }

    public String getAccount() {
        return mAccount;
    }
}

 

import com.hongri.designpattern.strategy.IPaymentStrategy;
import com.hongri.designpattern.strategy.PaymentContext;

/**
 * 信用卡支付策略实现类
 * @author zhongyao
 * @date 2019-11-03
 */
public class CreditCardStrategy implements IPaymentStrategy {
    /**
     * 信用卡策略pay方法中,新增了账号打印
     * @param context
     */
    @Override
    public void pay(PaymentContext context) {
        //此处需使用新的上下文,需将context转成NewPaymentContext
        NewPaymentContext newPaymentContext = (NewPaymentContext)context;
        System.out.println(
            "支付方式:信用卡支付 " + "收款人---姓名:" + newPaymentContext.getUserName() + " 账号:"
                + newPaymentContext.getAccount() + " 工资:" + newPaymentContext.getMoney()
                + "\n");
    }
}

客户端调用方式:

IPaymentStrategy creditCardStrategy = new CreditCardStrategy();
NewPaymentContext newPaymentContext = new NewPaymentContext("王五", 9000.0, "8384894893",creditCardStrategy);
newPaymentContext.payNow();

 

 

 

扩展方式二:在策略的算法实现上添加自己需要的数据的方式

直接新增一个支付策略类,并新增一个全局account变量,当调用支付策略的时候,需要将账户account传递过来。

/**
 * 信用卡支付策略实现类2
 *
 * @author zhongyao
 * @date 2019-11-03
 */
public class CreditCardStrategy2 implements IPaymentStrategy {

    private String mAccount;

    public CreditCardStrategy2(String account) {
        mAccount = account;
    }

    public String getAccount() {
        return mAccount;
    }

    @Override
    public void pay(PaymentContext context) {
        System.out.println(
            "支付方式:信用卡支付2 " + "收款人---姓名:" + context.getUserName() + " 账号:"
                + getAccount() + " 工资:" + context.getMoney()
                + "\n");
    }
}

客户端调用:

IPaymentStrategy creditCardStrategy2 = new CreditCardStrategy2("8384894893");
PaymentContext paymentContext3 = new PaymentContext("王五", 9000.0, creditCardStrategy2);
paymentContext3.payNow();

 

对于如上的两种扩展方式,对比如下:

1、对于扩展上下文的方式:这样实现,策略的实现风格更统一,策略需要的数据都统一从上下文来获取,这样在使用方法上也很统一;另外,在上下文中添加新的数据,别的相应算法也可以用得上,可以视为公共的数据。缺点是,这些数据只有一个特定的算法来使用,那么这些数据有些浪费;另外每次添加新的算法都会去扩展上下文,容易形成复杂的上下文对象层次。

2、对于在策略算法的实现上添加自己需要的数据的方式:这样实现,比较简单。缺点是跟其他策略实现的风格不一致,所以外部使用这些策略算法的时候也不一样了,难于以一个统一的方式来动态切换策略算法。

可以根据具体的业务需要自行扩展。

 

五、策略模式的优缺点:

一、策略模式的优点:

1、定义一系列算法

2、避免多重条件语句

3、更好的扩展性

二、策略模式的缺点:

1、客户必须了解每一种策略的不同

2、增加了对象数目

3、只适合扁平的算法结构

 

六、何时选用策略模式:

1、出现有许多相关的类,仅仅是行为有差别的情况下,可以使用策略模式来使用多个行为中的一个来配置一个类的方法,实现算法动态切换。

2、实现同一个算法,有很多不同实现的情况下,可以使用策略模式来把这些“不同的实现”实现成为一个算法的类层次。

3、需要封装算法中,有与算法相关数据的情况下,可以使用策略模式来避免暴露这些跟算法相关的数据结构。

4、出现抽象一个定义了很多行为的类,并且是通过多个if-else语句来选择这些行为的情况下,可以使用策略模式来代替这些条件语句。

 

 

你可能感兴趣的:(Java,设计模式)