目录
3.5、写一个实体类、一个service和一个controller,来验证我们的创新“策略模式”
1、代码设计必须讲究解耦合
为什么要解耦合,因为耦合深的代码不仅难以维护,不好扩展,还无法复用。Java设计之初就提供了抽象类和接口来作为粘合剂,做解耦的工具。
解耦合并不是切断联系,100%的解耦就是制造信息孤岛,没有信息的交换,组件就会相互孤立,系统也就不能称为系统。
人们常说:藕断丝连,这是来形容两个人之间的感情的。我觉得来概括好的设计更为贴切,断开的两段藕相对独立,每一段藕都是一个组件,而中间的丝就是他们之间的联系,藕与丝一起组成一个完整的系统。
2、解耦到处有,只遇有心人
蜂巢快递柜将快递员和收快递的人解耦了;服务员将厨师和食客解耦了;医院的叫号系统把病患和医生解耦了;这是生活中的例子。
系统设计中一样有非常多的解耦的例子。
生产者和消费者通过消息中间件来实现解耦;
java的线程池技术将任务与执行单元解耦;
Spring容器技术将JOPO的组装和定义解耦;
Tomcat将HTTP等协议和具体的web业务解耦;
设计模式中,抽象工厂、策略模式等都是解耦思想的优秀应用。
3、最好的设计:藕断丝连
最近就有一个难题困扰着组内的同事。
我们写了一个页面,可以代理用户发布的接口。其实后台就是做一些安全处理、报文重组等业务后使用HttpClient呼叫用户真实的接口。这个主要的业务流程是没有什么问题的,关键在于不同单位对外发布的接口会有自己的安全策略,需要代理方按照发布方的安全协议去实现。一个不好的设计就是在主流程代码中把不同部门的安全协议引入,代码里会有一堆的if 、else。而且随着发布接口的部门数量的增加,我们就需要不断修改我们的业务代码(主流程中增加else),这简直槽糕透了,我不能容忍这样的做法。
认真分析完毕这个需求后,我就这件事情拆分成主流程需求和个性化需求。将个性化需求塞进“策略”,我最初想设计一个xml注册表,使用java的工具类读取并解析这个注册表,然后将自定义的“组件”放置在HashMap中(自己的容器吧),主流程代码根据一个入参去查询这个HashMap,然后根据获取的信息找到对应的策略持有者,最后调用策略持有者的方法,就可以解析个性化的“安全协议”。这其实就是“策略模式”,也许有点变种,我将new然后add变成了读取xml然后解析并放到hashMap中。
后来翻阅spring源码的时候,发现读取并且解析xml,然后把解析得到的组件放置到一个容器中正是spring最根本、最成熟的技术,那么,我何必自己实现一遍呢?用现成的它不香吗?于是有了下边这个dome。
3.1 写xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<context:property-placeholder location="classpath*:properties/redis-config.properties" />
<bean id="policStrategy" name="police" class="com.miller.studyredis.strategy.PolicStrategy"/>
</beans>
3.2 写一个注册中心,加载并解析Bean
package com.miller.studyredis.registercenter;
import com.miller.studyredis.strategy.CommonStrategyImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
/**
* @program: oxnewbc
* @description: 策略注册中心
* @author: Miller.FAN
* @create: 2021-07-28 15:12
**/
@Component("registerCenter")
public class RegisterCenter {
//spring启动时,将注册表中的策略bean解析完毕后,加载到spring容器
private static ApplicationContext ac = new ClassPathXmlApplicationContext("spring/registration.xml");
//根据用户参数查找解析策略所在的bean
public CommonStrategyImpl getStrategyBean(String name) {
return (CommonStrategyImpl) ac.getBean(name);
}
}
3. 3、写一个接口,所有策略的“爸爸”
package com.miller.studyredis.strategy;
public interface CommonStrategyImpl {
//用户根据策略名称可以找到相应的策略
String strategy(String strategyName,String text) throws Exception;
}
3.4、写一个“儿子”,实现“爸爸”的愿望
package com.miller.studyredis.strategy;
import jodd.util.StringUtil;
/**
* @program: oxnewbc
* @description: 公安局的请求报文安全策略
* @author: Miller.FAN
* @create: 2021-07-27 17:47
**/
public class PolicStrategy implements CommonStrategyImpl{
private final static String strategyName = "police";
//获取注册中心,从注册中心获取HashMap中的信息,拿到bean的id,就可以使用之
@Override
public String strategy(String strategyName,String text) throws Exception {
System.out.println("策略名称" + strategyName);
System.out.println("请求处理的报文" + text);
if(StringUtil.isBlank(strategyName)) {
throw new Exception("strategyName can't be null or '' ");
}
if(StringUtil.isBlank(text)) {
throw new Exception("text can't be null or '' ");
}
if(strategyName.equals(strategyName)) {
//Map<String,Object> paras = JSON.par(text,HashMap.class);
//do nothing
}
return text;
}
}
3.5、写一个实体类、一个service和一个controller,来验证我们的创新“策略模式”
package com.miller.studyredis.entity;
/**
* @program: oxnewbc
* @description: 请求报文的实体类
* @author: Miller.FAN
* @create: 2021-07-28 16:38
**/
public class TextBody {
//根据该项选择使用的策略
private String name;
private String text;
public TextBody() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
@Override
public String toString() {
return "TextBody{" +
"name='" + name + '\'' +
", text='" + text + '\'' +
'}';
}
}
package com.miller.studyredis.service;
import com.miller.studyredis.registercenter.RegisterCenter;
import com.miller.studyredis.strategy.CommonStrategyImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @program: oxnewbc
* @description: 策略验证接口
* @author: Miller.FAN
* @create: 2021-07-28 16:22
**/
@Service
public class StrategyService {
@Autowired
private RegisterCenter regCenter;
public String requestHandler(String name,String body) {
CommonStrategyImpl strategy = regCenter.getStrategyBean(name);
String ret = "";
try {
ret = strategy.strategy(name,body);
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
}
package com.miller.studyredis.registercenter;
import com.miller.studyredis.strategy.CommonStrategyImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
/**
* @program: oxnewbc
* @description: 策略注册中心
* @author: Miller.FAN
* @create: 2021-07-28 15:12
**/
@Component("registerCenter")
public class RegisterCenter {
//spring启动时,将注册表中的策略bean解析完毕后,加载到spring容器
private static ApplicationContext ac = new ClassPathXmlApplicationContext("spring/registration.xml");
//根据用户参数查找解析策略所在的bean
public CommonStrategyImpl getStrategyBean(String name) {
return (CommonStrategyImpl) ac.getBean(name);
}
}
4、启动项目
我写的是一个spring boot工程

5、用postman测试一下

6、看一下日志

日志中的文字正是我们希望打印的内容,而Postman测试返回的报文也正是我们police对应的策略返回的。dome达到了我们设计的目标。
在成为架构师的道路上不断探索,有所创新。我就是Miller.Fan,一个设计狂人。
客观,站住,点个赞再走,我们拒绝白piao的。

被折叠的 条评论
为什么被折叠?



