天天看点

JAVA编程小技巧--策略模式

作者:极客郭瀚辰

一、定义

策略模式的定义:策略模式就是将一系列算法封装起来,并使它们之间相互替换。被封装起来的算法具有独立性外部不可改变其特性。--《来源于网络》

二、解决的问题

解决代码中充斥大量if...else造成高耦合度和复杂度,增加代码维护成本。

三、UML结构示意图

JAVA编程小技巧--策略模式

四、实际应用

话说小编在入职一家新公司后,接到了一个代码维护的项目,需要面对一堆的祖传代码,其中有一个1000多行的类核心代码大概像下面这个样子:

if(condition1){
        
    }else if(condition2){
        
    }else if(condition3){

    }else if(condition4){

    }else{
        
    }           

每个分支中都有数十行甚至数百行代码,正常情况下小编是不会主动去调整祖传代码的,这里要遵从‘够用原则’,能跑就行。但是这部分代码以后需要我来维护,而且就在小编入职不久就修改了两次,因此,就决定进行优化。

再说一些关于优化代码的题外话。像小编这种情况各位朋友可能也遇到过,自己编写代码的时候也有过类似的情况,项目工期紧、项目初期业务简单,所以在编码的合理性上可能会有不妥的地方。首先,我没有觉得这有什么不妥,还是够用原则。但是在业务发生变化的时候,我们就要考虑优化原来编码中‘不妥’的地方,那么什么时候去优化呢?小编给出的建议是,当同一部分代码改动超过2次以上(再一再二不再三);再者就是,从项目初期就可预见的需要大量的条件判断,这个时候就不要偷懒了,一定要优化,否则都是给自己挖的坑。

下面开始进行代码优化,采用策略模式解决上面if...else。为了安全起见,首先将各个判断语句间的代码从各个分支中剥离出来,然后进行测试,看看输出是否有问题,剥离后的代码如下所示:

if(condition1){
        doSomething1()
    }else if(condition2){
        doSomething2()
    }else if(condition3){
        doSomething3()
    }else if(condition4){
        doSomething4()
    }else{
        doOther()
    }

    doSomething1()
    doSomething2()
    doSomething3()
    doSomething4()
    doOther()           

代码剥离后,就可以编写策略代码,并将各个方法迁移到各自的策略中。

首先,创建HandlerStrategy接口类

interface HandlerStrategy {
    void doSomething();
}           

编写3个实现类,Handler1、Handler2、Handler3

class Handler1 implements HandlerStrategy{

    @Override
    public void doSomething() {
        System.out.println("Handler1--->doSomething");
    }
}           
class Handler2 implements HandlerStrategy{
    @Override
    public void doSomething() {
        System.out.println("Handler2--->doSomething");
    }
}           
class Handler3 implements HandlerStrategy{
    @Override
    public void doSomething() {
        System.out.println("Handler3--->doSomething");
    }
}           

编写策略工厂类

final class HandlerProvider {

    private HandlerProvider() {
    }

    static HandlerStrategy getHandler(String type) {
        if (type.equals("Handler1")) {
            return new Handler1();
        } else if (type.equals("Handler2")) {
            return new Handler2();
        } else if (type.equals("Handler3")) {
            return new Handler3();
        } else {
            return null;
        }
    }
}           

上面的代码完全可以实现功能,但是没有解决耦合的问题,只是将耦合点换了个地方,并且每增加一种策略都需要改动工厂类,不符合开闭原则。

继续优化,增加抽象工厂

public interface HandlerFactory {
    HandlerStrategy createHandler();
}           

分别为不同的handler创建各自的工厂

public class Handler1Factory implements HandlerFactory{
    @Override
    public HandlerStrategy createHandler() {
        return new Handler1();
    }
}           
public class Handler2Factory implements HandlerFactory{
    @Override
    public HandlerStrategy createHandler() {
        return new Handler3();
    }
}           
public class Handler3Factory implements HandlerFactory{
    @Override
    public HandlerStrategy createHandler() {
        return new Handler3();
    }
}           

并将HandlerProvider修改为如下:

final class HandlerProvider {

    private static Map<String,HandlerFactory> handlerFactorys = new HashMap<>(3);

    static {
        handlerFactorys.put("Handler1",new Handler1Factory());
        handlerFactorys.put("Handler2",new Handler2Factory());
        handlerFactorys.put("Handler3",new Handler3Factory());
    }

    private HandlerProvider() {
    }

    static HandlerStrategy getHandler(String type) {
        return handlerFactorys.get(type).createHandler();
    }
}           

此时,我们可以看到已经将if...else优化掉了。但是,新增策略的时候,依然还是要改动

HandlerProvider,并且没增加一种策略都要对应增加抽象工厂,代码量相对较大,继续优化。

将创建handler的抽象工厂类删除,并将创建handler的工厂方法迁移至handler中,代码如下:

class Handler1 implements HandlerStrategy{

    private Handler1(){}

    static Handler1 getInstance(){
        return new Handler1();
    }
    @Override
    public void doSomething() {
        System.out.println("Handler1--->doSomething");
    }
}           
class Handler2 implements HandlerStrategy{

    private Handler2(){}

    static Handler2 getInstance(){
        return new Handler2();
    }
    @Override
    public void doSomething() {
        System.out.println("Handler2--->doSomething");
    }
}           
class Handler3 implements HandlerStrategy{

    private Handler3(){}

    static Handler3 getInstance(){
        return new Handler3();
    }
    @Override
    public void doSomething() {
        System.out.println("Handler3--->doSomething");
    }
}           

创建枚举类,EHandler,用于保存handler类型间的关系,代码如下:

public enum EHandler {

    Handler1Type(Handler1.getInstance()),
    Handler2Type(Handler2.getInstance()),
    Handler3Type(Handler3.getInstance());

    private HandlerStrategy handlerStrategy;

    public HandlerStrategy getHandlerStrategy(){
        return this.handlerStrategy;
    }
    EHandler(HandlerStrategy handlerStrategy){
        this.handlerStrategy = handlerStrategy;
    }

}           

创建HandlerHelper,用于对外调用使用,整个策略的包,只对外暴露HandlerHelper,调用者根本不需要知道包内部是如何实现的。

public final class HandlerHelper {

    private HandlerHelper(){}

    public static void doHandler(String type){
        EHandler.valueOf(type).getHandlerStrategy().doSomething();
    }
}           

验证程序:

public class Main {

    public static void main(String[] args) {
        HandlerHelper.doHandler("Handler1Type");
        HandlerHelper.doHandler("Handler2Type");
        HandlerHelper.doHandler("Handler3Type");
    }

}           

运行结果:

Handler1--->doSomething
Handler2--->doSomething
Handler3--->doSomething           

至此,完成了if...else的优化,不足的地方是新增handler的时候,还是需要维护枚举类。这个就要看具体的需求了,如果策略数量不是很多可以考虑采用这种方式;如果策略数量多,可以考虑采用反射。

五、复盘

今天主要和大家分享策略模式,顺便分享了一下优化代码的一些心得,并且演练了一下优化if...else的过程。今天的分享就到这里,欢迎各位小伙伴留言,共同进步。

继续阅读