CRUD很无聊?一起学设计模式吧!--策略模式

简介: CRUD很无聊?一起学设计模式吧!--策略模式

定义与特点


策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

策略模式的主要优点如下:

  • 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句。
  • 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
  • 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
  • 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。

主要缺点如下:

  • 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
  • 策略模式造成很多的策略类。

UML



角色定义


策略模式涉及三个角色:

  • 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色定义所有支持的算法的公共接口。
  • 具体策略(ConcreteStrategy)角色,封装了具体的算法或行为。
  • 环境(Context)角色:用一个ConcreteStrategy来配置,持有一个Strategy的引用。

场景实战


提到策略模式不得不说一下《三十六计》,它是根据中国古代军事思想和丰富的斗争经验总结而成的兵书,是中华名族悠久非物质文化遗产之一,它的身影在留下来的战争故事中无处不在。

知己知彼方能百战不殆,在战争中使用哪种谋略需要因人而异。张三好色,使用美人计获取情报,而后图之;李四狡诈,使用苦肉计使其麻痹,放下戒心,然后破之;王二与麻子有仇,只需坐山观虎斗,使用借刀杀人让麻子杀了王二即可。

这个场景可以套用策略模式来实现


代码示例


抽象策略

首先定义所有计策的抽象类,所有策略的目的都是为了击败对手,定义公共方法 fightEnemy

/**
 * 策略接口,定义所有的接口
 * @date 2019/5/22 9:50
 */
public interface FightStrategy {
    /**
     * 杀敌之法
     */
    public void fightEnemy();
}

具体策略

我们定义三种策略,分别是美人计,苦肉计,借刀杀人计:

1. /**
2.  * 三十六计之美人计
3.  */
4. public class HoneyTrapStrategy implements FightStrategy{
5.     @Override
6.     public void fightEnemy() {
7.         System.out.println("使用‘美人计’取得胜利");
8.     }
9. }
1. /**
2.  * 三十六计之苦肉计
3.  */
4. public class SelfInjuryStrategy implements FightStrategy {
5. 
6.     @Override
7.     public void fightEnemy() {
8.         System.out.println("使用’苦肉计‘取得胜利");
9.     }
10. }
1. /**
2.  * 三十六计之借刀杀人
3.  */
4. public class CollateralStrategy implements FightStrategy{
5.     @Override
6.     public void fightEnemy() {
7.         System.out.println("使用’借刀杀人‘取得胜利");
8.     }
9. }

环境角色

环境角色主要是持有一个具体的策略,我们使用构造器在初始化环境类时传入具体的策略

/**
 * 环境角色-持有具体策略的引用
 */
public class StrategyContext {
    private FightStrategy strategy;
    public StrategyContext(FightStrategy strategy) {
        this.strategy = strategy;
    }
    public void fight(){
        this.strategy.fightEnemy();
    }
}

客户端

/**
 * 客户端需要根据具体的对手选择具体的策略
 */
public class FightClient {
    public static void main(String[] args) {
        FightClient client = new FightClient();
        client.fightEnemy("李四");
    }
    private void fightEnemy(String enemyName) {
        StrategyContext context = null;
        switch (enemyName){
            case "张三" :
                context = new StrategyContext(new HoneyTrapStrategy());
                break;
            case "李四":
                context = new StrategyContext(new SelfInjuryStrategy());
                break;
            case "王二":
                context = new StrategyContext(new CollateralStrategy());
                break;
        }
        context.fight();
    }
}

执行结果


扩展


在上面例子中客户端需要承担根据敌人选择具体的策略职责,即上面的 case语句的实现逻辑,把这样一大段代码放在客户端会造成客户端臃肿,影响阅读体验,我们有2种优化策略:

简单工厂

使用简单工厂方法,将选择策略的判断逻辑抽取到工厂类中,客户端传入 enemyName给简单工厂生成具体策略,实现逻辑如下:

/**
 *  简单工厂方法
 *      根据敌人名称选择具体的策略
 */
public class StrategyFactory {
    public static FightStrategy createFightStrategy(String enemyName){
        FightStrategy strategy;
        switch (enemyName){
            case "张三" :
                strategy = new HoneyTrapStrategy();
                break;
            case "李四":
                strategy = new SelfInjuryStrategy();
                break;
            case "王二":
                strategy = new CollateralStrategy();
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + enemyName);
        }
        return strategy;
    }
}

接下来改造客户端,选择具体策略的方法使用简单工厂生成:

/**
 * 使用简单工厂构建具体的策略
 */
public class FightClient {
    public static void main(String[] args) {
        FightClient client = new FightClient();
        client.fightEnemy("张三");
    }
    private void fightEnemy(String enemyName) {
        FightStrategy strategy = StrategyFactory.createFightStrategy(enemyName);
        StrategyContext context = new StrategyContext(strategy);
        context.fight();
    }
}

策略与简单工厂结合

这里主要改造环境角色类,构造方法不再接收具体的策略对象,而是使用 enemyName作为参数接收,让其拥有根据 enemyName选择策略的能力,改造后的环境类如下:

1. /**
2.  * 环境角色 结合简单工厂选择具体的策略
3.  */
4. public class StrategyContext {
5. 
6.     private FightStrategy strategy;
7. 
8.     public StrategyContext(String enemyName) {
9.         switch (enemyName){
10.             case "张三" :
11.                 strategy = new HoneyTrapStrategy();
12.                 break;
13.             case "李四":
14.                 strategy = new SelfInjuryStrategy();
15.                 break;
16.             case "王二":
17.                 strategy = new CollateralStrategy();
18.                 break;
19.             default:
20.                 throw new IllegalStateException("Unexpected value: " + enemyName);
21.         }
22.     }
23. 
24.     public void fight(){
25.         this.strategy.fightEnemy();
26.     }
27. }
改造后的客户端代码如下:
1. /**
2.  * 环境角色拥有选择策略的能力,客户端只需要认识Context角色
3.  */
4. public class FightClient {
5.     public static void main(String[] args) {
6.         FightClient client = new FightClient();
7.         client.fightEnemy("王二");
8.     }
9. 
10.     private void fightEnemy(String enemyName) {
11.         StrategyContext context = new StrategyContext(enemyName);
12.         context.fight();
13.     }
14. }

对比

使用简单工厂方法时客户端需要认识两个类: FightStrategy, StrategyContext,而使用策略与简单工厂结合的方式客户端只需要认识 StrategyContext即可,这使得算法类彻底与客户端分离,耦合度会更低。


应用场景


策略模式的使用场景很多,主要有以下几类:

  • 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
  • 《重构》一书中指出策略模式可以作为优化条件语句的技巧,一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
  • 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
  • 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
  • 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。
目录
相关文章
|
4月前
|
设计模式 算法 搜索推荐
Java 设计模式之策略模式:灵活切换算法的艺术
策略模式通过封装不同算法并实现灵活切换,将算法与使用解耦。以支付为例,微信、支付宝等支付方式作为独立策略,购物车根据选择调用对应支付逻辑,提升代码可维护性与扩展性,避免冗长条件判断,符合开闭原则。
679 35
|
5月前
|
设计模式 人工智能 算法
基于多设计模式的状态扭转设计:策略模式与责任链模式的实战应用
接下来,我会结合实战案例,聊聊如何用「策略模式 + 责任链模式」构建灵活可扩展的状态引擎,让抽奖系统的状态管理从「混乱战场」变成「有序流水线」。
|
9月前
|
设计模式 算法 Java
设计模式觉醒系列(04)策略模式|简单工厂模式的升级版
本文介绍了简单工厂模式与策略模式的概念及其融合实践。简单工厂模式用于对象创建,通过隐藏实现细节简化代码;策略模式关注行为封装与切换,支持动态替换算法,增强灵活性。两者结合形成“策略工厂”,既简化对象创建又保持低耦合。文章通过支付案例演示了模式的应用,并强调实际开发中应根据需求选择合适的设计模式,避免生搬硬套。最后推荐了JVM调优、并发编程等技术专题,助力开发者提升技能。
|
设计模式 算法 Kotlin
Kotlin - 改良设计模式 - 策略模式
Kotlin - 改良设计模式 - 策略模式
164 4
|
9月前
|
设计模式 算法 搜索推荐
【设计模式】【行为型模式】策略模式(Strategy)
一、入门 什么是策略模式? 策略模式是一种行为设计模式,允许在运行时选择算法或行为。它将算法封装在独立的类中,使得它们可以互换,而不影响客户端代码。 为什么需要策略模式? 策略模式的主要目的是解决算法
198 14
|
设计模式 算法 开发者
「全网最细 + 实战源码案例」设计模式——策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,用于定义一系列可替换的算法或行为,并将它们封装成独立的类。通过上下文持有策略对象,在运行时动态切换算法,提高代码的可维护性和扩展性。适用于需要动态切换算法、避免条件语句、经常扩展算法或保持算法独立性的场景。优点包括符合开闭原则、运行时切换算法、解耦上下文与策略实现、减少条件判断;缺点是增加类数量和策略切换成本。示例中通过定义抽象策略接口和具体策略类,结合上下文类实现动态算法选择。
460 8
「全网最细 + 实战源码案例」设计模式——策略模式
|
设计模式 存储 缓存
前端必须掌握的设计模式——策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,旨在将多分支复杂逻辑解耦。每个分支类只关心自身实现,无需处理策略切换。它避免了大量if-else或switch-case代码,符合开闭原则。常见应用场景包括表单验证、风格切换和缓存调度等。通过定义接口和上下文类,策略模式实现了灵活的逻辑分离与扩展。例如,在国际化需求中,可根据语言切换不同的词条包,使代码更加简洁优雅。总结来说,策略模式简化了多条件判断,提升了代码的可维护性和扩展性。
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
152 1
|
设计模式 前端开发 JavaScript
JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式
本文深入探讨了JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式,结合电商网站案例,展示了设计模式如何提升代码的可维护性、扩展性和可读性,强调了其在前端开发中的重要性。
223 2
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
125 2

热门文章

最新文章