设计模式:策略模式

143 阅读4分钟

定义

策略模式属于对象的行为模式,针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以互相替换。根据不同情况,将算法分割开来,委派给不同的对象,即一系列的具体策略类,在其上层还有一个共同的抽象策略类。

如下为策略模式的通用类图:

409df687-2cd5-4ef6-8ab8-257390d38c53_strategy.webp

Context:上下文角色,负责引用需要的Strategy。 (策略执行者)

Strategy:抽象策略角色。一般为接口或抽象类,用来提取策略的共同行为,具体策略实现类需实现其接口。

ConcreteStrategy:具体策略角色。实现或继承抽象策略角色,针对不同的情况进行逻辑处理。

策略模式的简单实现

// 上下文角色(环境)
    public class Context{
        private final Strategy strategy;

        public Context(Strategy strategy){
            this.strategy = strategy;
        }

        public void go(){
            strategy.go();
        }

    }

    // 抽象策略角色
    public interface Strategy{
        void go();
    }

    // 具体策略角色A
    public class ConcreteStrategyA implements Strategy{

        @Override
        public void go() {
            System.out.println("go A");
        }
    }

    // 具体策略角色B
    public class ConcreteStrategyB implements Strategy{

        @Override
        public void go() {
            System.out.println("go B");
        }
    }

使用:

public static void main(String[] args) throws IOException {
    Strategy strategy = new ConcreteStrategyA();
    Context context = new Context(strategy);
    context.go();
}

策略模式的使用场景及案例

策略模式可以根据不同处理逻辑来生成各种不同的具体策略类,而if else也是通过判断各种不同情况来进行不同的逻辑处理。所以,策略模式肯定是对比ifelse更好的处理方式。

通知很常见吧,常见的聊天软件,还是各种游戏都有各种各样的通知,比如你是一个博主,有人点赞了你的博文,那系统要发送一个有人点赞的通知,有人收藏了你的博客,系统要发送一个有人收藏你的博客的通知,有人评论了你的博客,系统要发送...,这种通知就很多了。按照一般情况下的实现方式:

if(点赞){
    // 发送点赞通知
}else if(收藏){
    // 发送收藏通知
}else if(评论){
    // 发送评论通知
}else if(..){
    ....
}

这种方式不说代码美不美观吧,拓展也是及其不方便,增加一个通知则需要对原有实现类进行修改,和开闭原则不相符。对这些通知处理的管理也及其不方便。

这时候策略模式就派上了用场,那如何用策略模式进行修改呢?

首先要定义一个抽象接口,无论是什么通知,共同行为都是发送通知,所以:

public interface INotifyStrategy{
    /**
     * 发送通知
     */
    void sendNotify();
    /**
     * 获取类型
     */
    String getType();
}

定义好抽象接口后,就需要根据不同的处理逻辑来生成不同的具体实现类,这里就拿点赞和收藏两种情况举例。

/**
 * 点赞通知具体实现
 */
public class LikedNotifyStrategy implements INotifyStrategy{
    @Override
    public void sendNotify() {
        System.out.println("发送点赞通知");
    }
    
    @Override
    public String getType() {
        return "liked";
    }
}
/**
 * 收藏通知具体实现
 */
public class CollectedNotifyStrategy implements INotifyStrategy{
    @Override
    public void sendNotify() {
        System.out.println("发送收藏通知");
    }
    
    @Override
    public String getType() {
        return "collected";
    }
}
/**
 * 通知上下文角色
 */
class NotifyContext{
    private final INotifyStrategy notifyStrategy;

    public NotifyContext(INotifyStrategy notifyStrategy){
        this.notifyStrategy = notifyStrategy;
    }
    
    public void sendNotify(){
        notifyStrategy.sendNotify();
    }
}

如果按照之前的方式,则还是无法避免通过对if else的判断,选择具体实现类。这里可以结合工厂模式,引入一个策略工厂,可以根据不同的类型自动选择相应的具体实现类。

@Component
public class NotifyStrategyFactory implements ApplicationContextAware {
    private static Map<String,INotifyStrategy> strategyMap;

    /**
 	 * 通过上下文获取容器中注册的INotifyStrategy类,并将其放入map中。
 	 */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, NotifyStrategy> map = applicationContext.getBeansOfType(INotifyStrategy.class);
        strategyMap = new HashMap<String, INotifyStrategy>();
        map.forEach((key, value) -> strategyMap.put(value.getType(), value));
    }

    private NotifyStrategyFactory(){}

    public static NotifyStrategy getInstance(String type){
        return strategyMap.get(type);
    }
}

进行使用

public static void main(String[] args) {
        String type = "liked";
        NotifyContext notifyContext = new NotifyContext(NotifyStrategyFactory.getInstance(type));
        notifyContext.sendNotify();
    }

相较于之前if else的写法,是不是好看了很多?

优缺点

优点:

  1. 策略模式符合开闭原则
  2. 避免使用大量的if else等条件判断语句
  3. 易于拓展

缺点:

  1. 必须先行知道所有的策略类,需自行选择使用哪个策略类。
  2. 会多出大量的策略类,加大维护成本和难度。