Spring 完成策略模式--自定义注解方法解耦if...else

策略模式

界定

界定一簇优化算法类,将每一个优化算法各自封裝起來,让她们能够相互之间更换,策略模式能够使优化算法的转变单独于应用他们的手机客户端

情景

应用策略模式,能够防止冗杂的if-else 或 switch支系分辨

完成

  1. 对策的界定

    对策的界定必须界定一个对策插口和一组完成这一插口的对策类,由于全部的对策类都完成同样的插口

public interface Strategy{
	void algorithm();
}

public class ConcreteStrategyA implements Strategy {
 @Override
 public void algorithm() {
 //实际的优化算法...
 }
}
public class ConcreteStrategyB implements Strategy {
 @Override
 public void algorithm() {
 //实际的优化算法...
 }
}
  1. 对策的建立

    在应用的情况下,一般会根据种类来分辨建立哪一个对策来应用,在对策前后文中,能够应用map维护保养好对策类

  2. 对策的应用

    策略模式包括一组可选对策,在应用对策时,一般如何确定应用哪一个对策呢?最普遍的是运作时动态性明确应用哪一种对策。程序流程在运作期内,依据配备、数值、互联网等这种不确定因素,动态性决策应用哪一种对策

public class StrategyContext{
	private static final Map<String, Strategy> strategies = new HashMap<>();
	static {
     strategies.put("A", new ConcreteStrategyA());
     strategies.put("B", new ConcreteStrategyB());
    }
    private static Strategy getStrategy(String type) {
         if (type == null || type.isEmpty()) {
    	     throw new IllegalArgumentException("type should not be empty.");
         }
         return strategies.get(type);
	}
    public void algorithm(String type){
        Strategy strategy = this.getStrategy(type);
        strategy.algorithm();
    }
}

UML

策略模式的建立和应用--Spring和自定义注解

在详细介绍策略模式时,在前后文中应用了map储存好的对策案例,在依据type获得实际的对策,启用对策优化算法。
当必须加上一种对策时,必须改动context编码,这违背了开闭原则:对改动关掉,对拓展对外开放。

要完成对拓展对外开放,就需要对type和实际的对策完成类在编码中开展关系,能够应用自定义注解的方法,在注释中特定对策的type。
对策前后文完成类完成 BeanPostProcessor 插口,在该插口中撰写对策种类与bean的关联并维护保养到对策前后文中。

package com.masterlink.strategy;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
public class StrategyDemoBeanPostProcessor implements BeanPostProcessor, Ordered {

    private final Set<Class<?>> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64));

    private final StrategyContext strategyContext;

    private StrategyDemoBeanPostProcessor(StrategyContext context) {
        this.strategyContext = context;
    }

    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }

    @Override
    public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {

        if (!this.nonAnnotatedClasses.contains(bean.getClass())) {
            // 获得应用 @StrategyDemo 注释的Class信息内容
            Class<?> targetClass = AopUtils.getTargetClass(bean);
            Class<Strategy> orderStrategyClass = (Class<Strategy>) targetClass;
            StrategyDemo ann = findAnnotation(targetClass);
            if (ann != null) {
                processListener(ann, orderStrategyClass);
            }
        }
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    protected void processListener(StrategyDemo annotation,
                                   Class<Strategy> classes) {
        // 申请注册对策
        this.strategyContext
                .registerStrategy(annotation.type(), classes);
    }

    private StrategyDemo findAnnotation(Class<?> clazz) {

        StrategyDemo ann = AnnotatedElementUtils.findMergedAnnotation(clazz, StrategyDemo.class);
        return ann;
    }

}


@Component
public class StrategyContext implements ApplicationContextAware {
    private final Map<String, Class<Strategy>> strategyClassMap = new ConcurrentHashMap<>(64);

    private final Map<String, Strategy> beanMap = new ConcurrentHashMap<>(64);

    private ApplicationContext applicationContext;

    /**
     * 申请注册对策
     * @param type
     * @param strategyClass
     */
    public void registerStrategy(String type, Class<Strategy> strategyClass){
        if (strategyClassMap.containsKey(type)){
            throw new RuntimeException("strategy type:" type " exist");
        }
        strategyClassMap.put(type, strategyClass);
    }

    /**
     * 实行对策
     * @param type
     */
    public void algorithm(String type){
        Strategy strategy = this.getStrategy(type);
        strategy.algorithm();
    }

    private Strategy getStrategy(String type) {
        if (type == null || type.isEmpty()) {
            throw new IllegalArgumentException("type should not be empty.");
        }
        Class<Strategy> strategyClass = strategyClassMap.get(type);
        return createOrGetStrategy(type, strategyClass);
    }

    private Strategy createOrGetStrategy(String type,Class<Strategy> strategyClass ){
        if (beanMap.containsKey(type)){
            return beanMap.get(type);
        }
        Strategy strategy = this.applicationContext.getBean(strategyClass);
        beanMap.put(type, strategy);
        return strategy;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

好用实例

在大家的服务平台中,有一部分是应用的netty架构撰写的tcp服务项目,在服务器端,必须将二进制转换为目标,在协议书设计,界定第一个字节数表明目标种类,例如int,String等,第二三个字节数,表明数据信息长短,后边的字节数位传送內容。
例如,
0x01, 0x00, c004, 0x00, 0x00, 0x00, c009,分析出去的內容是int类型数字9。
c002, 0x00, c003, c031, c032, c033, 分析出的內容是String种类,內容是 123。
在没有应用策略模式的情况下,必须将第一个字节数分析出去,然会应用if--else分辨种类,对后续的字节数开展分析。
在具体的完成全过程中,是应用了策略模式,而且应用注释的方法表明基本数据类型,完成全过程以下。

界定对策插口和注释

界定 CodecStrategyType 注释和编号视频解码器的对策插口 CodecStrategy

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CodecStrategyType {
    /**
     * 编码解码种类
     * @return
     */
    byte type();
}

public interface CodecStrategy<T> {
    T decoding(byte[] buffer);
}

/*
* 通用性编解码插口
 */
public interface Codec {
    Object decoding(byte[] bytes);
}


对策完成

完成二种种类的视频解码器: Integer  和 String

/**
 * integer编解码
 */
@CodecStrategyType(type = (byte)0x01)
@Service
public class IntgerCodecStrategy implements CodecStrategy<Integer> {
    @Override
    public Integer decoding(byte[] buffer) {
        int value;
        value = (int) ((buffer[3] & c0FF)
                | ((buffer[2] & c0FF)<<8)
                | ((buffer[1] & c0FF)<<16)
                | ((buffer[0] & c0FF)<<24));
        return value;
    }
}

@CodecStrategyType(type = (byte)c002)
@Service
public class StringCodecStrategy implements CodecStrategy<String> {

    @Override
    public String decoding(byte[] bufferr) {
        return new String(bufferr);
    }
}

对策前后文和对策申请注册

对策前后文类 CodecStrategyContext 给予了统一编解码通道,将 byte[] 变换为 Object 种类,另外给予对策的注释插口 void registerStrategy(Byte type, Class<CodecStrategy<?>> strategyClass) ,申请注册编解码种类相匹配的对策完成类。
对策前后文类另外还给予了对策Bean的建立,依据种类从Spring 的 ApplicationContext 获得对策bean,并缓存文件到map。
对策Bean解决类 CodecStrategyTypeBeanPostProcessor 中分析 CodecStrategyType 注释中特定的种类。


@Component
public class CodecStrategyContext implements ApplicationContextAware, Codec {
    private final Map<Byte, Class<CodecStrategy<?>>> strategyClassMap = new ConcurrentHashMap<>(64);

    private final Map<Byte, CodecStrategy<?>> beanMap = new ConcurrentHashMap<>(64);

    private ApplicationContext applicationContext;

    /**
     * 申请注册对策
     * @param type
     * @param strategyClass
     */
    public void registerStrategy(Byte type, Class<CodecStrategy<?>> strategyClass){
        if (strategyClassMap.containsKey(type)){
            throw new RuntimeException("strategy type:" type " exist");
        }
        strategyClassMap.put(type, strategyClass);
    }

    /**
     * 实行对策
     */
    @Override
    public Object decoding(byte[] bytes){
        Byte type = bytes[0];
        CodecStrategy<?> strategy =this.getStrategy(type);
        byte l1 = bytes[1];
        byte l2= bytes[2];
        short length =  (short) ((l2 & c0FF)
                | ((l1 & c0FF)<<8));
        byte[] contentBytes = new byte[length];
        arraycopy(bytes,3,contentBytes,0, length);
        return strategy.decoding(contentBytes);
    }

    private CodecStrategy<?> getStrategy(Byte type) {
        Class<CodecStrategy<?>> strategyClass = strategyClassMap.get(type);
        return createOrGetStrategy(type, strategyClass);
    }

    private CodecStrategy<?> createOrGetStrategy(Byte type, Class<CodecStrategy<?>> strategyClass ){
        if (beanMap.containsKey(type)){
            return beanMap.get(type);
        }
        CodecStrategy<?> strategy = this.applicationContext.getBean(strategyClass);
        beanMap.put(type, strategy);
        return strategy;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

@Component
public class CodecStrategyTypeBeanPostProcessor implements BeanPostProcessor, Ordered {

    private final Set<Class<?>> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64));

    private final CodecStrategyContext strategyContext;

    private CodecStrategyTypeBeanPostProcessor(CodecStrategyContext context) {
        this.strategyContext = context;
    }

    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }

    @Override
    public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {

        if (!this.nonAnnotatedClasses.contains(bean.getClass())) {
            // 获得应用 @StrategyDemo 注释的Class信息内容
            Class<?> targetClass = AopUtils.getTargetClass(bean);
            Class<CodecStrategy<?>> orderStrategyClass = (Class<CodecStrategy<?>>) targetClass;
            CodecStrategyType ann = findAnnotation(targetClass);
            if (ann != null) {
                processListener(ann, orderStrategyClass);
            }
        }
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    protected void processListener(CodecStrategyType annotation,
                                   Class<CodecStrategy<?>> classes) {
        // 申请注册对策
        this.strategyContext
                .registerStrategy(annotation.type(), classes);
    }

    private CodecStrategyType findAnnotation(Class<?> clazz) {

        CodecStrategyType ann = AnnotatedElementUtils.findMergedAnnotation(clazz, CodecStrategyType.class);
        return ann;
    }

}

应用和检测

检测Integer和String种类的对策:

  1. 0x01, 0x00, c004, 0x00, 0x00, 0x00, c009,分析出去的內容是int类型数字9。
  2. c002, 0x00, c003, c031, c032, c033, 分析出的內容是String种类,內容是 123。

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {CodecStrategyTest.CodecStrategyTestConfig.class})
public class CodecStrategyTest {

    @Resource
    Codec codec;

    @Test
    public void testInterDecoding(){
        byte[] buffer = new byte[]{
                0x01,0x00,  c004, 0x00, 0x00,0x00, c009
        };
        Integer decoding = (Integer)codec.decoding(buffer);
        assertThat(decoding)
                .isNotNull()
                .isEqualTo(9);
    }

    @Test
    public void testStringDecoding(){
        byte[] buffer = new byte[]{
                c002, 0x00, c003, c031, c032,c033
        };
        String decoding = (String)codec.decoding(buffer);
        assertThat(decoding)
                .isNotNull()
                .isEqualTo("123");
    }

    @ComponentScan({"com.masterlink.strategy"})
    @Configuration
    public static class CodecStrategyTestConfig {
    }
}

拓展繁杂种类

自定繁杂种类User类,相匹配协议类型为 c0A0, 第二 、3 字节数表明全部目标的字段距离,随后是 Integer 种类的age 和 String 种类的name,
例如 c0A0, 0x00 c010 0x00, c004, 0x00, 0x00, 0x00, c017, 0x00, c008, c05A,c068,c061,c06E,c067,c053, c061,c06E, 相匹配的user目标是

{
  "age": 23,
  "name": "ZhangSan"
}
@Data
public class User {
    private Integer age;
    private String name;
}

完成编解码对策类

已经知道 User 中的基础类型依靠了 Integer 和 String ,因此在User的编解码对策类中,依靠了 IntgerCodecStrategy 和 StringCodecStrategy


@CodecStrategyType(type = (byte) (c0A0))
@Service
public class UserCodeStrategy implements CodecStrategy<User> {
    private final StringCodecStrategy stringCodecStrategy;
    private final IntgerCodecStrategy intgerCodecStrategy;

    public UserCodeStrategy(StringCodecStrategy stringCodecStrategy, IntgerCodecStrategy intgerCodecStrategy) {
        this.stringCodecStrategy = stringCodecStrategy;
        this.intgerCodecStrategy = intgerCodecStrategy;
    }

    @Override
    public User decoding(byte[] buffer) {
        byte ageL1 = buffer[0];
        byte ageL2 = buffer[1];
        short ageLength =  (short) ((ageL2 & c0FF)
                | ((ageL1 & c0FF)<<8));
        byte[] ageBytes = new byte[ageLength];
        System.arraycopy(buffer,2, ageBytes,0,ageLength);

        byte nameL1 = buffer[0 ageLength];
        byte nameL2 = buffer[1 ageLength];

        short nameLength =  (short) ((nameL2 & c0FF)
                | ((nameL1 & c0FF)<<8));

        byte[] nameBytes = new byte[nameLength];
        System.arraycopy(buffer,2 ageLength 2, nameBytes,0,nameLength);

        User user = new User();
        user.setAge(intgerCodecStrategy.decoding(ageBytes));
        user.setName(stringCodecStrategy.decoding(nameBytes));
        return user;
    }
}

检测

根据检测能够发觉很轻轻松松的就拓展了一个繁杂种类的编解码优化算法,那样伴随着协议书的提升,能够保证对改动编码关掉,对拓展编码对外开放,合乎开闭原则。


    @Test
    public void testUserDecoding(){
        byte[] buffer = new byte[]{
                (byte)c0A0, (byte)0x00 ,(byte)c010 ,(byte)0x00, (byte)c004,
                (byte)0x00, (byte)0x00, (byte)0x00, (byte)c017, (byte)0x00,
                (byte)c008, (byte)c05A, (byte)c068, (byte)c061, (byte)c06E,
                (byte)c067, (byte)c053, (byte)c061, (byte)c06E
        };
        User user = (User)codec.decoding(buffer);
        assertThat(user)
                .isNotNull();
        assertThat(user.getAge()).isEqualTo(23);
        assertThat(user.getName()).isEqualTo("ZhangSan");
    }

汇总

  1. 应用策略模式,能够防止冗杂的if-else 或 switch支系分辨
  2. 把握自定义注解的是应用方法
  3. 与应用 @Service("name") 注释对比,自定义注解方法支撑点和拓展的种类或更灵便

关注我的微信公众号,一起探寻新专业知识新技术应用

评论(0条)

刀客源码 游客评论