【GitHub】-design-pattern-extend(设计模式扩展)

写在前面

  偶然间看到一篇文章 《Java 中保持扩展性的几种套路和实现》,写的不错,但是类图画的差了点儿意思。于是,自己动手画了画,对其中的内容作了一些调整,对包做了进一步划分,便于理解消化。以下是对GitHub项目 design-pattern-extend 的快览,后期将新的套路慢慢补充。


目录

  • 写在前面
  • 一、项目结构
  • 二、关键信息
    • 管道模式
    • 过滤器链模式
    • 事件分发模式
    • 模板+工厂模式
    • SPI模式
    • 注解模式
    • 其他
  • 三、参考资料
  • 写在后面
  • 系列文章


一、项目结构

  以下为GitHub项目 design-pattern-extend 的整体目录结构。images 目录下为设计模式的 plantuml 类图,client目录为模式main方法入口。

如果想向学习,又懒得敲代码的话,具体代码及示例请前往design-pattern-extend 自行获取。

design-pattern-extend
├─images
└─src└─main├─java│  └─cn│      └─thinkinjava│          └─design│              └─pattern│                  └─extend│                      ├─annotation│                      │  └─client│                      ├─eventdispatch│                      │  ├─client│                      │  ├─event│                      │  ├─listener│                      │  └─source│                      ├─filterchain│                      │  ├─client│                      │  ├─filter│                      │  └─request│                      ├─pipeline│                      │  ├─client│                      │  ├─context│                      │  └─value│                      ├─spi│                      │  ├─client│                      │  └─service│                      │      └─impl│                      └─templatefactory│                          ├─client│                          └─handler│                              └─base└─resources└─META-INF└─services

二、关键信息

下面简单说一下相关的设计模式扩展思路。

管道模式

管道模式简单说就是想对"某个对象"进行一些列的"操作"。

根据面向接口以及抽象的原则,
1、“操作”是要抽取出来一个接口,我们用管道值表示,即value包下的PipelineValue。
2、“某个对象”就是要操作的类,我们用上下文表示,即context包下的PipelineContext。
3、既然是管道,那“操作”得放到管道里面(添加/删除操作方法)还得执行管道操作方法(遍历管道值,调用方法),即pipeline包下的Pipeline。

这样,就形成3个体系,看类图,
在这里插入图片描述

以下为上下文对象,

package cn.thinkinjava.design.pattern.extend.pipeline.context;/*** 上下文** @author qiuxianbao* @date 2024/01/02*/
public interface PipelineContext {String FOR_TEST = "forTest";void set(String contextKey, Object contextValue);Object get(String contextKey);
}//
package cn.thinkinjava.design.pattern.extend.pipeline.context;import java.util.HashMap;
import java.util.Map;/*** 上下文的具体实现** @author qiuxianbao* @date 2024/01/02*/
public class StandardPipelineContext implements PipelineContext {private Map<String, Object> contextMap = new HashMap<>();@Overridepublic void set(String contextKey, Object contextValue) {contextMap.put(contextKey, contextValue);}@Overridepublic Object get(String contextKey) {return contextMap.get(contextKey);}}

以下为管道值对象,


package cn.thinkinjava.design.pattern.extend.pipeline.value;import cn.thinkinjava.design.pattern.extend.pipeline.context.PipelineContext;/*** 管道中的操作对象** @author qiuxianbao* @date 2024/01/02*/
public interface PipelineValue {/*** 具体的操作** @param context 上下文* @return*/boolean execute(PipelineContext context);
}//
package cn.thinkinjava.design.pattern.extend.pipeline.value;import cn.thinkinjava.design.pattern.extend.pipeline.context.PipelineContext;
import lombok.extern.slf4j.Slf4j;/*** 管道中的操作对象的抽象** @author qiuxianbao* @date 2024/01/02*/
@Slf4j
public abstract class AbstractPipelineValue implements PipelineValue {@Overridepublic boolean execute(PipelineContext context) {log.info("{} start", this.getClass().getSimpleName());boolean result = doExecute(context);log.info("{} end", this.getClass().getSimpleName());return result;}/*** 由子类实现** @param context* @return*/protected abstract boolean doExecute(PipelineContext context);
}//
package cn.thinkinjava.design.pattern.extend.pipeline.value;import cn.thinkinjava.design.pattern.extend.pipeline.context.PipelineContext;/*** 管道中的操作对象的具体实现** @author qiuxianbao* @date 2024/01/02*/
public class ForeTest1Value extends AbstractPipelineValue {@Overrideprotected boolean doExecute(PipelineContext context) {// 比如:设置了一些值context.set(PipelineContext.FOR_TEST, true);return true;}
}//
package cn.thinkinjava.design.pattern.extend.pipeline.value;import cn.thinkinjava.design.pattern.extend.pipeline.context.PipelineContext;/*** 管道中的操作对象的具体实现** @author qiuxianbao* @date 2024/01/02*/
public class ForeTest2Value extends AbstractPipelineValue {@Overrideprotected boolean doExecute(PipelineContext context) {return true;}
}

以下是管道操作,

package cn.thinkinjava.design.pattern.extend.pipeline;import cn.thinkinjava.design.pattern.extend.pipeline.value.PipelineValue;
import cn.thinkinjava.design.pattern.extend.pipeline.context.PipelineContext;/*** 管道** 适用场景:* 当你的数据流需要经过很多同等逻辑处理时,可以考虑使用此套路,便于后续扩展** @author qiuxianbao* @date 2024/01/02*/
public interface Pipeline {/*** 执行操作** @param context 上下文,即要处理的对象* @return*/boolean invoke(PipelineContext context);/*** 添加操作** @param value 管道中的操作对象* @return*/boolean addValue(PipelineValue value);/*** 删除操作** @param value 管道中的操作对象* @return*/boolean removeValue(PipelineValue value);
}// 核心
package cn.thinkinjava.design.pattern.extend.pipeline;import cn.thinkinjava.design.pattern.extend.pipeline.value.PipelineValue;
import cn.thinkinjava.design.pattern.extend.pipeline.context.PipelineContext;
import lombok.extern.slf4j.Slf4j;import java.util.ArrayList;
import java.util.List;/*** 管道实现类** @author qiuxianbao* @date 2024/01/02*/
@Slf4j
public class StandardPipeline implements Pipeline{private List<PipelineValue> valueList = new ArrayList<>();@Overridepublic boolean invoke(PipelineContext context) {boolean result = true;for (PipelineValue item : valueList) {try {result = item.execute(context);if (!result) {log.error("{}, execute is wrong", this.getClass().getSimpleName());}} catch (Exception e) {log.error(e.getMessage(), e);}}return result;}@Overridepublic boolean addValue(PipelineValue value) {if (valueList.contains(value)) {return true;}return valueList.add(value);}@Overridepublic boolean removeValue(PipelineValue value) {return valueList.remove(value);}
}

以下为客户端,

package cn.thinkinjava.design.pattern.extend.pipeline.client;import cn.thinkinjava.design.pattern.extend.pipeline.Pipeline;
import cn.thinkinjava.design.pattern.extend.pipeline.StandardPipeline;
import cn.thinkinjava.design.pattern.extend.pipeline.value.PipelineValue;
import cn.thinkinjava.design.pattern.extend.pipeline.context.PipelineContext;
import cn.thinkinjava.design.pattern.extend.pipeline.context.StandardPipelineContext;
import cn.thinkinjava.design.pattern.extend.pipeline.value.ForeTest1Value;
import cn.thinkinjava.design.pattern.extend.pipeline.value.ForeTest2Value;/*** 客户端** @author qiuxianbao* @date 2024/01/02*/
public class PipelineClient {public static void main(String[] args) {// 管道Pipeline pipeline = new StandardPipeline();// 管道中对象PipelineValue foreTestValue = new ForeTest1Value();PipelineValue graySwitchValue = new ForeTest2Value();pipeline.addValue(foreTestValue);pipeline.addValue(graySwitchValue);// 上下文PipelineContext context = new StandardPipelineContext();// 调用pipeline.invoke(context);System.out.println(context.get(PipelineContext.FOR_TEST));//        ForeTest1Value start
//        ForeTest1Value end
//        ForeTest2Value start
//        ForeTest2Value end
//        true}
}

过滤器链模式

过滤器链,既然是链,那么就会有先后顺序。但它并不像前面说的管道那样,前面执行完,然后交给后面执行。它和管道是有区别的,这里巧妙地运用到了this和索引。

管道模式是一层进来然后出去,接着进行下一层。
//      ForeTest1Value start
//      ForeTest1Value end
//      ForeTest2Value start
//      ForeTest2Value end
过滤器链是从外到内一层一层都先进来,然后再由内到外一层一层再出去。
//		ForTest1Filter before 1704180891151
//		ForTest2Filter before 1704180891151
//		ForTest2Filter end 1704180891152
//		ForTest1Filter end 1704180891152

在这里插入图片描述
以下为操作对象(假设的),

package cn.thinkinjava.design.pattern.extend.filterchain.request;/*** @author qiuxianbao* @date 2024/01/02*/
public interface HttpRequest {
}//
package cn.thinkinjava.design.pattern.extend.filterchain.request;/*** @author qiuxianbao* @date 2024/01/02*/
public class StandardHttpRequest implements HttpRequest {
}

以下为过滤器对象,

package cn.thinkinjava.design.pattern.extend.filterchain.filter;import cn.thinkinjava.design.pattern.extend.filterchain.FilterChain;
import cn.thinkinjava.design.pattern.extend.filterchain.request.HttpRequest;/*** 过滤器** @author qiuxianbao* @date 2024/01/02*/
public interface Filter {void doFilter(HttpRequest httpRequest, FilterChain filterChain);
}//
package cn.thinkinjava.design.pattern.extend.filterchain.filter;import cn.thinkinjava.design.pattern.extend.filterchain.FilterChain;
import cn.thinkinjava.design.pattern.extend.filterchain.request.HttpRequest;
import lombok.extern.slf4j.Slf4j;/*** @author qiuxianbao* @date 2024/01/02*/
@Slf4j
public class ForTest1Filter implements Filter {@Overridepublic void doFilter(HttpRequest httpRequest, FilterChain filterChain) {log.info("{} before {}", this.getClass().getSimpleName(), System.currentTimeMillis());// 这里是重点filterChain.doFilter(httpRequest);log.info("{} end {}", this.getClass().getSimpleName(), System.currentTimeMillis());}
}//
package cn.thinkinjava.design.pattern.extend.filterchain.filter;import cn.thinkinjava.design.pattern.extend.filterchain.FilterChain;
import cn.thinkinjava.design.pattern.extend.filterchain.request.HttpRequest;
import lombok.extern.slf4j.Slf4j;/*** @author qiuxianbao* @date 2024/01/02*/
@Slf4j
public class ForTest2Filter implements Filter {@Overridepublic void doFilter(HttpRequest httpRequest, FilterChain filterChain) {log.info("{} before {}", this.getClass().getSimpleName(), System.currentTimeMillis());filterChain.doFilter(httpRequest);log.info("{} end {}", this.getClass().getSimpleName(), System.currentTimeMillis());}
}

以下为过滤器链,

package cn.thinkinjava.design.pattern.extend.filterchain;import cn.thinkinjava.design.pattern.extend.filterchain.request.HttpRequest;
import cn.thinkinjava.design.pattern.extend.filterchain.filter.Filter;/*** 拦截器链** 适用场景:* 常见的web请求场景** @author qiuxianbao* @date 2024/01/02*/
public interface FilterChain {void doFilter(HttpRequest httpRequest);void addFilter(Filter filter);
}// 核心
package cn.thinkinjava.design.pattern.extend.filterchain;import cn.thinkinjava.design.pattern.extend.filterchain.request.HttpRequest;
import cn.thinkinjava.design.pattern.extend.filterchain.filter.Filter;import java.util.ArrayList;
import java.util.List;/*** @author qiuxianbao* @date 2024/01/02*/
public class StandardFilterChain implements FilterChain {private List<Filter> filterList = new ArrayList<>();private int currentIndex = 0;@Overridepublic void doFilter(HttpRequest httpRequest) {if (currentIndex == filterList.size()) {return;}Filter filter = filterList.get(currentIndex);currentIndex = currentIndex + 1;filter.doFilter(httpRequest, this);}@Overridepublic void addFilter(Filter filter) {if (filterList.contains(filter)) {return;}filterList.add(filter);}
}

以下为客户端,

package cn.thinkinjava.design.pattern.extend.filterchain.client;import cn.thinkinjava.design.pattern.extend.filterchain.FilterChain;
import cn.thinkinjava.design.pattern.extend.filterchain.StandardFilterChain;
import cn.thinkinjava.design.pattern.extend.filterchain.request.HttpRequest;
import cn.thinkinjava.design.pattern.extend.filterchain.request.StandardHttpRequest;
import cn.thinkinjava.design.pattern.extend.filterchain.filter.ForTest1Filter;
import cn.thinkinjava.design.pattern.extend.filterchain.filter.ForTest2Filter;/*** 客户端** @author qiuxianbao* @date 2024/01/02*/
public class FilterClient {public static void main(String[] args) {FilterChain filterChain = new StandardFilterChain();filterChain.addFilter(new ForTest1Filter());filterChain.addFilter(new ForTest2Filter());HttpRequest request = new StandardHttpRequest();filterChain.doFilter(request);//ForTest1Filter before 1704180891151//ForTest2Filter before 1704180891151//ForTest2Filter end 1704180891152//ForTest1Filter end 1704180891152}
}

事件分发模式

事件派发器分配事件,谁满足了事件,则会有相应的事件监听器去处理事件。
一句话,抽象出来几个对象:事件、事件监听器(谁满足、怎么处理)、事件的产生(事件源)、事件派发器(能够拿到所有事件的监听器,进行循环)

在这里插入图片描述

以下为事件,

package cn.thinkinjava.design.pattern.extend.eventdispatch.event;/*** 事件** @author qiuxianbao* @date 2024/01/02*/
public interface Event {/*** 事件名称** @return*/String getName();
}//
package cn.thinkinjava.design.pattern.extend.eventdispatch.event;/*** @author qiuxianbao* @date 2024/01/02*/
public class EventForTest1 implements Event {@Overridepublic String getName() {return getClass().getSimpleName();}
}//
package cn.thinkinjava.design.pattern.extend.eventdispatch.event;/*** @author qiuxianbao* @date 2024/01/02*/
public class EventFor2 implements Event {@Overridepublic String getName() {return getClass().getSimpleName();}
}

以下为事件监听器,

package cn.thinkinjava.design.pattern.extend.eventdispatch.listener;import cn.thinkinjava.design.pattern.extend.eventdispatch.event.Event;/*** 事件监听器,处理事件** @author qiuxianbao* @date 2024/01/02*/
public interface EventListener {/*** 是否支持此事件** @param event* @return*/boolean supportEvent(Event event);/*** 处理事件** @return*/boolean handlerEvent(Event event);
}//
package cn.thinkinjava.design.pattern.extend.eventdispatch.listener;import cn.thinkinjava.design.pattern.extend.eventdispatch.event.Event;
import lombok.extern.slf4j.Slf4j;/*** @author qiuxianbao* @date 2024/01/02*/
@Slf4j
public class EventListenerForTest implements EventListener {@Overridepublic boolean supportEvent(Event event) {return event.getName().contains("Test");}@Overridepublic boolean handlerEvent(Event event) {log.info("{} \t handler {}", this.getClass().getSimpleName(), event.getName());return true;}
}//
package cn.thinkinjava.design.pattern.extend.eventdispatch.listener;import java.util.ArrayList;
import java.util.List;/*** 事件监听器管理** @author qiuxianbao* @date 2024/01/02*/
public class EventListenerManager {private static List<EventListener> eventListenerList = new ArrayList<>();/*** 添加事件监听器** @param eventListener* @return*/public static boolean addEventListener(EventListener eventListener) {if (eventListenerList.contains(eventListener)) {return true;}return eventListenerList.add(eventListener);}/*** 移除事件监听器** @param eventListener* @return*/public static boolean removeEventListener(EventListener eventListener) {if (eventListenerList.contains(eventListener)) {return eventListenerList.remove(eventListener);}return true;}/*** 获取事件监听器** @return*/public static List<EventListener> getEventListenerList() {return eventListenerList;}}

以下为事件源,

package cn.thinkinjava.design.pattern.extend.eventdispatch.source;import cn.thinkinjava.design.pattern.extend.eventdispatch.event.Event;/*** 事件源** @author qiuxianbao* @date 2024/01/02*/
public interface EventSource {/*** 发出事件** @return*/Event fireEvent();
}// 
package cn.thinkinjava.design.pattern.extend.eventdispatch.source;import cn.thinkinjava.design.pattern.extend.eventdispatch.event.Event;
import cn.thinkinjava.design.pattern.extend.eventdispatch.event.EventForTest1;
import lombok.extern.slf4j.Slf4j;/*** @author qiuxianbao* @date 2024/01/02*/
@Slf4j
public class EventSourceForTest1 implements EventSource {@Overridepublic Event fireEvent() {// 发出的就是具体的事件了Event event = new EventForTest1();log.info("{} \t fireEvent {}", this.getClass().getSimpleName(), event.getName());return event;}
}// 
package cn.thinkinjava.design.pattern.extend.eventdispatch.source;import cn.thinkinjava.design.pattern.extend.eventdispatch.event.Event;
import cn.thinkinjava.design.pattern.extend.eventdispatch.event.EventFor2;
import lombok.extern.slf4j.Slf4j;/*** @author qiuxianbao* @date 2024/01/02*/
@Slf4j
public class EventSourceFor2 implements EventSource {@Overridepublic Event fireEvent() {Event event = new EventFor2();log.info("{} \t fireEvent {}", this.getClass().getSimpleName(), event.getName());return event;}
}

以下为事件派发器,

package cn.thinkinjava.design.pattern.extend.eventdispatch;import cn.thinkinjava.design.pattern.extend.eventdispatch.event.Event;
import cn.thinkinjava.design.pattern.extend.eventdispatch.listener.EventListener;
import cn.thinkinjava.design.pattern.extend.eventdispatch.listener.EventListenerManager;
import org.apache.commons.collections.CollectionUtils;/*** 事件分发器** @author qiuxianbao* @date 2024/01/02*/
public class EventDispatcher {private EventDispatcher() {}/*** 分发事件** @param event*/public static void dispatchEvent(Event event) {// 核心if (CollectionUtils.isNotEmpty(EventListenerManager.getEventListenerList())) {for (EventListener eventListener : EventListenerManager.getEventListenerList()) {if (eventListener.supportEvent(event)) {eventListener.handlerEvent(event);}}}}
}

以下为客户端,

package cn.thinkinjava.design.pattern.extend.eventdispatch.client;import cn.thinkinjava.design.pattern.extend.eventdispatch.EventDispatcher;
import cn.thinkinjava.design.pattern.extend.eventdispatch.listener.EventListenerManager;
import cn.thinkinjava.design.pattern.extend.eventdispatch.source.EventSource;
import cn.thinkinjava.design.pattern.extend.eventdispatch.source.EventSourceForTest1;
import cn.thinkinjava.design.pattern.extend.eventdispatch.source.EventSourceFor2;
import cn.thinkinjava.design.pattern.extend.eventdispatch.listener.EventListener;
import cn.thinkinjava.design.pattern.extend.eventdispatch.listener.EventListenerForTest;/*** @author qiuxianbao* @date 2024/01/02*/
public class EventClient {public static void main(String[] args) {// 创建事件监听器EventListener eventListener = new EventListenerForTest();EventListenerManager.addEventListener(eventListener);// 创建事件源EventSource eventSource1 = new EventSourceForTest1();EventSource eventSource2 = new EventSourceFor2();// 发布事件EventDispatcher.dispatchEvent(eventSource1.fireEvent());EventDispatcher.dispatchEvent(eventSource2.fireEvent());//        11:50:17.029 [main] INFO cn.thinkinjava.design.pattern.extend.eventdispatch.source.EventSourceForTest1 - EventSourceForTest1 	 fireEvent EventForTest1
//        11:50:17.067 [main] INFO cn.thinkinjava.design.pattern.extend.eventdispatch.listener.EventListenerForTest - EventListenerForTest 	 handler EventForTest1
//        11:50:17.077 [main] INFO cn.thinkinjava.design.pattern.extend.eventdispatch.source.EventSourceFor2 - EventSourceFor2 	 fireEvent EventFor2}
}

模板+工厂模式

提到模板,通常都是在抽象类中实现通用逻辑,然后留出接口未实现的交给其子类实现。这里组合工厂,工厂用于维护所有的处理器。

在这里插入图片描述
以下是处理器 + 工厂,

package cn.thinkinjava.design.pattern.extend.templatefactory.handler.base;import cn.thinkinjava.design.pattern.extend.templatefactory.PiiContent;import java.lang.reflect.Field;/*** @author qiuxianbao* @date 2024/01/04*/
public interface PiiDomainFieldHandler {/*** 处理实际操作* 读----从PiiContent获取数据回填domain** @param domain* @param domainField* @param piiContent* @param <T>* @return*/<T extends Object> boolean handlerRead(T domain, Field domainField, PiiContent piiContent);/*** 处理实际操作* 写----将domain中需要写入pii的字段数据写入PiiContent** @param domain* @param domainField* @param piiContent* @param <T>* @return*/<T extends Object> boolean handlerWrite(T domain, Field domainField, PiiContent piiContent);/*** 当前处理器是否支持该领域对象** @param domain* @param domainField* @param <T>* @return*/<T extends Object> boolean isSupport(T domain, Field domainField);/*** 获取处理器对应的元信息** @return*/String getPiiDomainMeta();
}//
package cn.thinkinjava.design.pattern.extend.templatefactory.handler.base;import cn.thinkinjava.design.pattern.extend.templatefactory.PiiContent;
import lombok.extern.slf4j.Slf4j;import java.lang.reflect.Field;/*** 模板方法* 通过抽象类实现** @author qiuxianbao* @date 2024/01/04*/
@Slf4j
public abstract class PiiDomainFieldHandlerBase implements PiiDomainFieldHandler{@Overridepublic <T> boolean handlerRead(T domain, Field domainField, PiiContent piiContent) {log.info("{} handlerRead {}", this.getClass().getSimpleName(), domain.getClass().getSimpleName());return true;}@Overridepublic <T> boolean handlerWrite(T domain, Field domainField, PiiContent piiContent) {log.info("{} handlerWrite {}", this.getClass().getSimpleName(), domain.getClass().getSimpleName());return true;}
}//
package cn.thinkinjava.design.pattern.extend.templatefactory.handler;import cn.thinkinjava.design.pattern.extend.templatefactory.handler.base.PiiDomainFieldHandlerBase;
import lombok.extern.slf4j.Slf4j;import java.lang.reflect.Field;/*** @author qiuxianbao* @date 2024/01/04*/
@Slf4j
public class ForTestSupportFieldHandler extends PiiDomainFieldHandlerBase {@Overridepublic <T> boolean isSupport(T domain, Field domainField) {if (this.getClass().getSimpleName().equalsIgnoreCase(domain.getClass().getSimpleName())) {log.info("{} is support, to do some business", this.getClass().getSimpleName());return true;}return false;}@Overridepublic String getPiiDomainMeta() {return this.getClass().getSimpleName();}
}//
package cn.thinkinjava.design.pattern.extend.templatefactory.handler;import cn.thinkinjava.design.pattern.extend.templatefactory.handler.base.PiiDomainFieldHandlerBase;
import lombok.extern.slf4j.Slf4j;import java.lang.reflect.Field;/*** @author qiuxianbao* @date 2024/01/04*/
@Slf4j
public class ForTestNotSupportFieldHandler extends PiiDomainFieldHandlerBase {@Overridepublic <T> boolean isSupport(T domain, Field domainField) {if (this.getClass().getSimpleName().equalsIgnoreCase(domain.getClass().getSimpleName())) {log.info("{} is support, to do some business", this.getClass().getSimpleName());return true;}return false;}@Overridepublic String getPiiDomainMeta() {return this.getClass().getSimpleName();}
}//
package cn.thinkinjava.design.pattern.extend.templatefactory.handler;import cn.thinkinjava.design.pattern.extend.templatefactory.handler.base.PiiDomainFieldHandler;import java.util.ArrayList;
import java.util.List;/*** 工厂类* 手动添加处理器** @author qiuxianbao* @date 2024/01/04*/
public class PiiDomainFieldHandlerFactory {/*** 创建领域处理器** @return*/public static List<PiiDomainFieldHandler> createPiiDomainFieldHandler() {List<PiiDomainFieldHandler> piiDomainFieldHandlerList = new ArrayList();// 添加处理器piiDomainFieldHandlerList.add(new ForTestSupportFieldHandler());piiDomainFieldHandlerList.add(new ForTestNotSupportFieldHandler());return piiDomainFieldHandlerList;}
}

以下是上下文对象,

package cn.thinkinjava.design.pattern.extend.templatefactory;import java.util.HashMap;
import java.util.Map;/*** 上下文对象** @author qiuxianbao* @date 2024/01/04*/
public class PiiContent {public static String FORTEST="fortest";private Map<String, Object> piiDataMap = new HashMap<>();private Map<String, Object> piiContextMap = new HashMap<>();public void putPiiData(String domainFieldName, Object domainFieldValue) {piiDataMap.put(domainFieldName, domainFieldValue);}public Object getPiiData(String domainFieldName) {return piiDataMap.get(domainFieldName);}public void putPiiContext(String contextName, Object contextNameValue) {piiContextMap.put(contextName, contextNameValue);}public Object getPiiContext(String contextName) {return piiContextMap.get(contextName);}
}

以下是处理器注册器,从工厂中拿出处理器,对外提供处理操作,

package cn.thinkinjava.design.pattern.extend.templatefactory;import cn.thinkinjava.design.pattern.extend.templatefactory.handler.PiiDomainFieldHandlerFactory;
import cn.thinkinjava.design.pattern.extend.templatefactory.handler.base.PiiDomainFieldHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 处理器注册器** @author qiuxianbao* @date 2024/01/04*/
@Slf4j
public class PiiHandlerRegistry {private static Map<String, PiiDomainFieldHandler> piiDomainFieldHandlerMap = new HashMap<>();public static void putHandler(String piiDomainFieldName, PiiDomainFieldHandler piiDomainFieldHandler) {if (StringUtils.isEmpty(piiDomainFieldName)) {log.warn(" piiDomainFieldName is null,continue");return;}if (piiDomainFieldHandler == null) {log.warn(piiDomainFieldName + " piiDomainFieldHandler is null,continue");return;}if (!piiDomainFieldHandlerMap.containsKey(piiDomainFieldName)) {piiDomainFieldHandlerMap.put(piiDomainFieldName, piiDomainFieldHandler);}}public static <T extends Object> int handlerRead(T domain, Field domainField, PiiContent piiContent) {int num = 0;for (Map.Entry<String, PiiDomainFieldHandler> piiDomainFieldHandlerEntry :piiDomainFieldHandlerMap.entrySet()) {if (piiDomainFieldHandlerEntry.getValue().isSupport(domain, domainField)) {piiDomainFieldHandlerEntry.getValue().handlerRead(domain, domainField, piiContent);}}return num;}public static <T extends Object> int handlerWrite(T domain, Field domainField, PiiContent piiContent) {int num = 0;for (Map.Entry<String, PiiDomainFieldHandler> piiDomainFieldHandlerEntry :piiDomainFieldHandlerMap.entrySet()) {if (piiDomainFieldHandlerEntry.getValue().isSupport(domain, domainField)) {piiDomainFieldHandlerEntry.getValue().handlerWrite(domain, domainField, piiContent);}}return num;}public static Map<String, PiiDomainFieldHandler> getPiiDomainFieldHandlerMap() {return piiDomainFieldHandlerMap;}public static void init() {List<PiiDomainFieldHandler> piiDomainFieldHandlerList = PiiDomainFieldHandlerFactory.createPiiDomainFieldHandler();if (CollectionUtils.isNotEmpty(piiDomainFieldHandlerList)) {for (PiiDomainFieldHandler piiDomainFieldHandler :piiDomainFieldHandlerList) {putHandler(piiDomainFieldHandler.getPiiDomainMeta(), piiDomainFieldHandler);}}}
}

以下是客户端,

package cn.thinkinjava.design.pattern.extend.templatefactory.client;import cn.thinkinjava.design.pattern.extend.templatefactory.PiiContent;
import cn.thinkinjava.design.pattern.extend.templatefactory.PiiHandlerRegistry;
import cn.thinkinjava.design.pattern.extend.templatefactory.handler.ForTestNotSupportFieldHandler;
import cn.thinkinjava.design.pattern.extend.templatefactory.handler.ForTestSupportFieldHandler;
import cn.thinkinjava.design.pattern.extend.templatefactory.handler.base.PiiDomainFieldHandler;import java.util.Map;/*** 客户端** @author qiuxianbao* @date 2024/01/04*/
public class PiiClient {public static void main(String[] args) {// 通过工厂,把处理器放到Map中PiiHandlerRegistry.init();// 遍历处理器for (Map.Entry<String, PiiDomainFieldHandler> entryHandler :PiiHandlerRegistry.getPiiDomainFieldHandlerMap().entrySet()) {System.out.println(entryHandler.getKey() + "\t" + entryHandler.getValue().getPiiDomainMeta());}//PiiContent piiContent = new PiiContent();piiContent.putPiiContext(PiiContent.FORTEST, PiiContent.FORTEST);// 请求处理System.out.println("ForTestSupportFieldHandler start");PiiHandlerRegistry.handlerRead(new ForTestSupportFieldHandler(), null, piiContent);System.out.println("ForTestSupportFieldHandler end");// 请求处理System.out.println("ForTestNotSupportFieldHandler start");PiiHandlerRegistry.handlerRead(new ForTestNotSupportFieldHandler(), null, piiContent);System.out.println("ForTestNotSupportFieldHandler end");}
}

SPI模式

SPI核心就是ServiceLoader

package java.util;
public final class ServiceLoader<S>implements Iterable<S> {private static final String PREFIX = "META-INF/services/";
}    

以下为简单示例:
1、resources目录下建META-INF/services目录
2、新建文件,文件名为接口全路径名。文件内容为实现类的全路径名。

在这里插入图片描述

接口和实现类,

package cn.thinkinjava.design.pattern.extend.spi.service;/*** @author qiuxianbao* @date 2024/01/02*/
public interface RemoteService {
}//
package cn.thinkinjava.design.pattern.extend.spi.service.impl;import cn.thinkinjava.design.pattern.extend.spi.service.RemoteService;/*** @author qiuxianbao* @date 2024/01/02*/
public class RemoteServiceImpl implements RemoteService {
} 

工具类,

package cn.thinkinjava.design.pattern.extend.spi;import java.util.HashMap;
import java.util.Map;/*** 存储策略依赖的服务, 统一管理** @author qiuxianbao* @date 2024/01/02*/
public class DependServiceRegistryHelper {private static Map<String, Object> dependManagerMap = new HashMap<>();public static boolean registryMap(Map<Class, Object> dependManagerMap) {for (Map.Entry<Class, Object> dependEntry : dependManagerMap.entrySet()) {registry(dependEntry.getKey(), dependEntry.getValue());}return true;}public static boolean registry(Class cls, Object dependObject) {dependManagerMap.put(cls.getCanonicalName(), dependObject);return true;}public static Object getDependObject(Class cls) {return dependManagerMap.get(cls.getCanonicalName());}
}//
package cn.thinkinjava.design.pattern.extend.spi;import cn.thinkinjava.design.pattern.extend.spi.service.RemoteService;import java.util.Iterator;
import java.util.ServiceLoader;/*** SPI的方式加载** 说明:* 1.resource文件夹下建 META-INF/services文件夹* 2.创建一个文件,文件名为接口的全限定名,文件内容为接口实现类的全限定名** @author qiuxianbao* @date 2024/01/02*/
public class SpiServiceLoaderHelper {public static RemoteService getProductPackageRemoteServiceInterface() {Object serviceCache = DependServiceRegistryHelper.getDependObject(RemoteService.class);if (serviceCache != null) {return (RemoteService) serviceCache;}RemoteService serviceInterface = loadSpiImpl(RemoteService.class);DependServiceRegistryHelper.registry(RemoteService.class, serviceInterface);return serviceInterface;}/*** 以spi的方式加载实现类** @param cls* @return*/private static <P> P loadSpiImpl(Class<P> cls) {ServiceLoader<P> spiLoader = ServiceLoader.load(cls);Iterator<P> iterator = spiLoader.iterator();if (iterator.hasNext()) {return iterator.next();}throw new RuntimeException("SpiServiceLoaderHelper loadSpiImpl failed, please check spi service");}}

以下为客户端,

package cn.thinkinjava.design.pattern.extend.spi.client;import cn.thinkinjava.design.pattern.extend.spi.SpiServiceLoaderHelper;
import cn.thinkinjava.design.pattern.extend.spi.service.RemoteService;/*** @author qiuxianbao* @date 2024/01/02*/
public class SPIClient {public static void main(String[] args) {RemoteService remoteService= SpiServiceLoaderHelper.getProductPackageRemoteServiceInterface();System.out.println(remoteService);// cn.thinkinjava.main.extend.spi.service.impl.ProductPackageRemoteServiceInterfaceImpl@2752f6e2}
}

注解模式

通过添加注解,可以进行一些扩展操作。
比如:可以把所有加过注解的类通过Map缓存中,再进行反射处理。
TcpMapping + TcpMappingScan + TcpFinder

以下是一个简单示例:

package cn.thinkinjava.design.pattern.extend.annotation;import org.springframework.stereotype.Component;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用于测试的标识注解** @author qiuxianbao* @date 2024/01/04*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface ForTestAnnotation {
}//
package cn.thinkinjava.design.pattern.extend.annotation;/*** 代测试的Bean** @author qiuxianbao* @date 2024/01/04*/
@ForTestAnnotation
public class ForTestBean {public ForTestBean() {System.out.println(ForTestBean.class.getSimpleName() + " init");}}//
package cn.thinkinjava.design.pattern.extend.annotation;import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;/*** 注解解析器** @author qiuxianbao* @date 2024/01/04*/
@Component
public class ForTestAnnotationProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 获取目标类是否有ForTestAnnotation注解ForTestAnnotation annotation = AnnotationUtils.findAnnotation(AopUtils.getTargetClass(bean),ForTestAnnotation.class);if (annotation == null) {return bean;}// 处理想要的扩展System.out.println(beanName + " has ForTestAnnotation");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}
}//
package cn.thinkinjava.design.pattern.extend.annotation.client;import org.springframework.context.annotation.AnnotationConfigApplicationContext;/*** 客户端** @author qiuxianbao* @date 2024/01/04*/
public class ForTestClient {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("cn.thinkinjava.main.extend.annotation");System.out.println(ForTestClient.class.getSimpleName());}}

其他


三、参考资料

《Java 中保持扩展性的几种套路和实现》


写在后面

  如果本文内容对您有价值或者有启发的话,欢迎点赞、关注、评论和转发。您的反馈和陪伴将促进我们共同进步和成长。


系列文章

【GitHub】- design-pattern(设计模式)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/602895.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux rsh命令教程:远程shell命令的使用和注意事项(附案例详解和注意事项)

Linux rsh命令介绍 rsh&#xff08;remote shell&#xff09;提供用户环境&#xff0c;也就是Shell&#xff0c;以便指令能够在指定的远端主机上执行。rsh连接到指定的主机&#xff0c;并执行指定的命令。rsh将其标准输入复制到远程命令&#xff0c;将远程命令的标准输出复制到…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《考虑多元不确定性和备用需求的微电网双层鲁棒容量规划》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 这个标题涉及微电网&#xff08;Microgrid&#xff09;的双层鲁棒容量规划&#xff0c;考虑了多元不确定性和备用需求。让我们逐步解读这个标题&#xf…

【KD】知识蒸馏(knowledge distillation)简单介绍

最近学到了知识蒸馏的相关知识&#xff0c;来简单总结一下૮꒰ ˶• ༝ •˶꒱ა。 知识蒸馏 知识蒸馏&#xff0c;是一种模型压缩的手段。通过训练学生模仿教师的行为&#xff0c;将嵌入在大的教师模型中的知识迁移到小的学生模型。 例如&#xff0c;TinyBERT(Jiao et al.,2…

RocketMQ源码 发送顺序消息源码分析

前言 rocketmq 发送顺序消息和普通消息的主流程区别大部分一致的&#xff0c;区别在于&#xff1a;普通消息发送时&#xff0c;从所有broker的队列集合中 轮询选择一个队列&#xff0c;而顺序队列可以提供用户自定义消息队列选择器&#xff0c;从NameServer 分配的顺序 broker…

使用JavaScript实现动态生成并管理购物车的深入解析

一、引言 在当前的互联网时代&#xff0c;电子商务已成为我们日常生活的重要组成部分。购物车作为电子商务网站的核心功能之一&#xff0c;其实现方式对于用户体验至关重要。本文将深入探讨如何使用JavaScript实现一个动态生成并管理购物车的功能&#xff0c;并详细介绍其实现…

Linux Shell数学运算与条件测试

一、Shell数学运算 1.Shell常见的算术运算符号 序号算术运算符号意义1、-、*、/、%加、减、乘、除、取余2**幂运算3、–自增或自减4&&、||、&#xff01;与、或、非5、!相等、不相等&#xff0c;也可写成6、、-、*、/、%赋值运算符&#xff0c;a1相等于aa1 2.Shell常…

.NET Standard 支持的 .NET Framework 和 .NET Core

.NET Standard 是针对多个 .NET 实现推出的一套正式的 .NET API 规范。 推出 .NET Standard 的背后动机是要提高 .NET 生态系统中的一致性。 .NET 5 及更高版本采用不同的方法来建立一致性&#xff0c;这种方法在大多数情况下都不需要 .NET Standard。 但如果要在 .NET Framewo…

QT 高DPI解决方案

一、根据DPI实现动态调整控件大小&#xff08;三种方式&#xff09; 1、QT支持高DPI&#xff08;针对整个进程中所有的UI&#xff09; // main函数中 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling)tips&#xff1a;&#xff08;1&#xff09;如果不想全局设置&am…

Nodejs搭配axios下载图片

新建一个文件夹&#xff0c;npm i axios 实测发现只需保留node_modules文件夹&#xff0c;删除package.json不影响使用 1.纯下载图片 其实该方法不仅可以下载图片&#xff0c;其他的文件都可以下载 const axios require(axios) const fs require(fs) var arrPic [https:…

最大输出 18W,集成 Type-C PD 输出和各种快充输出协议

一、产品简介 IP6510是一款集成同步开关的降压转换器、支持 9 种输出快充协议、支持 Type-C 输出和 USB PD协议&#xff0c;为车载充电器、快充适配器、智能排插提供完整的解决方案。 IP6510 内置功率 MOS&#xff0c;输入电压范围是 4.5V到 32V&#xff0c;输出电压范围是 3…

案例101:基于微信小程序的停车共享管理系统设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

一个命令查看linux系统是Centos还是Ubuntu

目 录 一、 背景介绍 二、一个命令查看linux系统的简单方法 1、 uname -a 2、cat /etc/issue 3、lsb_release -a 4、 dmesg | grep Ubuntu 一、 背景介绍 Linux 系统基本上分为两大类&#xff1a; 1. Red Hat 系列&#xff1a;包括 Red Ha…

使用pytorch-superpoint与pytorch-superglue项目实现训练自己的数据集

superpoint与superglue的组合可以实现基于深度学习的图像配准,官方发布的superpoint与superglue模型均基于coco数据训练,与业务中的实际数据或许存在差距,为此实现基于开源的pytorch-superpoint与pytorch-superglue项目实现训练自己的数据集。然而,在训练pytorch-superpoin…

电缆线标书:打造高质量工程的关键一步

电缆线标书制作是工程项目中至关重要的一环&#xff0c;它不仅仅是一份文件&#xff0c;更是对工程质量和实施过程的全面规划和控制。在电缆线标书中&#xff0c;涉及到的内容十分丰富&#xff0c;包括但不限于工程概况、技术要求、材料清单、施工方案、质量控制等多个方面。 …

【LMM 012】TinyGPT-V:24G显存训练,8G显存推理的高效多模态大模型

论文标题&#xff1a;TinyGPT-V: Efficient Multimodal Large Language Model via Small Backbones 论文作者&#xff1a;Zhengqing Yuan, Zhaoxu Li, Lichao Sun 作者单位&#xff1a;Anhui Polytechnic University, Nanyang Technological University, Lehigh University 论文…

AI:118-基于深度学习的法庭口译实时翻译

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

VMware中删除虚拟机

虚拟机使用完成后&#xff0c;需要删除虚拟机如何操作呢&#xff1f; 1.首先进入VMware 2.选择需要删除的虚拟机&#xff0c;点击右键 3.直接选择“移除”&#xff1f; 当然不是&#xff0c;这只是从这么目录显示中去掉了&#xff0c;并非 “真正” 删除该虚拟机 注意&#x…

正交投影矩阵与透视投影矩阵的推导

正交投影矩阵 正交投影矩阵的视锥体是一个长方体 [ l , r ] [ b , t ] [ f , n ] [l,r][b,t][f,n] [l,r][b,t][f,n]&#xff0c;我们要把这个长方体转换到一个正方体 [ − 1 , 1 ] [ − 1 , 1 ] [ − 1 , 1 ] [-1,1][-1,1][-1,1] [−1,1][−1,1][−1,1]中&#xff0c;如下图所…

机器学习--ROC AUC

参考 机器学习-ROC曲线 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/347470776一文看懂ROC、AUC - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/81202617 在了解之前&#xff0c;我们先来认识一下以下的概念 针对一个二分类问题&#xff0c;将实例分成正类(postive)或…

常见算法(JavaScript版)

持续更新中… 目录 排序冒泡排序选择排序插入排序希尔排序快速排序&#xff08;必须掌握&#xff09;优化枢纽选择 堆排序归并排序 查找算法二分查找 排序 假设以下所有排序都是升序 快速排序在大部分情况下是效率最高的&#xff0c;所以笔试的时候要求写排序算法&#xff0…