使用策略模式和工厂模式动态绑定多实现类实例

  摘要: 由于业务场景复杂,一个算法需要开发行为变化多端的多个实现类,然后在系统运行时根据不同场景装载不同的类实例。为了使源码具有更好的可扩展性和可重用性,在借鉴前人处理方法的基础上,介绍在Spring项目中,基于策略模式和工厂模式实现动态匹配相关实现类示例,并调用其中的函数。

前言

  策略模式(Strategy Pattern)属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的、独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。

  策略模式是对算法的包装,是把使用算法的对象和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是:“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。下面就以一个示意性的实现讲解策略模式实例的结构。

  主要解决的问题是:在有多种相似算法的情况下,使用 if...else 会增加系统复杂性,降低可扩展性。如果有新的策略,只需要在工厂类加载的包中添加其实现类即可。

  使用场景:一个系统的某个算法接口有许多许多实现类,而它们的不同之处只是它们形形色色的行为。

  策略模式应用实例: 1、诸葛亮的三个锦囊,每一个锦囊都是一个神机妙算的策略。 2、旅行时选择顺风车、飞机等,每一种旅行方式都是一个策略。

  欲了解更多相关知识点请移步《Spring 动态绑定多实现类实例综述》。

业务场景回顾

  需求描述:定制一个绘图工具,她根据客户端发送的指令可以画出正方形、矩形、圆形和三角形等各种各样的几何图形。例如,当客户端需要绘制三角形的时候,就调用绘制三角形的方法;当需要绘制圆形的时候,就调用绘制圆形的方法。

  策略模式定义了一组算法,把它们一个个封装起来, 并且使它们可相互替换。厂模式用于封装和管理对象的创建,是一种创建型模式。

/**
 * 画图工具接口
 * @author Wiener
 * @date 2021/1/9
 */
public interface Shape {
    /**
     * 画图实现方法
     */
    void draw();
}

  定义一个抽象角色ShapeStrategyFactory,由一个接口和一个Map 变量构成。此角色给出所有的具体策略类所需的实现类实例。

// ---我是分割线---

import com.eg.wiener.service.Shape;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;


/**
 * 策略工厂
 *
 * @author Wiener
 * @date 2021/1/13
 */
public class ShapeStrategyFactory {

    private static Map<String, Shape> SHAPE_REGISTERS = new HashMap<>();

    /**
     * 由策略工厂注入Shape接口的不同实现类
     * @param code 实现类编码
     * @param shapeImpl 实现类
     */
    public static void register(String code, Shape shapeImpl) {
        if (StringUtils.isNotBlank(code)) {
            SHAPE_REGISTERS.put(code, shapeImpl);
        }
    }
    public static Shape get(String code) {
        return SHAPE_REGISTERS.get(code);
    }
}

// ---我是分割线---

import com.eg.wiener.service.Shape;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

/**
 * TODO
 *
 * @author Wiener
 * @date 2021/1/7
 */
@Service
public class Circle implements Shape {

    @Override
    public void draw() {
        System.out.println("Inside Circle::draw() method.");
    }

    @PostConstruct
    public void init() {
        ShapeStrategyFactory.register("circle", this);
    }

}

// ---我是分割线---
import com.eg.wiener.service.Shape;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

/**
 * 长方形
 *
 * @author Wiener
 * @date 2021/1/7
 */
@Service
public class Rectangle implements Shape {

    @Override
    public void draw() {
        System.out.println("Inside Rectangle::draw() method.");
    }
    @PostConstruct
    public void init() {
        ShapeStrategyFactory.register("rectangle", this);
    }
}
// ---我是分割线---
import com.eg.wiener.service.Shape;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

/**
 * TODO
 *
 * @author Wiener
 * @date 2021/1/7
 */
@Service
public class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("Inside Square::draw() method.");
    }
    @PostConstruct
    public void init() {
        ShapeStrategyFactory.register("square", this);
    }
}
// ---我是分割线---
import com.eg.wiener.service.Shape;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

/**
 * 三角形
 *
 * @author Wiener
 * @date 2021/1/7
 */
@Service
public class Triangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Inside Triangle::draw() method.");
    }
    @PostConstruct
    public void init() {
        ShapeStrategyFactory.register("triangle", this);
    }
}

  这段代码的关键是ShapeStrategyFactory类,它是一个策略工厂,里面定义了一个全局的map变量 SHAPE_REGISTERS,在所有Shape的实现类中注册当前实例到map中,然后在调用的地方通过ShapeStrategyFactory类的Shape get(String code)根据code从map获取画图类实例即可。测试用例如下:

    //通过注解把不同实现类的bean注入变量,与策略工厂做对比
    @Autowired
    private Map<String, Shape> beanMap;	
    @GetMapping("/ssDraw")
    public void ssDraw(String code) {
        beanMap.get(code).draw();		
        ShapeStrategyFactory.get(code).draw();
    }

  我们也可以通过注解把不同实现类的bean注入变量,与策略工厂对比后发现,两者可以完成同样的工作,但是前者代码工作量更少,主要是不需要在每个实现类加入注册类的非业务代码。

结束语

  欢迎点赞阅读,一同学习交流;若有疑问,请在文章下方留下你的神评妙论!

Reference

posted @ 2021-02-08 20:41  楼兰胡杨  阅读(1621)  评论(0编辑  收藏  举报