设计模式七: 策略(Strategy)

简介: 简介策略属于行为型模式的一种,策略模式允许对象的行为或算法在运行时改变,使用不同的算法达成相同的结果或目的.实现层面上,定义一个抽象的算法接口, 然后根据具体算法的不同定义不同的类去实现该接口, 调用时不同实现的实例可以相互替换.策略模式适用于多个类只有行为不同,运行时根据上下文决定使用哪种行为; 根据实际情况不同算法可能不同, 或算法的最优实现在未来确定.意图定义一系列算法,封装每个算法,并使它们可以互换。

简介

策略属于行为型模式的一种,策略模式允许对象的行为或算法在运行时改变,使用不同的算法达成相同的结果或目的.

实现层面上,定义一个抽象的算法接口, 然后根据具体算法的不同定义不同的类去实现该接口, 调用时不同实现的实例可以相互替换.

策略模式适用于多个类只有行为不同,运行时根据上下文决定使用哪种行为; 根据实际情况不同算法可能不同, 或算法的最优实现在未来确定.

意图

定义一系列算法,封装每个算法,并使它们可以互换。

类图

img_31c30029f7339ab5ebc50d0c3bc1d889.png

实现

以绝地求生游戏为例, 要想赢得最后的比赛可能由多种方式(策略), 抽象出 策略接口 WinStrategy, 实现策略 比如激进点的方式AlwaysKillWinStrategy,猥琐点的方式AlwaysHideWinStrategy.

下面我们将实现策略模式(一,二,三步), 并最后讨论更具实际意义的策略与上下文通信的两种方式及其各自利弊(四).

一. 定义策略接口

/**
 * 赢得吃鸡的策略接口
 */
public interface WinStrategy {
    void win();
}

二. 实现策略,使用两种不同的算法实现

/**
 * 采用人挡杀人的模式
 */
public class AlwaysKillWinStrategy implements WinStrategy {
    public void win() {
        System.out.println("总是很钢,大杀四方");
    }
}

/**
 * 采用LYB的模式
 */
public class AlwaysHideWinStrategy implements WinStrategy {
    public void win() {
        System.out.println("总是很苟,活得长久");
    }
}

三. 调用

public class StrategyTest {
    public static void main(String[] args) {
        WinStrategy iWillWin = new AlwaysHideWinStrategy();
        iWillWin.win();
        iWillWin = new AlwaysKillWinStrategy();
        iWillWin.win();
    }

}

四. 策略与上下文

上面的例子中我们并没有体现出上下文的概念, 其隐藏的上下文在main()函数中. 除了 策略接口实现策略 外, 还需要一个 策略上下文 角色.一般由上下文持有策略的引用, 并对策略进行实际调用,下面的代码定义该角色

/**
 * 上下文
 */
public class Context {

    private String weather;
    WinStrategy winStrategy;

    public Context(String weather, WinStrategy winStrategy) {
        this.weather = weather;
        this.winStrategy = winStrategy;
    }

    public void winGame() {
        winStrategy.win();
        System.out.println("天气情况:"+this.weather+"; 采用的策略:"+winStrategy.getClass());
    }

}

//调用
public class StrategyTest {
    public static void main(String[] args) {
        WinStrategy iWillWin = new AlwaysHideWinStrategy();
        Context context = new Context("雾天",iWillWin);
        context.winGame();
    }
}

策略中有时候也需要某些上下文信息, 例如win()方法需要游戏场景中的天气情况, 这种情况下可以将上下文引用传入策略, 代码修改如下:

/**
 * 赢得吃鸡的策略接口,上下文作为参数传入
 */
public interface WinStrategy {
    void win(Context context);
}

/**
 * 采用LYB的模式, 打印出天气情况
 */
public class AlwaysHideWinStrategy implements WinStrategy {
    public void win(Context context) {
        System.out.println("总是很苟,活得长久; 天气: "+context.getWeather());
    }
}

/**
 * 上下文
 */
@Data
public class Context {

    private String weather;
    WinStrategy winStrategy;

    public Context(String weather, WinStrategy winStrategy) {
        this.weather = weather;
        this.winStrategy = winStrategy;
    }

    public void winGame() {
        winStrategy.win(this);
        System.out.println("天气情况:"+this.weather+"; 采用的策略"+winStrategy.getClass());
    }

}

上面通过将上下文引用作为参数传递给策略来实现了策略与上下文的通信, 对于不同策略的实现,未必都会用到上下文中的全部信息, 这种情况下传递上下文引用成为了一种"浪费". 因此有的人更愿意将使用的信息作为属性定义在策略实现类中,实现更有针对性的代码

两种方式各有利弊, 传递上下文引用的方式利于扩展, 且可以做到风格统一, 但可能会导致"浪费", 如果扩展太多上下文子类不利于理解. 注入的方式实现在策略类中方式比较简单, 容易实现, 但也会导致策略与其他策略风格不一致.

/**
 * 赢得吃鸡的策略接口
 */
public interface WinStrategy {
    void win();
}

/**

 * 采用人挡杀人的模式
 */
@AllArgsConstructor
public class AlwaysKillWinStrategy implements WinStrategy {

    private String weather;

    public void win() {
        System.out.println("总是很钢,大杀四方"+" 天气:"+weather);
    }
}

public class StrategyTest {
    public static void main(String[] args) {
        WinStrategy iWillWin = new AlwaysKillWinStrategy("雾天");
        Context context = new Context("雾天",iWillWin);
        context.winGame();
    }
}

总结

优点: 1. 易于扩展;2.避免了if...else... 3. 代码重用

缺点: 调用方需要知道使用的哪种算法, 算法需要完全暴露; 可能会导致实现类过多;

相关文章
|
7月前
|
设计模式 算法
设计模式之 Strategy(策略模式)
设计模式之 Strategy(策略模式)
54 1
|
7月前
|
设计模式 安全 测试技术
【C/C++ 设计模式 单例】单例模式的选择策略:何时使用,何时避免
【C/C++ 设计模式 单例】单例模式的选择策略:何时使用,何时避免
147 0
|
7月前
|
设计模式 算法 Java
行为型设计模式-策略模式(Strategy Pattern)
行为型设计模式-策略模式(Strategy Pattern)
|
7月前
|
设计模式 编解码 C++
【ffmpeg 视频播放】深入探索:ffmpeg视频播放优化策略与设计模式的实践应用(一)
【ffmpeg 视频播放】深入探索:ffmpeg视频播放优化策略与设计模式的实践应用
209 0
|
7月前
|
设计模式 存储 缓存
【ffmpeg 视频播放】深入探索:ffmpeg视频播放优化策略与设计模式的实践应用(二)
【ffmpeg 视频播放】深入探索:ffmpeg视频播放优化策略与设计模式的实践应用
116 0
|
2月前
|
设计模式 监控 算法
Java设计模式梳理:行为型模式(策略,观察者等)
本文详细介绍了Java设计模式中的行为型模式,包括策略模式、观察者模式、责任链模式、模板方法模式和状态模式。通过具体示例代码,深入浅出地讲解了每种模式的应用场景与实现方式。例如,策略模式通过定义一系列算法让客户端在运行时选择所需算法;观察者模式则让多个观察者对象同时监听某一个主题对象,实现松耦合的消息传递机制。此外,还探讨了这些模式与实际开发中的联系,帮助读者更好地理解和应用设计模式,提升代码质量。
Java设计模式梳理:行为型模式(策略,观察者等)
|
7月前
|
设计模式 监控 Java
设计模式 - 观察者模式(Observer):Java中的战术与策略
【4月更文挑战第7天】观察者模式是构建可维护、可扩展系统的关键,它在Java中通过`Observable`和`Observer`实现对象间一对多的依赖关系,常用于事件处理、数据绑定和同步。该模式支持事件驱动架构、数据同步和实时系统,但需注意避免循环依赖、控制通知粒度,并关注性能和内存泄漏问题。通过明确角色、使用抽象和管理观察者注册,可最大化其效果。
128 2
|
4月前
|
设计模式 JavaScript 前端开发
从工厂到单例再到策略:Vue.js高效应用JavaScript设计模式
【8月更文挑战第30天】在现代Web开发中,结合使用JavaScript设计模式与框架如Vue.js能显著提升代码质量和项目的可维护性。本文探讨了常见JavaScript设计模式及其在Vue.js中的应用。通过具体示例介绍了工厂模式、单例模式和策略模式的应用场景及其实现方法。例如,工厂模式通过`NavFactory`根据用户角色动态创建不同的导航栏组件;单例模式则通过全局事件总线`eventBus`实现跨组件通信;策略模式用于处理不同的表单验证规则。这些设计模式的应用不仅提高了代码的复用性和灵活性,还增强了Vue应用的整体质量。
60 1
|
5月前
|
设计模式 安全 Java
Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
120 0
|
7月前
|
设计模式 Java
设计模式之策略 Strategy
设计模式之策略 Strategy
48 1