工作中遇到的设计模式
工作中遇到的设计模式
1.策略模式+模板设计模式;
项目要求:
erp项目是面向网店用户的,就支持淘宝,京东,拼多多等多电商平台 ,而我是负责平台这一块,也就是我需要从各个电商的平台拉取数据,为减少大量if 因此使用策略模式,为统一代码执行逻辑 因此采用模板设计模式
模板类:SalesAbstract
代码流程:
1.校验权限等数据(父类)
2.拉取平台数据(实现类特制)
3.根据平台数据进行解析成erp项目的数据结构(实现类特制)
4.解析后的数据进行数据库的操作(父类)
方法说明:
1.该类中的supports()方法返回值就是各个平台的code,由各种平台进行实现
2.pullAfterSalesOrder()该方法就是的具体实现就是各个平台的具体的sdk的代码
3.afterSalesOrderProcessing 该方法是具体的方法了,该方法里面的参数就是erp项目所定义的bean对象了,也就是说代码在执行完了流程3将各种平台的数据转化成了项目定义的数据后直接调用这个方法就行了
public abstract class SalesAbstract {
//子类实现该方法返回各平台code
public abstract Integer supports();
//模板形式
public void exec(){
//校验权限参数 父类方法
check();
//拉取平台数据 抽象方法
pullAfterSalesOrder();
//根据平台数据进行解析成erp项目的数据结构 抽象方法
dataFormat();
//解析后的数据进行数据库的操作 父类方法
analysisOfAfterAales()
}
}
SalesSolverChooser 策略模式选择器 该类的意义就在于register()方法上,其中的各种子类(淘宝/抖音等) 实现了SalesAbstract 类,然后注册到SalesSolverChooser中的map中,其中的key就是各个平台的标识码,由SalesAbstract的supports() 方法来实现,然后前端在调用请求的时候,需要传入各平台的标识码,
public class SalesSolverChooser implements ApplicationContextAware {
private Map<Integer, SalesAbstract> chooseMap = new HashMap<>();
private ApplicationContext context;
public SalesAbstract choose(Integer type) {
return chooseMap.get(type);
}
@PostConstruct
public void register() {
Map<String, SalesAbstract> solverMap = context.getBeansOfType(SalesAbstract.class);
for (SalesAbstract solver : solverMap.values()) {
for (Integer support : solver.supports()) {
chooseMap.put(support, solver);
}
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
}
使用示例:
salesSolverChooser.choose(askSalesOrderDTO.getOrderChannel()); 该方法中的getOrderChannel的值也就是由前端传递进来的,通过这种方式来实现策略模式减少大量的if
public class AfterSalesServicesImpl implements AfterSalesServices {
private SalesSolverChooser salesSolverChooser;
@Override
public void pullSalesOrder(AskSalesOrderDTO askSalesOrderDTO) {
try {
log.info("同步售后订单,店铺id:{},参数:{}",askSalesOrderDTO.getStoreId(), JSONObject.toJSONString(askSalesOrderDTO));
SalesAbstract salesAbstract=salesSolverChooser.choose(askSalesOrderDTO.getOrderChannel());
salesAbstract.execPullAfterSalesOrder(askSalesOrderDTO);
}catch (Exception e){
log.error("同步售后订单失败,店铺id:{},原因:{},异常栈是",askSalesOrderDTO.getStoreId(),e,e);
}
}
}
责任链模式
这个模式的具体实现代码已经找不到了,因为这块是好几年前的了,代码已经丢失了
他的功能要求就是公司举办个比赛
excel 转pdf
然后在完成基本的转化之后,有一些额外的要求 比如加上水印,加上背景色,自动合并单元格等等
所以当时就想到了用责任链模式的思想
首先是通过枚举的方式来进行定义节点,枚举的code 是他的执行顺序,name 则是相对应的执行方法的bean的名字,然后就在内存里根据这个code 来以及传递的code 列表来行程这个责任链 ,code 列表是用来进行选择的,因为当时的需求的上就是属于选择框形式的,可以来勾选想要的效果,然后我就通过这种形式来生成动态的责任链
文件每进入到一个节点之后进行操作,先是生成一个临时文件,然后临时文件进行节点的操作,然后操作完成之后就将老文件给删除,临时文件变更成为新的文件,然后新文件又进入到下一个节点,如果节点操作失败的情况下,那么就直接将当时的临时文件给删了,然后老文本跳过该节点直接进入下一个节点,
装饰者模式
这个的流程就是
1.需要通过线程池来提高响应速度
2.通过inheritthreadLocal 来进行传递数据
3.因为inheritthreadLocal 的逻辑是在创建线程的时候传递数据,但是线程池里面存在着核心线程,他是一直存活的
4.通过装饰者模式来装饰---> 简单的实现是通过spring线程池
这个的需求是来自于线程上下文数据传递的。因为项目中多出地方用到了多线程来提高响应速度。但是在调用多线程的时候,常规的数据是无法进行传递的,就比如用户信息。因为我们获取用户的信息的接口是通过request来进行获取token进行解析然后把数据存储到threadlocal里面,但是在进行线程的切换的时候,threadlocal是无法获取到的,因为threadlocal 获取数值的方式是通过thread作为key来获取里面的ThreadLocalMap 然后在通过 ThreadLocalMap 用当前用的对象(this)作为key来获取value值,
java.lang.ThreadLocal
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); //t 就是当前的线程
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); //this 就是当前的线程
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
但是因为线程的切换后 thread 值已经发生变化了,于是后续我们是采用了inheritthreadlocal 来替换 threadlocal的,inheritthreadlocal 他能够在创建线程对象的时候把调用线程的值给赋值到创建的线程对象中,因此在线程切换后他能够获取到调用线程的数据,
但是因为我们用的是线程池,在线程池里面是存在着核心线程数的,因为会存在着 使用 多线程的时候 用到了线程池里面的核心线程数 从而导致了 调用线程的inheritthreadlocal 里面的数值没有传递过去
于是在后续 研究了之后 发现我们可以使用装饰者模式的方式来进行增强spring的线程池
因为spring的线程池ThreadPoolTaskExecutor 他有个属性 taskDecorator,该属性就是属于装饰器了,具体的代码如下 ,通过自定义装饰器来实现spring的装饰器,然后再 重写decorate()方法。该种方式的作用其实就是在调用方法的时候,还是属于调用线程在调用,因此我们可以通过ThreadLocal 来获取到调用线程的数据,然后再通过自定义任务 重写 Runnable 方法的时候 把数据给赋值到子线程里面去
@Configuration
public class ContextCopyingDecorator implements TaskDecorator {
@Bean("taskExecutor") // bean 的名称,默认为首字母小写的方法名
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//其他参数省略
//设置装饰器
executor .setTaskDecorator(new ContextCopyingDecorator());
return executor;
}
@Override
public Runnable decorate(Runnable runnable) {
try {
//获取父线程的context
String father = ThreadLocalData.getUa();
return new Runnable() {
@Override
public void run() {
try {
//将父线程的context设置进子线程里
ThreadLocalData.setUa(father);
//子线程方法执行
runnable.run();
} finally {
//清除子线程context
ThreadLocalData.remove();
}
}
};
} catch (IllegalStateException e) {
return runnable;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗