策略模式

约 778 字大约 3 分钟

策略模式

策略设计模式其实就是多态,最好结合责任链、享元进行处理会更优。

问题

本章节主要是解决 Controller 和 Service 代码爆炸问题,在原有基础上进行了重构

.
├── service
│   ├── TwoPolicyService.java
│   ├── impl
│   │   ├── HelloTwoPolicyServiceImpl.java
│   │   ├── NilTwoPolicyServiceImpl.java
│   │   └── WorldTwoPolicyServiceImpl.java
│   └── manager
│       └── TwoPolicyManager.java
└── web
    ├── TwoPolicyController.http
    └── TwoPolicyController.java

看一下 Controller 代码

@RequestMapping("/two")
@RestController
public class TwoPolicyController {

    @GetMapping("/policy")
    public String policy(HelloRequest request) {
        return ThreePolicyManager.getPolicy(request.getPolicy()).print(request);
    }
}

看一下 Service 代码

public interface TwoPolicyService extends InitializingBean {
    /**
     * @param helloRequest 请求
     */
    String print(HelloRequest helloRequest);
}
@Service
public class HelloTwoPolicyServiceImpl implements ThreePolicyService {

    @Override
    public String print(HelloRequest helloRequest) {
        return helloRequest.getName() + "hello";
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ThreePolicyManager.register("hello", this);
    }
}

@Service
public class WorldTwoPolicyServiceImpl implements ThreePolicyService {
    @Override
    public String print(HelloRequest helloRequest) {
        return helloRequest.getName() + "world";
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ThreePolicyManager.register("world", this);
    }
}
@Service
public class NilTwoPolicyServiceImpl implements ThreePolicyService {

    @Override
    public String print(HelloRequest helloRequest) {
        return ThreePolicyManager.NIL;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ThreePolicyManager.register(ThreePolicyManager.NIL, this);
    }

}

这里需要加一个管理器,TwoPolicyManager(享元):

public final class TwoPolicyManager {

    public static final String NIL = "Nil";

    private static final Map<String, ThreePolicyService> CONTAINER = new HashMap<>();

    public static boolean register(String key, ThreePolicyService service) {
        return CONTAINER.put(key, service) != null;
    }

    public static ThreePolicyService getPolicy(String channel) {
        ThreePolicyService policyService = CONTAINER.get(channel);
        if (Objects.isNull(policyService)) {
            return CONTAINER.get(NIL);
        }
        return policyService;
    }

}

然后进行测试:


 Testing started at 10:25 ...

 HTTP/1.1 200
 Content-Type: text/plain;charset=UTF-8
 Content-Length: 8
 Date: Mon, 20 Apr 2020 02:25:34 GMT
 Keep-Alive: timeout=60
 Connection: keep-alive

 > 2020-04-20T102534.200.txt > renhello

 Response code: 200; Time: 22ms; Content length: 8 bytes
 HTTP/1.1 200
 Content-Type: text/plain;charset=UTF-8
 Content-Length: 8
 Date: Mon, 20 Apr 2020 02:25:34 GMT
 Keep-Alive: timeout=60
 Connection: keep-alive

 > 2020-04-20T102534-1.200.txt >renworld

 Response code: 200; Time: 46ms; Content length: 8 bytes

如果 HelloRequest 是从数据库获取的,你可以在 Service 层调用,可以返回特定结果,你发现好像一切都变好了, 但是你发现 ThreePolicyManager.register("world", this); 好像每个实现类都需要写一遍,好累赘, 是不是有好方法解决它呢,想了想注解+枚举+适配器是个不错的方案,可以解决 key 冲突问题和减少代码量。 枚举代码和注解如下:

@Getter
@AllArgsConstructor
public enum PolicyEnum {

    HELLO("hello", "helloPolicy"),

    WORLD("world", "worldPolicy"),

    NIL("Nil", "NilPolicy"),

    ;

    private final String code;
    private final String desc;
}

@Component
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public  @interface PolicyType {

    @NonNull
    PolicyEnum value();

}

代码中间加一层适配器:

public abstract class ThreePolicyAdapt implements ThreePolicyService {

    @Override
    public void afterPropertiesSet() throws Exception {
        PolicyType annotation = this.getClass().getAnnotation(PolicyType.class);
        if (Objects.nonNull(annotation)) {
            ThreePolicyManager.register(annotation.value().getCode(), this);
        }
    }
}

实现类变成了下面的样子:

@PolicyType(PolicyEnum.HELLO)
public class HelloThreePolicyServiceImpl extends ThreePolicyAdapt  {

    @Override
    public String print(HelloRequest helloRequest) {
        return helloRequest.getName() + "hello";
    }

}

@PolicyType(PolicyEnum.WORLD)
public class WorldThreePolicyServiceImpl extends ThreePolicyAdapt {
    @Override
    public String print(HelloRequest helloRequest) {
        return helloRequest.getName() + "world";
    }
}

@PolicyType(PolicyEnum.NIL)
public class NilThreePolicyServiceImpl extends ThreePolicyAdapt {

    @Override
    public String print(HelloRequest helloRequest) {
        return ThreePolicyManager.NIL;
    }
}

现在需要增加策略, 只需要增加一个类,继承 ThreePolicyAdapt,并在实现上增加一个注解,就 ok。 缺点: 每次都需要在枚举类中增加一个类型 HELLO("hello", "helloPolicy")。 如果不想增加适配器,使用 Spring 特性又如何解决上面的问题呢?使用 Spring 初始化 bean 的钩子。

@Component
public class SpringThreePolicyManager implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        PolicyType policyType = AnnotationUtils.findAnnotation(bean.getClass(), PolicyType.class);
        if (Objects.nonNull(policyType)) {
            PolicyEnum policyEnum = policyType.value();
            ThreePolicyManager.register(policyEnum.getCode(), (ThreePolicyService) bean);
        }
        return bean;
    }

}

缺点: 如果上面管理器很多,需要很多钩子,然后给你个眼神你体会一下吧,怎么抉择取决于你。

Github 地址open in new window