设计模式-策略模式

146 阅读3分钟

这是我参与 8 月更文挑战的第 6 天,活动详情查看: 8月更文挑战

策略模式

一个类的行为或算法可以在运行时改变.

实现思路

通过定义接口和实现类,来实现对一个/多个行为的的不同实现方式.

传入参数,在工厂类的map中选择需要执行的对象,返回结果

主要解决的问题

多个相似算法的情况下,使用if,else所带来的复杂和难以维护

例如有两种动物,1.如果是松鼠,那么获取他的松果的数量 2.如果是熊猫,就获取他竹子的数量

如果通过if/eles 来实现

if (type.equals("松鼠")) {
		// 获取松果数量
} else if (type.equals("熊猫")) {
			//获取竹子的数量
}

当有更多动物进来时,我们就要不断修改if/else 中的代码

如何通过策略模式实现呢?

public interface Animals {

    int getFoodNumber();

}

//松鼠的实现方法
@Override
    public int getFoodNumber() {
        return songguoNum;
    }

// 熊猫的实现方法
@Override
    public int getFoodNumber() {
        return zhuziNum;
    }

使用工厂类将策略模式放入map中,然后通过传入的参数自动调用对应的行为

public class AnimalFactory {

    // 用户保存策略对象
    public static final Map<String, Object> map = new HashMap<>();

    // 根据关键词将对象存入map中
    static{
        map.put("松鼠",new Songshu());
        map.put("熊猫",new Panda());
    }

    // 根据关键词执行对应的方法.
    public static int getFoodNum(String type) {
        Animals animals = (Animals)map.get(type);
        return animals.getFoodNumber();
    }

}

执行结果

int 松果 = AnimalFactory.getFoodNum("松鼠");
        System.out.println("松果"+松果);
        int 竹子 = AnimalFactory.getFoodNum("熊猫");
        System.out.println("竹子"+竹子);

并没有解决开放封闭原则的根本问题,虽然不需要在if/else 代码中添加判断,但是还是要创建新的策略对象并且在工厂类中将新的策略对象放入map中

使用spring原生注解来实现

1.将实现类注入容器 @Component

@Component(value = "松鼠")
public class Songshu implements Animals{

@Component(value = "熊猫")
public class Panda implements Animals{

2.改变工厂类

给map<String,Animals>添加@Autowied 注解,那么Animals接口的所有注入的实现类都会放入map中

@Component
public class NewAnimalFactory {

// // 关键功能 Spring 会自动将 EntStrategy 接口的类注入到这个Map中
@Autowired
public Map<String,Animals> map;

// 存入的值默认为bean id ,可以通过@Component(value="")来设置
public int getFoodNum(String type) {
	Animals animals = map.get(type);
        return animals.getFoodNumber();
    }

}

3.使用

注意,1.要启动服务,不要直接main函数运行,都没有开启spring服务

2.使用@Autowied来引入工厂,不要使用new来新建工厂,都没有注入spring

@Autowired
    NewAnimalFactory newAnimalFactory;

    @Test
    public void AnimalsTest() {
        int 竹子 = newAnimalFactory.getFoodNum("熊猫");
        System.out.println("竹子"+竹子);
        int 松果 = newAnimalFactory.getFoodNum("松鼠");
        System.out.println("松果"+松果);
    }

第二种方法,使用list方法实现

相比较于map方法,list方法不需要给实现类的bean设置名称,接口新建一个方法,实现类返回一个标识符(类似于设置名称)

String typeName();

// 熊猫实现
@Override
    public String typeName() {
        return "熊猫";
    }

// 松鼠实现
@Override
    public String typeName() {
        return "松鼠";
    }

将实现类注入到list中,对list进行遍历比较,获得对应的数据

@Autowired
    public List<Animals> animalsList;

public Animals getAnimals(String type) {
        for (Animals animals:animalsList) {
            if (type.equals(animals.typeName())) {
                return animals;
            }
        }
        return null;
    }

执行

@Test
    public void getAnimals() {
        Animals 松鼠 = newAnimalFactory.getAnimals("松鼠");
        System.out.println(松鼠);
        Animals 熊猫 = newAnimalFactory.getAnimals("熊猫");
        System.out.println(熊猫);
        Animals 猴子 = newAnimalFactory.getAnimals("猴子");
        System.out.println(猴子);
    }

策略模式SPI实现开闭原则

直接迭代实现类,并且进行判断获得对应的对象

public static Animals getAnimals(String type) {
        ServiceLoader<Animals> load = ServiceLoader.load(Animals.class);
        Iterator<Animals> iterator = load.iterator();
        while(iterator.hasNext()) {
            Animals animals = iterator.next();
            if (type.equals(animals.typeName())) {
                return animals;
            }
        }
        return null;
    }

执行

public static void main(String[] args) {
        Animals 松鼠 = SPIAnimalsFactory.getAnimals("松鼠");
        System.out.println("松鼠:"+松鼠);

        Animals 熊猫 = SPIAnimalsFactory.getAnimals("熊猫");
        System.out.println("熊猫:"+熊猫);

    }

优缺点

优点:1.避免多重判断 2.扩展性良好,只需要实现接口即可 3.封装内容可以自由切换

缺点:只增加一个选择,就需要增加一个新的类实现接口.