【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.rhkb.cn/news/232400.html

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

相关文章

【话题】ChatGPT等大语言模型为什么没有智能2

我们接着上一次的讨论&#xff0c;继续探索大模型的存在的问题。正巧CSDN最近在搞文章活动&#xff0c;我们来看看大模型“幻觉”。当然&#xff0c;本文可能有很多我自己的“幻觉”&#xff0c;欢迎批评指正。如果这么说的话&#xff0c;其实很容易得出一个小结论——大模型如…

深入理解CRON表达式:时间调度的艺术

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

用友U8+CRM 逻辑漏洞登录后台漏洞复现

0x01 产品简介 用友U8 CRM客户关系管理系统是一款专业的企业级CRM软件&#xff0c;旨在帮助企业高效管理客户关系、提升销售业绩和提供优质的客户服务。 0x02 漏洞概述 用友 U8 CRM客户关系管理系统 reservationcomplete.php文件存在逻辑漏洞&#xff0c;未授权的攻击者通过…

分布式锁3: zk实现分布式锁3 使用临时顺序节点+watch监听实现阻塞锁

一 zk实现分布式锁 1.1 使用临时顺序节点 的问题 接上一篇文章&#xff0c;每个请求要想正常的执行完成&#xff0c;最终都是要创建节点&#xff0c;如果能够避免争抢必然可以提高性能。这里借助于zk的临时序列化节点&#xff0c;实现分布式锁 1. 主要修改了构造方法和lock方…

案例102:基于微信小程序的旅游社交管理系统设计与实现

文末获取源码 开发语言&#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 小程序…

socket实现视频通话-WebRTC

最近喜欢研究视频流&#xff0c;所以思考了双向通信socket&#xff0c;接下来我们就一起来看看本地如何实现双向视频通讯的功能吧~ 客户端获取视频流 首先思考如何获取视频流呢&#xff1f; 其实跟录音的功能差不多&#xff0c;都是查询电脑上是否有媒体设备&#xff0c;如果…

如何通过内网穿透实现无公网IP远程访问内网的Linux宝塔面板

文章目录 一、使用官网一键安装命令安装宝塔二、简单配置宝塔&#xff0c;内网穿透三、使用固定公网地址访问宝塔 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。…

高性价比LDR6028Type-C转3.5mm音频和PD快充转接器

随着市面上的大部分手机逐渐取消了3.5mm音频耳机接口&#xff0c;仅保留一个Type-C接口&#xff0c;追求音质和零延迟的用户面临着一大痛点。对于这些用户&#xff0c;Type-C转3.5mm接口线的出现无疑是一大福音。这款线材在刚推出时就受到了手机配件市场的热烈欢迎&#xff0c;…

asp.net core使用gb2312编码

nuget包 安装System.Text.Encoding.CodePages 使用 //将byte[]转化为gb2312的字符串&#xff0c;要确保byte[]是存储的gb2312的字符串&#xff0c;要不然会乱码 string ToGb213(byte[] str) {//首先需要注册Encoding.RegisterProvider(CodePagesEncodingProvider.Instance)…

ATTCK视角下的信息收集:主机发现

目录 1、利用协议主动探测主机存活 利用ICMP发现主机 利用ARP发现主机 利用NetBIOS协议发现主机 利用TCP/UDP发现主机 利用DNS协议发现主机 利用PRC协议发现主机程序 2、被动主机存活检测 利用Browser主机探测存活主机 利用ip段探测主机存活 利用net命令探测主机存活…

大模型实战笔记02——大模型demo

大模型实战笔记02——大模型demo 1、大模型及InternLM模型介绍 2、InternLM-Chat-7B智能对话Demo 3、Lagent智能体工具调用Demo 4、浦语灵笔图文创作理解Demo 5、通用环境配置 注 笔记图片均为视频截图 笔记课程视频地址&#xff1a;https://www.bilibili.com/video/BV1Ci4y1…

C# Emgu.CV4.8.0读取rtsp流录制mp4可分段保存

【官方框架地址】 https://github.com/emgucv/emgucv 【算法介绍】 EMGU CV&#xff08;Emgu Computer Vision&#xff09;是一个开源的、基于.NET框架的计算机视觉库&#xff0c;它提供了对OpenCV&#xff08;开源计算机视觉库&#xff09;的封装。EMGU CV使得在.NET应用程序…

鸿蒙开发之拖拽事件

一、拖拽涉及的方法 Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)//拖拽开始.onDragStart((event: DragEvent) > {console.log(drag event onDragStartevent.getX())})//拖拽进入组件范围&#xff0c;需要监听onDrop配合.onDragEnter((event: DragEvent) …

今日小寒|三九将至,注意防寒保暖

小寒小寒&#xff0c;冻成一团 小寒是反应气温变化的节令 这时正值“三九”前后 标志着开始进入 一年中最寒冷的日子 小寒&#xff0c;是二十四节气中的第23个节气&#xff0c;冬季的第5个节气。小寒一过&#xff0c;就进入的三九天了&#xff0c;民间有“小寒胜大寒”之说…

使用 Maven 的 dependencyManagement 管理项目依赖项

使用 Maven 的 dependencyManagement 管理项目依赖项 介绍 在开发 Java 项目时&#xff0c;管理和协调依赖项的版本号是一项重要而繁琐的任务。 而 Maven 提供了 <dependencyManagement> 元素&#xff0c;用于定义项目中所有依赖项的版本。它允许您指定项目中每个依赖…

Netty实战(待完善)

Netty组件 1. Bootstrap, ServerBootstrap Netty 中 Bootstrap 类是客户端程序的启动引导类&#xff0c;ServerBootstrap 是服务端启动引导类。 2. NioEventLoop, NioEventLoopGroup NioEventLoop 中维护了一个线程和任务队列&#xff0c;支持异步提交执行任务&#xff0c;…

C++中的new和delete

相关文章 C智能指针 文章目录 相关文章前言一、new 运算符1. operator new 函数的范围2. 在类中重载new运算符3. 分配失败 二、delete 运算符1. 内存泄露统计示例2. 在类中重载delete运算符 总结 前言 在C中&#xff0c;new和delete是用于动态内存管理的运算符&#xff0c;它们…

el-upload上传文件

需求&#xff1a;选中或拖拽文件后&#xff0c;使用http-request属性实现自动上传&#xff0c;并根据后端传回来的结果显示错误和控制fileList的显示&#xff0c;如果后端返回成功&#xff0c;则文件显示在文件列表处&#xff0c;如果后端返回失败&#xff0c;则文件列表不显示…

听GPT 讲Rust源代码--compiler(15)

File: rust/compiler/rustc_arena/src/lib.rs 在Rust源代码中&#xff0c;rustc_arena/src/lib.rs文件定义了TypedArena&#xff0c;ArenaChunk&#xff0c;DroplessArena和Arena结构体&#xff0c;以及一些与内存分配和容器操作相关的函数。 cold_path<F: FnOnce,drop,new,…

基于商品列表的拖拽排序后端实现

目录 一&#xff1a;实现思路 二&#xff1a;实现步骤 二&#xff1a;实现代码 三&#xff1a;注意点 一&#xff1a;实现思路 后台实现拖拽排序通常需要与前端进行配合&#xff0c;对商品的列表拖拽排序&#xff0c;前端需要告诉后端拖拽的元素和拖动的位置。 这里我们假…