小白看了Java设计模式一书,晕晕乎乎,好像懂了,又好像啥都没懂。对照着书本一行行手打代码,完毕,执行,ok,设计模式已掌握。但到了项目中却又茫然。
小白我啊,今天尝试一下在自己的demo项目中使用策略模式,手动狗头。
我们要做什么,首先得知道它是什么吗,让我们使用bing来搜索,bing来搜索,bing来搜索(重要的事情说三遍,ps:反正不要用baidu)
来自菜鸟教程
小白具象一下,咱去饭店吃饭,对着菜单咔咔点,上菜了桌面有蒸羊羔、蒸熊掌、蒸鹿尾儿(小白搁着“报蔡明”呢);银耳羹;一大瓶可乐;还要了一大碗米饭;
小白和他那个假设存在的女朋友小红要吃饭了啊。那么小白想吃一口蒸熊掌,小白用筷子夹一了块,津津有味的吃起来了;小红想喝一口银耳羹,小红用勺子舀了一勺文雅的送入了樱桃小口;小白突然噎住了,还好还好,有大瓶可乐,于是小白赶忙摆好杯子倒上了满满一杯可乐,猛喝一大口,呼呼,舒服。
小白吃饱喝足了,要开始分解了。在这个模型中,小白吃饭是明确目的,但是吃的东西会有很多,不一样的菜可能会用到不一样的餐具,假设这家饭店只有筷子,小白会不会为了吃到银耳羹而在吃完了蒸熊掌后换一家有勺子的店呢,那如果需要几十种餐具才能吃的饭,岂不是要跑几十家?
在这个模型中,我们将小白的行为抽离出来并映射到策略模式中:
1)小白吃饭是整体目的
2)小白先吃什么,是一种策略(可以理解为后面用什么吃的前提行为,代码中就是if里面的判断)
3)这个店家有各种菜不同餐具(提供给小白选择的平台,小白不用关心怎么做的,餐具哪里买的)
模型说完,接下来用代码来实现
首先是一个通用接口
package com.example.demo.service;
/**
* @author Niezj
*/
public interface EatFoodService {
/**
* 小白如何吃到这道菜
* @param object 传入参数,自己根据业务进行修改
* @return 返回信息 自己根基业务需要自己修改
* */
String eatFoodBehavior(Object object);
/**
* 小白要吃的这道菜的名字
*
* @return string (菜名字)
* */
String eatFoodName();
}
接下来是几个不同的实现类
1)吃蒸熊掌
package com.example.demo.service.impl;
import com.example.demo.service.EatFoodService;
import org.springframework.stereotype.Service;
/**
* @author Niezj
* 吃蒸熊掌
*/
@Service
public class EatFoodBearHandServiceImpl implements EatFoodService {
@Override
public String eatFoodBehavior(Object object) {
System.out.println("\n");
System.out.println("\n");
System.out.println("小白用筷子夹了一块蒸熊掌,并说到,真好吃");
System.out.println("\n");
return "吃蒸熊掌";
}
@Override
public String eatFoodName() {
//demo 使用,具体业务请严谨命名
return "蒸熊掌";
}
}
2)喝银耳羹
package com.example.demo.service.impl;
import com.example.demo.service.EatFoodService;
import org.springframework.stereotype.Service;
/**
* @author Niezj
* 银耳羹具体实现类
*/
@Service
public class EatFoodTremellaSoupServiceImpl implements EatFoodService {
@Override
public String eatFoodBehavior(Object object) {
System.out.println("\n");
System.out.println("\n");
System.out.println("(小白假设的女朋友)小红和了一口银耳羹");
System.out.println("\n");
return "喝银耳羹";
}
@Override
public String eatFoodName() {
//demo 使用,具体业务请严谨命名
return "银耳羹";
}
}
3)喝可乐
package com.example.demo.service.impl;
import com.example.demo.service.EatFoodService;
import org.springframework.stereotype.Service;
/**
* @author Niezj
* 大佬喝阔乐
*/
@Service
public class EatFoodCokeServiceImpl implements EatFoodService {
@Override
public String eatFoodBehavior(Object object) {
System.out.println("\n");
System.out.println("\n");
System.out.println("小白吨吨吨喝了一瓶可乐,然后嗝了一声");
System.out.println("\n");
return "喝可乐";
}
@Override
public String eatFoodName() {
//demo 使用,具体业务请严谨命名
return "可乐";
}
}
4)最后来一个没钱只能吃大米饭就蒜的默认实现类
package com.example.demo.service.impl;
import com.example.demo.service.EatFoodService;
import org.springframework.stereotype.Service;
/**
* @author Niezj
* 小白因为没带钱,只能吃大米饭就大蒜了
*/
@Service
public class EatFoodRiceServiceImpl implements EatFoodService {
@Override
public String eatFoodBehavior(Object object) {
System.out.println("\n");
System.out.println("\n");
System.out.println("小白摸摸口袋,对老板说来一碗米饭不要钱的蒜来两头");
System.out.println("\n");
return "吃大米饭就蒜";
}
@Override
public String eatFoodName() {
//demo 使用,具体业务请严谨命名
return "大米饭就蒜";
}
}
将以上实现类封装到一个全局唯一的map当中(承上启下的文件)
package com.example.demo.config;
import com.example.demo.service.DesignService;
import com.example.demo.service.EatFoodService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
* @author Niezj
*/
@Component
public class EatFoodConfig {
@Resource
private ApplicationContext applicationContext;
/**
* spring中一个接口多个实现的时候,可以用
* @Qualifier("name") name为service注解里面的别名,没有的话就是默认首字母小写的类名
* autowierd 配和 的形式精准说要使用哪个实现类
* 当然还有其他精准注入的方式,比如@resource(name = "同上名称取值方式")
*
*/
@Qualifier("eatFoodRiceServiceImpl")
@Autowired
private EatFoodService eatFoodService;
/**
* 全局唯一eatFood实现类型map
*/
public static final Map<String, EatFoodService> EAT_FOOD_SERVICE_MAP = new HashMap<>(16);
/**
* 项目启动的时候先去将所有实现了吃饭的文件获取到并放到全局唯一的一个map中
*/
@PostConstruct
public void getAllFood() {
Map<String, EatFoodService> models = applicationContext.getBeansOfType(EatFoodService.class);
models.forEach((k, v) -> {
EatFoodService eatFoodService = applicationContext.getBean(k, EatFoodService.class);
// 自定义业务字典code,用于key的名称
String serviceName = eatFoodService.eatFoodName();
if (StringUtils.isNotBlank(serviceName)) {
EAT_FOOD_SERVICE_MAP.put(serviceName, eatFoodService);
}
});
}
/**
*
* 开放接口,提供输入判断该什么菜用什么吃,具体的策略实现
*
*/
public Object selectEatFood(String eatFoodName,Object obj){
// map获取,给定一个默认的实现,防止空指针异常
// 具体业务具体实现,自己修改返回信息
return EAT_FOOD_SERVICE_MAP.getOrDefault(eatFoodName,eatFoodService).eatFoodBehavior(obj);
}
}
以上完成策略模式
那么赶紧来看看具体实现效果吧 ,写一个Controller
package com.example.demo.controller;
import com.example.demo.config.EatFoodConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author ADMIN
*/
@RestController
public class EatFoodController {
@Autowired
private EatFoodConfig eatFoodConfig;
@GetMapping("eatFood")
public void eatFood(String eatFoodName) {
Object obj = new Object();
eatFoodConfig.selectEatFood(eatFoodName, obj);
}
}
调用接口看一下正不正确,我们先输入蒸熊掌,然后喝银耳羹,再喝可乐,最后胡乱输一个字符串看看能不能吃大米饭
1 蒸熊掌
2 银耳羹
3 喝可乐
4 测试一下我们兜底的吃大米饭会不会生效,这里设置了当输入的策略名称找不到对应的实现时候吃大米饭当实现
以上完成策略模式的简单实现。
最后引用高中写作文给出的小故事:
国内最大日化公司引进了一条国外肥皂生产线。这条生产线能将肥皂从原材料的加入直到包装装箱自动完成。
但是,意外发生了。销售部门反映有的肥皂盒是空的。于是,这家公司立刻停止了生产线,并与生产线制造商取得联系。得知这种情况在设计上是无法避免的。
经理要求工程师们解决这个问题。于是成立一个以几名博士为核心、十几名研究生为骨干的团队。知识类型涉及光学、图像识别、自动化控制、机械设计等等门类。
在耗费数十万后,工程师们在生产线上一套X光机和高分辨率监视器,当机器对X光图像进行识别后,一条机械臂会自动将空盒从生产线上拿走。
另外一家私人企业司也遇到了同样的情况,老板对管理生产线的小工说:你一定要解决这个问题。于是这个小工找来一台电风扇,摆在生产线旁,另一端放上一个箩筐。装肥皂的盒子逐一在风扇前通过,只要有空盒子便会被吹离生产线,掉在箩筐里。 问题解决之。
两种做法并没有错,我们不能批判说小成本能解决的问题花这么多钱,毕竟不同的处境有不同的做法不是吗?