设计模式之策略模式

本文介绍了策略模式,定义为封装一组可互换的算法。其实现运用面向对象的继承和多态,有算法自由切换、扩展性好等优点,但也存在策略类增多、需对外暴露等缺点。还介绍了策略模式与简单工厂、注解和反射结合的扩展方式,以解决依赖和开闭原则问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义

定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。

通用类图

策略模式通用类图

具体实现

策略模式就是当满足不同条件时,使用不同的策略执行。主要用到了面向对象的继承和多态机制。

抽象的策略角色定义了每个策略或算法必须具有的方法和属性。

package com.yrs.strategy;

/**
 * @Author: yangrusheng
 * @Description: 抽象策略角色
 * @Date: Created in 18:00 2019/6/22
 * @Modified By:
 */
public interface Strategy {
    void execute();
}

具体的策略角色实现了抽象策略角色,该类含有具体的算法。

package com.yrs.strategy;

/**
 * @Author: yangrusheng
 * @Description: 具体的策略角色
 * @Date: Created in 18:02 2019/6/22
 * @Modified By:
 */
public class ConcreteStrategy1 implements Strategy {
    @Override
    public void execute() {
        System.out.println("ConcreteStrategy1 execute!!!");
    }
}
package com.yrs.strategy;

/**
 * @Author: yangrusheng
 * @Description: 具体的策略角色
 * @Date: Created in 18:02 2019/6/22
 * @Modified By:
 */
public class ConcreteStrategy2 implements Strategy {
    @Override
    public void execute() {
        System.out.println("ConcreteStrategy2 execute!!!");
    }
}

Context为封装角色,也叫上下文角色,含有抽象策略成员变量,封装变化,屏蔽高层模块对策略、算法的直接访问。

package com.yrs.strategy;

/**
 * @Author: yangrusheng
 * @Description: 封装角色
 * @Date: Created in 18:04 2019/6/22
 * @Modified By:
 */
public class Context {

    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }
    //调用策略方法
    public void execute() {
        strategy.execute();
    }
}

下面看看客户端如何使用。

package com.yrs.strategy;

/**
 * @Author: yangrusheng
 * @Description:
 * @Date: Created in 18:07 2019/6/22
 * @Modified By:
 */
public class Client {

    public static void main(String[] args) {
        //声明具体策略
        Strategy strategy = new ConcreteStrategy1();
        //声明上下文对象
        Context context = new Context(strategy);
        //执行策略方法
        context.execute();
    }
}

策略模式的优点

  • 算法可以自由切换
  • 扩展性良好

策略模式的缺点

  • 策略类会随着策略的增多不断增多,可复用可能性小
  • 所有策略类都需要对外暴露,客户端需要知道使用哪个策略类

策略模式扩展

策略模式 + 简单工厂

通过上面的描述可以看出,客户端需要选择使用哪个策略,依赖了具体的策略类,违背了依赖倒置原则。我们可以结合简单工厂模式,把选择使用哪个策略类封装到Context类中。这样客户端就不需要依赖具体的策略类了。

package com.yrs.strategy;

/**
 * @Author: yangrusheng
 * @Description: 利用简单工厂模式创建策略角色
 * @Date: Created in 18:11 2019/6/22
 * @Modified By:
 */
public class ContextFactory {

    private Strategy strategy;

    //判断选择哪个策略放在了封装角色里面,客户端不做判断
    public ContextFactory(Integer type) {
        switch (type) {
            case 1:
                this.strategy = new ConcreteStrategy1();
                break;
            case 2:
                this.strategy = new ConcreteStrategy2();
                break;
            default:
                break;
        }
    }

    //调用策略方法
    public void execute() {
        strategy.execute();
    }
}

客户端使用:

package com.yrs.strategy;

/**
 * @Author: yangrusheng
 * @Description:
 * @Date: Created in 18:07 2019/6/22
 * @Modified By:
 */
public class Client {
    public static void main(String[] args) {
        // 简单工厂 + 策略模式。客户端不需要知道使用哪个策略类
        ContextFactory contextFactory = new ContextFactory(2);
        contextFactory.execute();
    }
}

同样,我们会发现把选择程序移到上下文类中后,每当增加新策略,switch语句就会新增一个选项,这其实违背了开闭原则。那么有没有什么方法,可以在增加策略类后不改变上下文类中的内容呢?这就是接下来我们要讲的内容。

策略模式 + 注解 + 反射

我们可以通过使用注解的方式标记策略类和条件的关系,然后使用反射技术实例化策略类,从而执行具体算法。

注解StrategyInfo:

package com.yrs.strategy;

import java.lang.annotation.*;

/**
 * @Author: yangrusheng
 * @Description:
 * @Date: Created in 18:40 2019/6/22
 * @Modified By:
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface StrategyInfo {
    int type() default 0;
}

具体的策略类要加上StrategyInfo注解:

package com.yrs.strategy;

/**
 * @Author: yangrusheng
 * @Description: 具体的策略角色
 * @Date: Created in 18:02 2019/6/22
 * @Modified By:
 */
@StrategyInfo(type = 1)
public class ConcreteStrategy1 implements Strategy {
    @Override
    public void execute() {
        System.out.println("ConcreteStrategy1 execute!!!");
    }
}

封装角色AnnotationReflectContext :

package com.yrs.strategy;

import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.reflections.Reflections;

/**
 * @Author: yangrusheng
 * @Description: 注解 + 反射 创建具体策略对象
 * @Date: Created in 18:44 2019/6/22
 * @Modified By:
 */
public class AnnotationReflectContext {

    private final static Map<Integer, Class> allStrategyMap = new ConcurrentHashMap<>();

    private final static String pkgName = "com.yrs.strategy";

    private Strategy strategy;

    static {
        Reflections reflections = new Reflections(pkgName);
        Set<Class<?>> annotatedClasses = reflections.getTypesAnnotatedWith(StrategyInfo.class);
        for (Class<?> classObj: annotatedClasses) {
            StrategyInfo strategyInfo = classObj.getAnnotation(StrategyInfo.class);
            allStrategyMap.put(strategyInfo.type(), classObj);
        }
    }

    public AnnotationReflectContext(Integer type) {
        if (allStrategyMap.containsKey(type)) {
            try {
                this.strategy = (Strategy) allStrategyMap.get(type).newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    //调用策略方法
    public void execute() {
        strategy.execute();
    }
}

客户端调用:

package com.yrs.strategy;

/**
 * @Author: yangrusheng
 * @Description:
 * @Date: Created in 18:07 2019/6/22
 * @Modified By:
 */
public class Client {

    public static void main(String[] args) {
        // 注解 + 反射
        AnnotationReflectContext annotationReflectContext = new AnnotationReflectContext(1);
        annotationReflectContext.execute();
    }
}

用上述的方式就可以实现新增策略类,而不用改变Context内容了。符合了开闭原则。

源代码: https://github.com/ByrsH/Design-Patterns/tree/master/Design Patterns/src/main/java/com/yrs/strategy

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值