设计模式中的多态——策略模式详解(代码片段)

takumicx takumicx     2023-01-08     141

关键词:

目录

1. 关于策略模式

策略模式和java语言的多态特性有些像。java的多态特性允许我们面向接口编程,不用关心接口的具体实现。接口所指向的实现类,以及通过接口调用的方法的具体行为可以到运行时才绑定。这么做最大的好处是在尽可能实现代码复用的前提下更好地应对具体实现类的变化。比如我想增加一种接口的实现或者修改原有实现类的某个行为,那我几乎不用修改任何客户端代码。策略模式可以说正是这种思想在设计模式上的运用。它可以使我们更好的复用代码,同时使程序结构设计更有弹性,更好的应对变化。

2. 策略模式详解

2.1 策略模式定义

策略模式定义了一系列算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户端而独立的变化。

可以使用多态进行类比来理解策略模式的定义。一系列算法可以理解成接口的不同实现类,因为不同实现类都实现了相同的接口,因而它们也可以相互替换。策略模式让算法独立于客户端而变化与接口的实现类可以独立于使用接口的客户端变化类似。

2.2 策略模式的UML类图

技术分享图片

从UML类图上可以看出,策略模式中主要有3个角色

  • 抽象策略接口
    上图中的Strategy即抽象策略接口,接口中定义了抽象的策略算法algorithm()。

  • 具体的策略实现类
    上图中的StrategyA和StrategyB即具体的策略实现。不同的策略实现类都实现了抽象策略接口,并重写了其抽象策略方法。因为都实现了相同的策略接口,因而算法可以相互替换,并且可以动态的改变具体的算法实现。

  • 封装策略的上下文环境
    上图中的Context即策略的上下文环境。它屏蔽了高层模块对策略算法的直接访问,封装了可能存在的变化。而且提供了修改Strategy的setter方法,可以动态的改变算法的具体实现。

3.策略模式的优点

我们可以结合使用策略模式的例子并与其它实现方案进行对比来看看策略模式到底有什么好处

3.1 一个使用策略模式的例子

定义一个汽车类Car。由于汽车最大的特点是能跑,因而我们赋予该类一个move行为。但要跑起来需要提供能源,通常而言这种能源是汽油,但现在纯靠电池驱动的汽车也越来越多。因而Car的move行为就有两种不同的行为,一种是使用汽油跑,一种是使用电能跑。因而我们可以这么定义

  • 抽象的汽车类Car
/**
 * @author: takumiCX
 * @create: 2018-10-13
 **/
public abstract class Car 

    //汽车品牌
    private String brand;

    public Car(String brand) 
        this.brand = brand;
    

    public Car(String brand, MoveStrategy strategy) 
        this.brand = brand;
        this.moveStrategy=strategy;
    

    //汽车的运行策略:使用汽油运行,使用电能运行等等
    private MoveStrategy moveStrategy;

    //运行方法
    public void move() 
        System.out.print(brand);
        moveStrategy.move();
    

    public void setMoveStrategy(MoveStrategy moveStrategy) 
        this.moveStrategy = moveStrategy;
    

在抽象汽车类中定义了一个move()方法表示汽车具有运行的行为,但是由于到底是使用汽油运行还是使用电能运行并没有直接定义在里面,而是调用了策略接口中定义的move方法。该策略接口以组合的方式封装在Car内部,并提供了setter方法供客户端动态切换汽车的运行方式。

  • 使用汽油运行的策略实现
/**
 * @author: takumiCX
 * @create: 2018-10-14
 **/

/**
 * 使用汽油运行的策略实现
 */
public class GasolineMoveStrategy implements MoveStrategy

    @Override
    public void move() 
        System.out.println(" Use Gasoline Move!");
    
  • 使用电池运行的策略实现
/**
 * @author: takumiCX
 * @create: 2018-10-15
 **/

/**
 * 使用电能运行的策略实现
 */
public class ElectricityMoveStrategy implements MoveStrategy 
    @Override
    public void move() 
        System.out.println(" Use Electricity Move!");
    
  • 具体的汽车实现类
    比如我们通过继承的方式定义一辆特斯拉汽车,特斯拉汽车默认是纯电动的
/**
 * @author: takumiCX
 * @create: 2018-10-13
 **/
public class TeslaCar extends Car 

    public TeslaCar(String brand) 
        super(brand,new ElectricityMoveStrategy());
    
  • 客户端代码
    首先构造一辆特斯拉车观察其运行方式,并通过setter方法动态改变其运行方式为汽油驱动
/**
 * @author: takumiCX
 * @create: 2018-10-13
 **/
public class Client 

    public static void main(String[] args) 

        TeslaCar car = new TeslaCar("Tesla");

        car.move();

        car.setMoveStrategy(new GasolineMoveStrategy());

        car.move();
    
  • 运行结果
    技术分享图片

3.2 与其他实现方式的对比

其实上面的例子除了使用策略模式外,还有其他实现方式,但它们都有比较明显的缺点。

3.2.1接口的实现方式

/**
 * @author: takumiCX
 * @create: 2018-10-15
 **/
public interface Move 
    
    void move();

并让抽象父类Car实现它

/**
 * @author: takumiCX
 * @create: 2018-10-13
 **/
public abstract class Car implements Move
    //汽车品牌
    private String brand;

    public Car(String brand) 
        this.brand = brand;
    

这样所有继承Car的具体汽车类都必须实现自己的move方法,也就是让具体的汽车子类来决定汽车的具体行为:到底是使用汽油运行还是使用电池运行。但是这么做至少有以下几个缺点

  • 1.具体的汽车运行行为不方便后期维护。因而move行为无法被复用,具体的实现都分散在了子类中。如果要对某种驱动方式的实现进行修改,不得不修改所有子类,这简直是灾难。

  • 2.导致类数量的膨胀。同样品牌的汽车,由于有汽油和电动两种运行方式,不得不为其维护两个类,如果在增加一种驱动方式,比如氢能源驱动,那不得为每个品牌的汽车再增加一个类。

  • 3.不方便move行为的扩展,也不方便动态的更换其实现方式。

3.2.2 if-else的实现方式

move方法接受客户端传递的参数,通过if-else或者swich-case进行判断,选择正确的驱动方式。

public void move(String moveStrategy) 
    if("electricity".equals(moveStrategy))
        System.out.println(" Use Electricity Move!");
    else if("gasoline".equals(moveStrategy))
        System.out.println(" Use Gasoline Move!");
    

但这样做相当于硬编码,不符合开闭原则。比如我要增加一种氢能源的驱动方式,这种实现就需要修改move中的代码。而如果使用上面说的策略模式,则只需要增加一个实现实现策略接口的具体策略实现类,而不需要修改move中的任何代码,即可被客户端所使用。

/**
 * @author: takumiCX
 * @create: 2018-10-15
 **/
public class HydrogenMovetrategy implements MoveStrategy 
    @Override
    public void move() 
        System.out.println(" Use Hydrogen Move!");
    

3.3 使用策略模式的优点

  • 1.可以优化类结构,当类的某种功能有多种实现时,可以在类中定义策略接口,将真正的功能实现委托给具体的策略实现类。这样避免了类膨胀,也能更好的进行扩展和维护。

  • 2.避免使用多重条件判断导致的硬编码和扩展性差的问题

  • 3.可以使具体的算法实现自由切换,增强程序设计的弹性。

4. 使用工厂方法模式改进原有策略模式

所有的策略实现都需要对外暴露,上层模块必须知道具体的策略实现类,这与迪米特法则相违背。为此,可以使用工厂方法模式进行解耦。

  • 策略工厂接口
/**
 * @author: takumiCX
 * @create: 2018-10-16
 **/
public interface MoveStrategyFactory 

    MoveStrategy create();
  • 氢能源驱动方式的工厂
/**
 * @author: takumiCX
 * @create: 2018-10-16
 **/
public class HydrogenMoveStrategyFactory implements MoveStrategyFactory 
    @Override
    public MoveStrategy create() 
        return new HydrogenMovetrategy();
    
  • 客户端
/**
 * @author: takumiCX
 * @create: 2018-10-13
 **/
public class Client 

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException 

        TeslaCar car = new TeslaCar("Tesla");

        MoveStrategyFactory factory = new HydrogenMoveStrategyFactory();

        MoveStrategy moveStrategy = factory.create();

        car.setMoveStrategy(moveStrategy);

        car.move();

    

这样我们通过工厂方法模式封装了具体策略类的创建过程,同时也避免了向高层模块暴露。最后运行结构如下
技术分享图片

5. 总结

当完成某项功能有多种不同的实现时,可以实用策略模式。策略模式封装了不同的算法,并且使这些算法可以相互替换,这提高了代码的复用率也增强了程序设计的弹性。并且可以结合其他设计模式比如工厂方法模式向上层模块屏蔽具体的策略类,使代码更易于扩展和维护。

5. 参考资料

  • 《Head First 设计模式》
  • 《设计模式之禅》

javascript设计模式中策略模式的使用方法(代码片段)

策略模式策略模式策略模式组成组成多态在策略模式中的体现多态策略模式的优点改造方案一改造方法二面向对象的策略模式Javascript计算奖金版本的策略模式,策略模式定义一系列的算法,把它们一个个封装起来,... 查看详情

行为型设计模式-策略模式详解(代码片段)

基本介绍策略模式(StrategyPattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,是一种对象行为型模式。模式结构Context(环境角色):持有抽象策略角色的... 查看详情

设计模式之策略模式与责任链模式详解和应用(代码片段)

目录1.策略模式1.1目标1.2.内容定位1.3.定义1.4.应用场景1.5.促销优惠业务场景1.6用策略模式实现选择支付方式的业务场景1.7策略模式在框架源码中的体现1.8策略模式的优缺点2责任链模式2.1责任链楼式的应用场景2.2利用责任链模式... 查看详情

行为型设计模式-策略模式详解(代码片段)

基本介绍策略模式(StrategyPattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,是一种对象行为型模式。模式结构Context(环境角色):持有抽象策略角色的... 查看详情

设计模式-策略模式(strategypattern)(代码片段)

classVersionResourceResolverprivateIStrategystargegy;//接口多态实现策略publicvoiddostargegy.doSomeThing();  查看详情

架构师内功心法,属于游戏设计模式的策略模式详解(代码片段)

一、策略模式的应用场景策略模式(StrategyPattern)是指定义了算法家族、分别封装起来,让它们之间可以相互替换,此模式让算法的变化不会影响到使用算法的用户。1.1应用场景假如系统中有很多类,而他们的区别仅仅在于他们... 查看详情

详解设计模式之策略模式(代码片段)

在讲策略模式之前,我们先看一个日常生活中的小例子:现实生活中我们到商场买东西的时候,卖场往往根据不同的客户制定不同的报价策略,比如针对新客户不打折扣,针对老客户打9折,针对VIP客户打8... 查看详情

设计模式之策略模式(strategy)详解及代码示例(代码片段)

一、策略模式的定义  策略(Strategy)模式的定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把... 查看详情

java面试题详解一:面向对象三大特性(代码片段)

...多态3.多态的作用简单来说:解藕。详细点就是,多态是设计模式的基础(既然是基础,那么一些设计模式中肯定有多态的下面三个条件)4.多态存在的三个条件有继承关系子类重写了父类方法父类引用指向子类对象5.注意:因为多... 查看详情

策略模式(代码片段)

背景介绍:/**策略设计模式的原理解释:duck类(固定不变有叫与显示的方法)作为基类是变化很少的封装出来而可变的就以组合的方式(注入接口)然后通过多态的实现来完成。这样以后还需要增加不同类型的鸭子以及不同的... 查看详情

javascript设计模式之策略模式(代码片段)

...一个个封装起来。将不变的部分和变化的部分隔开是每个设计模式的主题,策略模式也不例外,策略模式的目的就是将算法的使用与算法的实现分离开来。策略模式的实现并不复杂,关键是如何从策略模式的实现背后... 查看详情

经典案例python详解设计模式:策略模式(代码片段)

完成一项任务往往有多种方式,我们将其称之为策略。比如,超市做活动,如果你的购物积分满1000,就可以按兑换现金抵用券10元,如果购买同一商品满10件,就可以打9折,如果如果购买的金额超过500,就可以享受满减50元的优... 查看详情

java设计模式——策略模式(代码片段)

1、简介策略模式(StrategyPattern)是一种比较简单的模式,也叫做政策模式(PolicyPattern)。其定义如下:Defineafamilyofalgorithms,encapsulateeachone,andmaketheminterchangeable.(定义一组算法,将每个算法都封装起来,并且使它们之间可以互... 查看详情

c++各类设计模式及实现详解(代码片段)

软件领域中的设计模式为开发人员提供了一种使用专家设计经验的有效途径。设计模式中运用了面向对象编程语言的重要特性:封装、继承、多态,真正领悟设计模式的精髓是可能一个漫长的过程,需要大量实践经验... 查看详情

孪生兄弟状态模式与策略模式有什么区别,究竟该如何选择(代码片段)

...它们实际所代表的的事物特征是有本质区别的,选择哪个设计模式,代表了你看待业务场景的角度。从合理角度地对业务进程抽象,选择恰当的设计模式,才能让代码有更好的结构。这篇文章重点说说我对状态模式和策略模式区... 查看详情

设计模式----------策略模式

策略模式,不讲过多的废话。我们来直接看代码。1、我们先定一个接口packagecom.guoguo.celvemoshi;/***定义一个策略接口*@author蝈蝈**/publicinterfaceStrategyService{//定义一个可执行方法publicvoidexecute();}2、策略的具体实现(java多态---不懂先... 查看详情

设计模式---策略模式(代码片段)

...f0c;每一条途径对应一种算法,此时我们可以使用一种设计模式来实现灵活地选择解决途径,也能够方便地增加新的解决途径。策略模式包含角色Contex 查看详情

设计模式笔记-策略模式(代码片段)

设计模式笔记-策略模式文章目录设计模式笔记-策略模式1.策略模式2.常见写法3.常见框架中的策略模式1.策略模式策略模式(Strategy),定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换,在使... 查看详情