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

写在前面

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



一、项目结构

  以下为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
    }
}

以下是一个真实场景Netty客户端接收数据的示例,代码中运用到了 “事件派发” + “工厂(参数转换器)”。简单说一下思路,具体代码请前往GitHub自行查找。

在Netty服务端下发指令后,客户端可以根据不同的指令进行相应的逻辑处理。
由于客户端入口只有一个TcpClientHandler#channelRead0,但是逻辑有很多个。这个时候,就可以用事件派发模式。

把不同逻辑组装起来统一管理,然后在入口处,根据不同的指令调用不同的操作。本质上解决是if...else的问题。

核心:
1.自定义注解@TcpMapping,用于实现不用的逻辑
2.定义TcpMappingScan用于收集@TcpMapping,进行统一管理
3.调用是从统一管理处取出,通过反射操作

在这里插入图片描述


模板+工厂模式

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

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

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

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值