策略模式

    策略模式是一种软件设计模式,是指对象有某个行为,但是在不同的场合会有不同的实现算法,用编程语言来说,就是说这个类的行为和算法会在运行时作出改变。这种模式属于行为型模式。在我们生活中有这样的例子,比如说商场促销活动,不同的商品有这不同的促销方案八折、五折、生日卡,我们去旅行时,可以选择飞机、火车、大巴等多种出行方案,支付的时候可以选择微信、支付宝、银联等等。

这种场景下,根据用户的需求需要对这个算法作出选择,用户不知道这个算法的具体实现,但是用户知道自己选择哪个算法。

我们以支付为例,先来看看不使用策略模式的代码有什么弊端。

比如说现在有一个购物类,从买东西到下单付款都有一系列的流程,加入东西都买好了,现在要付款了。

/**
 * 支付类
 */
public class PayType {

    public void payMoney(String type){
        if ("支付宝".equals(type)) {
            System.out.println("支付宝支付,消费200,余额20");
        }else if ("微信".equals(type)){
            System.out.println("微信支付,消费200,余额10");
        }else if ("银联".equals(type)){
            System.out.println("银联支付,消费200,余额3000");
        }
    }
}


/**
 * 客户端调用
 */
public class Strategy {

    public static void main(String[] args) {

        PayType payType = new PayType();
        String type ="支付宝";
        //选择支付宝支付
        payType.payMoney(type);
    }
}

     我们可以看到,这个支付类有很多if  else的判断语句,假如我需要再添加一种支付方式,就需要再次添加一个if 判断,这种方式不但使得支付类语句变得复杂,不易维护,而且也违背了开闭原则。

如果采用策略模式就能够很好的解决这个问题。我们来看下策略模式怎么写。

     先看下策略模式的定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

这里面有几个关键字,具体理解是将每一种算法单独作为一个类,每次新增一个算法就新增一个策略类,修改某个算法不需要修改整个策略类,直接修改对应的算法即可。

策略模式的主要结构如下:

抽象策略类(Strategy):定义一个公共的算法接口,每种算法都实现了这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。

具体策略(Concrete Strategy):实现了这个算法接口的具体实现类。

环境类(Context):持有一个策略类的引用,最终由客户端调用。

策略接口:

/**
 * 支付接口(策略接口)
 */
public interface PayType {

    public void payMoney();
}

具体的策略类:

/**
 * 微信支付(具体的策略类)
 */
public class WeiChart implements PayType {
    @Override
    public void payMoney() {
        System.out.println("使用微信支付,付款200元");
    }
}

/**
 * 支付宝支付(具体的策略类)
 */
public class Ali implements PayType {
    @Override
    public void payMoney() {
        System.out.println("使用支付宝支付,付款金额200元");
    }
}

/**
 * 银联卡支付(具体的策略类)
 */
public class BlankCard implements PayType {
    @Override
    public void payMoney() {
        System.out.println("使用银行卡支付,付款金额200,余额20元");
    }
}

环境类,上下文对象:

/**
 * 上下文对象,持有策略类的引用
 */
public class Context {

    PayType payType;

    public Context(PayType payType) {
        this.payType = payType;
    }

    public PayType getPayType() {
        return payType;
    }

    public void setPayType(PayType payType) {
        this.payType = payType;
    }
}

客户端调用:

/**
 * 客户端调用
 */
public class Strategy {

    public static void main(String[] args) {
        //使用微信支付
        Context context1 = new Context(new WeiChart());
        context1.getPayType().payMoney();

        //使用银联卡支付
        Context context2 = new Context(new BlankCard());
        context2.getPayType().payMoney();
    }
}

使用微信支付,付款200元
使用银行卡支付,付款金额200,余额20元

那么,这里有个疑问,为什么不能直接使用接口引用指向具体的实现类,还需要再加一层Context类呢?

我们看下这个类的定义,上下文对象,这个上下文对象是干嘛的,顾名思义,承上启下,上指的是客户端调用类,下指的就是我们这个策略类。

在这里,将实例化具体策略的过程由客户端转到Context类中,客户端只需要和Context类交互即可,使得支付算法和客户端彻底分离,更加降低了客户端和策略类之间的耦合。

看下UML类图:

策略模式-LMLPHP

使用策略模式的优缺点

优点:

1、能够减少代码的if else逻辑判断,可以在不改变源代码的情况下,新增接口实现类,灵活增加新算法。符合开闭原则。

2、把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。

缺点:

1、客户端调用类必须明确知道所有算法的区别。

2、每新增一个算法,就需要添加一个策略类。

其实我们发现,策略模式和工厂模式非常相似,但是侧重点不同,策略模式注重的是行为的切换,不是行为的实现。而简单工厂模式注重的是对象的创建。

04-22 05:23