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

news2024/10/1 21:40:07

写在前面

  偶然间看到一篇文章 《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(设计模式)

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

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

相关文章

第11章 GUI Page462~476 步骤二十三,二十四,二十五 Undo/Redo ③实现“Undo/Redo”菜单项

工程六 添加“编辑”菜单和子菜单 菜单ID分别为 idMenuEditUndo 和 idMenuEditRedo 热键&#xff08;快捷键&#xff09;分别为CtrlZ 和 CtrlShiftZ 变量名分别为 MenuItemEditUndo 和 MenuItemEditRedo 分别添加事件 ActionLink类增加成员函数 运行效果&#xff1a;“添加…

【C语言】Linux实现高并发处理的过程

一、实现高并发的几种策略 C语言本身并没有内建的多线程支持&#xff08;新版C语言支持&#xff0c;但用得不多&#xff09;&#xff0c;但是在多数操作系统中&#xff0c;可以使用库来实现多线程编程。例如&#xff0c;在POSIX兼容系统上&#xff0c;可以使用 pthreads 库来创…

【Web开发】会话管理与无 Cookie 环境下的实现策略

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; Web开发 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 问题&#xff1a; 思路&#xff1a; 方法&#xff1a; 结语 我的其他博客 前言 在当今Web应用程序中&#xff0c;会话…

实时语义分割模型PP-LiteSeg论文解读

paper&#xff1a;PP-LiteSeg: A Superior Real-Time Semantic Segmentation Model official implementation&#xff1a;https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.8/paddleseg/models/pp_liteseg.py 本文的创新点 提出了一种灵活的轻量级解码器&#xf…

力扣:438. 找到字符串中所有字母异位词 题解

Problem: 438. 找到字符串中所有字母异位词 438. 找到字符串中所有字母异位词 预备知识解题思路复杂度Code其它细节推荐博客或题目博客题目滑动窗口哈希表 预备知识 此题用到了双指针算法中的滑动窗口思想&#xff0c;以及哈希表的运用。c中是unordered_map。如果对此不了解的u…

基于Spring-boot-websocket的聊天应用开发总结

目录 1.概述 1.1 Websocket 1.2 STOMP 1.3 源码 2.Springboot集成WS 2.1 添加依赖 2.2 ws配置 2.2.1 WebSocketMessageBrokerConfigurer 2.2.2 ChatController 2.2.3 ChatInRoomController 2.2.4 ChatToUserController 2.3 前端聊天配置 2.3.1 index.html和main.j…

路由器01_工作原理

一、回顾交换机工作原理 交换机里面维护了一张MAC地址表&#xff0c;主要记录的是MAC地址和接口的对应关系。 交换机在初始状态下&#xff0c;MAC地址表是空的&#xff0c;当收到一个来自某接口的数据时&#xff0c;首先查看数据帧中的MAC地址表&#xff0c;对照自己的MAC地址…

MySQL进阶篇(二) 索引

一、索引概述 1. 介绍 索引&#xff08;index&#xff09;是帮助 MySQL 高效获取数据 的 数据结构&#xff08;有序&#xff09;。在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用&#xff08;指向&#xff09;数…

React 实现拖放功能

介绍 本篇文章将会使用react实现简单拖放功能。 样例 布局拖放 LayoutResize.js import React, {useState} from "react"; import { Button } from "antd"; import "./LayoutResize.css";export const LayoutResize () > {const [state,…

imgaug库指南(六):从入门到精通的【图像增强】之旅

引言 在深度学习和计算机视觉的世界里&#xff0c;数据是模型训练的基石&#xff0c;其质量与数量直接影响着模型的性能。然而&#xff0c;获取大量高质量的标注数据往往需要耗费大量的时间和资源。正因如此&#xff0c;数据增强技术应运而生&#xff0c;成为了解决这一问题的…

海康威视安全接入网关 任意文件读取漏洞复现

0x01 产品简介 海康威视安全接入网关是一种网络安全产品,旨在提供安全、可靠的远程访问和连接解决方案. 0x02 漏洞概述 海康威视安全接入网关使用Jquery-1.7.2 , 该版本存在任意文件读取漏洞,可获取服务器内部敏感信息泄露(安博通应用网关也存在此漏洞) 0x03 复现环境 …

未完成销量任务的智己汽车突发大规模车机故障,竞争压力不小

2024年刚开年&#xff0c;智己汽车便上演了一出“开门黑”。 近日&#xff0c;不少车主在社交平台发帖&#xff0c;反映智己LS6出现大规模车机故障&#xff0c;包括但不限于主驾驶屏幕不显示车速、档位、行驶里程&#xff0c;左右转盲区显示失效&#xff0c;无转向灯、雷达提醒…

04 帧 Frame

文章目录 04 帧 Frame4.1 相机相关信息4.2 特征点提取4.2.1 特征点提取 ExtractORB()4.3 ORB-SLAM2对双目/RGBD特征点的预处理4.3.1 双目视差公式4.3.2 双目图像特征点匹配 ComputeStereoMatches()4.3.3 根据深度信息构造虚拟右目图像&#xff1a;ComputeStereoFromRGBD() 4.4 …

Unity 欧盟UMP用户隐私协议Android接入指南

Unity 欧盟UMP用户协议Android接入指南 官方文档链接开始接入mainTemplate.gradle 中引入CustomUnityPlayerActivity 导入UMP相关的包java类中新增字段初始化UMPSDK方法调用![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/d882171b068c46a1b956e80425f3a9cf.png)测…

【STM32】STM32学习笔记-ADC单通道 ADC多通道(22)

00. 目录 文章目录 00. 目录01. ADC简介02. ADC相关API2.1 RCC_ADCCLKConfig2.2 ADC_RegularChannelConfig2.3 ADC_Init2.4 ADC_InitTypeDef2.5 ADC_Cmd2.6 ADC_ResetCalibration2.7 ADC_GetResetCalibrationStatus2.8 ADC_StartCalibration2.9 ADC_GetCalibrationStatus2.10 A…

网络优化篇(一)---------TCP重传性能优化

本文通过一个TCP重传优化的实际问题,详细讲解问题的分析、定位、优化过程。 通过本文你将学到: 如何通过linux命令和/proc文件系统分析TCP性能数据如何通过linux命令和netlink api分析某个具体的TCP连接的性能数据如何通过bcc工具分析TCP性能数据如何通过调整系统参数优化TCP重…

63.接口安全设计(活动管理系统:三)

文章目录 一、参数校验二、统一封装返回值三、做权限控制四、加验证码五、 限流六、加ip白名单七、校验敏感词八、使用https协议九、数据加密十、做风险控制 在日常工作中&#xff0c;开发接口是必不可少的事情&#xff0c;无论是RPC接口还是HTTP接口&#xff0c;我们都应该考虑…

opencv007 图像运算——加减乘除

今天学习图像处理的基础——加减乘除&#xff0c;总体来说比较好理解&#xff0c;不过生成的图片千奇百怪哈哈哈哈 opencv中图像的运算本质是矩阵的运算 加法 做加法之前要求两张图片形状&#xff0c;长宽&#xff0c;通道数完全一致 cv2.add(img1, img2) add的规则是两个图…

【算法笔记】深入理解dfs(两道dp题)

DFS过程的概述 一个一个节点的搜&#xff0c;如果是树状结构的话&#xff0c;先找到最左边那一条分支搜到最后一个节点&#xff0c;这个时候最后一个节点&#xff08;假设是b&#xff09;的数据会被更新&#xff08;具体看题目的要求&#xff09;&#xff0c;然后返回到上一个…

debug mccl 02 —— 环境搭建及初步调试

1, 搭建nccl 调试环境 下载 nccl 源代码 git clone --recursive https://github.com/NVIDIA/nccl.git 只debug host代码&#xff0c;故将设备代码的编译标志改成 -O3 (base) hipperhipper-G21:~/let_debug_nccl/nccl$ git diff diff --git a/makefiles/common.mk b/makefiles/…