一、简述

日常开发中,会遇见类似于使用不同方式发送消息,例如:邮件、短信。再或者碰见文章分享之类的需求。那么我们平时如果不是用设计模式来做的情况下,会出现很多个 if-else 或者 switch 语句块。这样的话,代码耦合性也会非常高,将来再增加一个需求,则会导致一直增加判断语句块。也违反了面向对象的开闭原则。那么我们有什么好的解决方式呢?今次,则用反射+策略模式来重构一下代码,使之更加灵活。

如果有代码更好的优化方式,请下方留言。

码云:Demo地址

二、不使用反射的策略模式

抽象策略角色(接口)

public interface MyStragtegy {
String play();
}
  • 1

  • 2

  • 3

具体实现策略

CatStragtegy实现CatStragtegy接口

public class CatStragtegy implements MyStragtegy {
@Override
public String play() {
String str = "猫玩毛线球,玩的一团糟";
return str;
}
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

DogStragtegy实现CatStragtegy接口

public class DogStragtegy implements MyStragtegy {
@Override
public String play() {
String str = "狗狗玩飞盘,玩的很开心";
return str;
}
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

环境角色(Content)

public class MyStragtrgyContent  {

private String type;//策略方式

private MyStragtegy myStragtegy;//策略接口

public MyStragtrgyContent(String type, MyStragtegy myStragtegy) {
this.type = type;
this.myStragtegy = myStragtegy;
}

public MyStragtegy getMyStragtegy() {
return myStragtegy;
}

public boolean option(String type){
return this.type.equals(type);
}

}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

业务逻辑代码

  1. service代码

@Component
public class StragtegyService {

private static List<MyStragtrgyContent> stragtegies = new ArrayList<>();

static {
stragtegies.add(new MyStragtrgyContent("cat",new CatStragtegy()));
stragtegies.add(new MyStragtrgyContent("dog",new DogStragtegy()));
}

public String play(String type){
List<MyStragtrgyContent> collect = stragtegies.stream().filter(x -> x.option(type)).collect(Collectors.toList());
if (collect!=null&&collect.size()>0){
return collect.get(0).getMyStragtegy().play();
}else {
return "我们还没有这个宠物哟!~";
}
}
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

2.Controller代码

@Controller
public class DemoController {

@Autowired
private StragtegyService stragtegyService;

@ResponseBody
@RequestMapping("play")
public String play(String type){
if (type!=null&&!"".equals(type)){
return stragtegyService.play(type);
}else {
return "要选择一起玩的宠物哟!~";
}
}
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

总结

代码耦合性太高,每加一个宠物则需要再service中的静态代码块添加实例从而导致,我们如果忘记加了,则实现不了新宠物的陪玩。也违反了类的开闭原则。

接下来修改代码用spring反射机制实现

三、使用Spring反射机制实现策略模式

接口不做修改,使用原来的接口(策略角色)

public interface MyStragtegy {
String play();
}

修改具体实现策略(交给spring管理,起别名是为了方便取)**

@Component("cat")//如果用反射机制的情况下需要交给spring管理
public class CatStragtegy implements MyStragtegy {
@Override
public String play() {
String str = "猫玩毛线球,玩的一团糟";
return str;
}
}
@Component("dog")//如果用反射机制的情况下需要交给spring管理
public class DogStragtegy implements MyStragtegy {
@Override
public String play() {
String str = "狗狗玩飞盘,玩的很开心";
return str;
}
}

修改环境角色(上下文)

@Component
public class MyStragtrgyReflexContent implements ApplicationContextAware,InitializingBean {

private Map<String,MyStragtegy> beanMap ;

private ApplicationContext applicationContext;


/**
* 实现ApplicationContextAware接口,Spring容器会在创建MyStragtrgyReflexContent类之后,
* 自动调用实现接口的setApplicationContextAware()方法,
* 调用该方法时,会将ApplicationContext(容器本身)作为参数传给该方法,
* 我们可以在该方法中将Spring传入的参数ApplicationContext赋给MyStragtrgyReflexContent对象的applicationContext实例变量,因此接下来可以通过该applicationContext实例变量来访问容器本身。
*
* 作者:那我懂你意思了_de16
* 链接:https://www.jianshu.com/p/e435dd6c7339
* 来源:简书
* 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
* @param applicationContext
* @throws BeansException
*/

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}

/**
* 实现InitializingBean接口,该接口提供了afterPropertiesSet方法。
* spirng容器在初始化bean的时候会执行afterPropertiesSet方法,
* 我们可以在该方法中调用applicationContext接口提供的getBeansOfType方法获得实现MyStragtegy类的Bean,将之存储至map集合中
*
* 作者:那我懂你意思了_de16
* 链接:https://www.jianshu.com/p/e435dd6c7339
* 来源:简书
* 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
* @throws Exception
*/

@Override
public void afterPropertiesSet() throws Exception {
Map<String,MyStragtegy> map = applicationContext.getBeansOfType(MyStragtegy.class);
this.beanMap = map;
}

public MyStragtegy getMyStragtegy(String beanName){
return this.beanMap.get(beanName);
}



}
  • MyStragtrgyReflexContent 类实现ApplicationContextAware接口,Spring容器会在创建MyStragtrgyReflexContent 类之后,自动调用实现接口的setApplicationContextAware()方法,调用该方法时,会将ApplicationContext(容器本身)作为参数传给该方法,我们可以在该方法中将Spring传入的参数ApplicationContext赋给MyStragtrgyReflexContent 对象的applicationContext实例变量,因此接下来可以通过该applicationContext实例变量来访问容器本身。

  • 实现InitializingBean接口,该接口提供了afterPropertiesSet方法。spirng容器在初始化bean的时候会执行afterPropertiesSet方法,我们可以在该方法中调用applicationContext接口提供的getBeansOfType方法获得实现MyStragtegy类的Bean,将之存储至map集合中。

  • 摘录自https://www.jianshu.com/p/e435dd6c7339

业务逻辑代码

  1. Service代码

这里的type就作为BeanName直接传过去了,如果不想这么做也可以自己转换下

@Service
public class StragtegyReflexService {

@Autowired
private MyStragtrgyReflexContent reflexContent;


public String play(String type){
MyStragtegy myStragtegy = reflexContent.getMyStragtegy(type);
if (myStragtegy!=null){
return myStragtegy.play();
}else {
return "还没有这个宠物哟!~";
}
}

}
  1. Controller

@Controller
public class DemoController {

@Autowired
private StragtegyService stragtegyService;

@ResponseBody
@RequestMapping("play")
public String play(String type){
if (type!=null&&!"".equals(type)){
return stragtegyService.play(type);
}else {
return "要选择一起玩的宠物哟!~";
}
}

@ResponseBody
@RequestMapping("playReflex")
public String playReflex(String type){
if (type!=null&&!"".equals(type)){
return stragtegyService.play(type);
}else {
return "要选择一起玩的宠物哟!~";
}
}

}

总结

通过使用spring反射实现策略模式,简化了代码,也让开发人员更专注的写业务代码了,这样如果我们增加了一个其他宠物的情况下,也只需要增加一个实现类就可以了。

阿里面试官:HashMap中的8和6的关系(1)

各大互联网企业Java面试题汇总,如何成功拿到百度的offer

阿里面试题剖析,如何保证消息不被重复消费?

一文搞定并发面试题

深入理解JVM垃圾收集机制,下次面试你准备好了吗







本文分享自微信公众号 - Java高级架构师(java968)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

08-31 14:46