【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<>();

    @Override
    public void set(String contextKey, Object contextValue) {
        contextMap.put(contextKey, contextValue);
    }

    @Override
    public 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 {

    @Override
    public 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 {

    @Override
    protected 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 {

    @Override
    protected 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<>();

    @Override
    public 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;
    }

    @Override
    public boolean addValue(PipelineValue value) {
        if (valueList.contains(value)) {
            return true;
        }

        return valueList.add(value);
    }

    @Override
    public 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 {

    @Override
    public 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 {

    @Override
    public 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;

    @Override
    public void doFilter(HttpRequest httpRequest) {
        if (currentIndex == filterList.size()) {
            return;
        }

        Filter filter = filterList.get(currentIndex);
        currentIndex = currentIndex + 1;
        filter.doFilter(httpRequest, this);
    }

    @Override
    public 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 {

    @Override
    public String getName() {
        return getClass().getSimpleName();
    }
}

//
package cn.thinkinjava.design.pattern.extend.eventdispatch.event;

/**
 * @author qiuxianbao
 * @date 2024/01/02
 */
public class EventFor2 implements Event {

    @Override
    public 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 {

    @Override
    public boolean supportEvent(Event event) {
        return event.getName().contains("Test");
    }

    @Override
    public 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 {

    @Override
    public 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 {

    @Override
    public 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{

    @Override
    public <T> boolean handlerRead(T domain, Field domainField, PiiContent piiContent) {
        log.info("{} handlerRead {}", this.getClass().getSimpleName(), domain.getClass().getSimpleName());
       return true;
    }

    @Override
    public <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 {

    @Override
    public <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;
    }

    @Override
    public 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 {

    @Override
    public <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;
    }

    @Override
    public 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 {

    @Override
    public 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;
    }

    @Override
    public 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(设计模式)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++ Qt开发:Charts与数据库组件联动

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍Charts组件与QSql数据库组件的常用方法及灵活…

【STM32】STM32学习笔记-定时器定时中断 定时器外部时钟(14)

00. 目录 文章目录 00. 目录01. 定时器中断相关API1.1 TIM_InternalClockConfig1.2 TIM_TimeBaseInit1.3 TIM_TimeBaseInitTypeDef1.4 TIM_ClearFlag1.5 TIM_ITConfig1.6 TIM_Cmd1.7 中断服务函数1.8 TIM_ETRClockMode2Config 02. 定时器定时中断接线图03. 定时器定时中断示例0…

嵌入式项目——平衡小车(1)

焊接 驱动板需要焊接的如上图。 陀螺仪8pin排母电机两路排线插口。(个别同学需要焊接)两个电池仓,注意电池仓分正反。 安装 底部电池板 4个 双通尼龙柱M3*224个 尼龙螺钉M3*6电机驱动板

个人笔记:分布式大数据技术原理(二)构建在 Hadoop 框架之上的 Hive 与 Impala

有了 MapReduce&#xff0c;Tez 和 Spark 之后&#xff0c;程序员发现&#xff0c;MapReduce 的程序写起来真麻烦。他们希望简化这个过程。这就好比你有了汇编语言&#xff0c;虽然你几乎什么都能干了&#xff0c;但是你还是觉得繁琐。你希望有个更高层更抽象的语言层来描述算法…

Java数组:一维数组、二维数组、Arrays

文章目录 1、一维数组1.1 数组概念1.2 数组的声明1.3 数组的静态初始化1.4 数组的动态初始化1.5 地址值1.6 数组元素访问1.7 索引1.8 数组的遍历1.9 数组两种初始化方式的区别 2、二维数组2.1 二维数组的声明2.2 二维数组的静态初始化2.3 二维数组的动态初始化2.4 二维数组的遍…

扩散模型: Diffusion Model概念讲解

Diffusion Model 课件视频前向扩散过程 在原始图像中逐步添加高斯分布随机噪声,直到最后得到完全噪声的图像。 反向降噪过程 逐步去除噪声图中的噪声,得到最后原图。 根据噪声图和时间步得到预测的噪声,然后噪声图减去噪声得到原始图 噪声预测的标签来自于前向扩散过程中添加…

TSINGSEE青犀智能分析网关V4在智慧园区车辆违停检测场景中的应用

一、背景与需求 园区作为企业办公、生产制造的重要场所&#xff0c;主要道路车辆违停等违规行为会对园区的安全造成隐患&#xff0c;并且在上下班高峰期内&#xff0c;由于发现不及时&#xff0c;车辆违停行为会造成出入口拥堵现象&#xff0c;这也成为园区管理的棘手问题。为了…

胡圆圆的暑期实习经验分享

背景 实验室一般是在研究生二年级的时候会放实习&#xff0c;在以后的日子就是自己完成毕业工作要求&#xff0c;基本上不再涉及实验室的活了&#xff0c;目前是一月份也是开始准备暑期实习的好时间。实验室每年这个时候都会有学长学姐组织暑期实习经验分享&#xff0c;本着不…

uniappVue3版本中组件生命周期和页面生命周期的详细介绍

一、什么是生命周期&#xff1f; 生命周期有多重叫法&#xff0c;有叫生命周期函数的&#xff0c;也有叫生命周期钩子的&#xff0c;还有钩子函数的&#xff0c;其实都是代表&#xff0c;在 Vue 实例创建、更新和销毁的不同阶段触发的一组钩子函数&#xff0c;这些生命周期函数…

STM32F407-14.3.10-表73具有有断路功能的互补通道OCx和OCxN的输出控制位-00x00-11x11(总结)

如上表73所示&#xff0c;主输出使能&#xff08;MOE0&#xff09;的8种OCx与OCxN的输出状态及波形图&#xff0c;已经单独整理输出8篇文章&#xff0c;方便需要时单独回查。 根据表73可得以下结论 1、从00x00~01x00的前5种状态的OCx与OCxN的引脚电平全由GPIO端口的上下拉决定…

Java序列化篇----第一篇

系列文章目录 文章目录 系列文章目录前言一、什么是java序列化,如何实现java序列化?二、保存(持久化)对象及其状态到内存或者磁盘三、序列化对象以字节数组保持-静态成员不保存四、序列化用户远程对象传输前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,…

1389 蓝桥杯 二分查找数组元素 简单

1389 蓝桥杯 二分查找数组元素 简单 //C风格解法1&#xff0c;lower_bound(),通过率100% //利用二分查找的方法在有序的数组中查找&#xff0c;左闭右开 #include <bits/stdc.h> using namespace std;int main(){int data[200];for(int i 0 ; i < 200 ; i) data[i] …

回首2023,期待2024!

2023&#xff0c;在改变中到来 2023年1月1日&#xff0c;我从成都冷清的学校回到了哈尔滨的老家&#xff0c;开始了保研之前的最后一个寒假 当时的目标是将之前的科研理论转化为实际&#xff0c;生产出一篇sci&#xff0c;助力保研加分 星移斗转&#xff0c;事与愿违&#x…

贯穿设计模式-责任链模式

样例代码 涉及到的项目样例代码均可以从https://github.com/WeiXiao-Hyy/Design-Patterns.git获取 需求 实时地&#xff0c;根据city&#xff0c;sex&#xff0c;product字段进行业务投放&#xff0c;比如&#xff1a;北京的男生&#xff1b;四川的电脑等等 → 责任链模式&…

React之useRef hook

介绍 useRef是react的自定义hook&#xff0c;它用来引用一个不需要渲染的值。这篇文章会介绍useRef的简单用法。 使用场景 1.实现节流 通过useRef实现节流功能&#xff0c;在限制时间内多次提交&#xff0c;已第一次提交为准。 useThrottle.jsx import {useEffect, useRef,…

【gRPC学习】使用go学习gRPC

个人博客:Sekyoro的博客小屋 个人网站:Proanimer的个人网站 RPC是远程调用,而google实现了grpc比较方便地实现了远程调用,gRPC是一个现代的开源远程过程调用(RPC)框架 概念介绍 在gRPC中&#xff0c;客户端应用程序可以直接调用另一台计算机上的服务器应用程序上的方法&#…

Generator - JavaScript的异步颠覆者

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》 ​ ​ 目录 ✨ 前言 什么是Generator 生成器函数的执行流程控制 异步编程应用 ✨ 结语 ✨ 前言 Java…

滑动窗口协议仿真(2024)

1.题目描述 滑动窗口协议以基于分组的数据传输协议为特征&#xff0c;该协议适用于在数据链路层以及传输层中对按 顺序传送分组的可靠性要求较高的环境。在长管道传输过程&#xff08;特别是无线环境&#xff09;中&#xff0c;相应的滑动窗口 协议可实现高效的重传恢复。附录 …

[Redis实战]分布式锁

四、分布式锁 4.1 基本原理和实现方式对比 分布式锁&#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁&#xff0c;只要大家使用的是同一把锁&#xff0c;那么我们就能锁住线程&#xff0c;不让线程进行&#xf…

浏览器的渲染流程

✨专栏介绍 在当今数字化时代&#xff0c;Web应用程序已经成为了人们生活和工作中不可或缺的一部分。而要构建出令人印象深刻且功能强大的Web应用程序&#xff0c;就需要掌握一系列前端技术。前端技术涵盖了HTML、CSS和JavaScript等核心技术&#xff0c;以及各种框架、库和工具…