1、案例引入

例如当前有这么一段大量if/else的业务逻辑处理代码,如下所示: 

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class DemoApplicationTests {

    @Test
    void noDesign(){
        String name="张三";
        if(name.equals("张三")){
            // 张三的业务逻辑部分
            System.out.println("张三完成任务");
        }else if(name.equals("李四")){
            // 李四的业务逻辑部分
            System.out.println("李四完成任务");
        }else if(name.equals("王五")){
            // 王五的业务逻辑部分
            System.out.println("王五完成任务");
        }
    }
}

在上述代码里面我们采用类似于下面的这两行代码去代替真实的业务逻辑:

// 张三的业务逻辑部分
System.out.println("张三完成任务");

上述这些代码当if/else的分支处理逻辑逐渐增多时,该区域就会看起来比较臃肿,代码不美观,而且也不利于后期维护,对于这种情况我们可以采用策略模式+工厂模式+模板方法模式对其进行优化。

2、使用策略模式进行优化

首先我们使用策略模式将if里面的这部分算法逻辑抽取出来, 抽取为一个个策略类:

/**
 * 策略模式的总接口
 */
public interface Handler{

    public void doThings() ;
}

每一个具体的策略类(ZhangSanHandler、LiSiHandler、WangWuHandler)中的doThings方法表示策略方法: 

public class ZhangSanHandler implements Handler{

    @Override
    public void doThings() {
        // 张三的业务逻辑部分
        System.out.println("张三完成任务");
    }
}

public class LiSiHandler implements Handler{

    @Override
    public void doThings() {
        // 李四的业务逻辑部分
        System.out.println("李四完成任务");
    }
}

public class WangWuHandler implements Handler{

    @Override
    public void doThings() {
        // 王五的业务逻辑部分
        System.out.println("王五完成任务");
    }
}

接着我们可以使用抽取出来的策略方法去代替if里面之前的代码逻辑,可以把if里面的多行代码变成一行代码:

import com.bc.work.design.LiSiHandler;
import com.bc.work.design.WangWuHandler;
import com.bc.work.design.ZhangSanHandler;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class DemoApplicationTests {

    @Test
    void noDesign(){
        String name="张三";
        if(name.equals("张三")){
            new ZhangSanHandler().doThings();
        }else if(name.equals("李四")){
            new LiSiHandler().doThings();
        }else if(name.equals("王五")){
            new WangWuHandler().doThings();
        }
    }
}

3、使用工厂模式进行优化

在引入了策略模式以后,可以看到还是会有if/else语句,当策略类逐渐增多时,这个if/else语句会影响我们代码的美观,还有代码的可读性,这个时候我们就可以引入工厂模式来消除这个if/else语句。

首先构建一个可以注册策略类和获取策略类的工厂,如下所示: 

import cn.hutool.core.util.StrUtil;
import java.util.HashMap;
import java.util.Map;

public class Factory {

    private static Map<String, Handler> strategyMap = new HashMap<>();

    public static Handler getInvokeStrategy(String str) {
        return strategyMap.get(str);
    }

    public static void register(String str, Handler handler) {
        if (StrUtil.isBlank(str)|| null == handler) {
            return;
        }
        strategyMap.put(str, handler);
    }
}

 接着我们再修改策略类的总接口Handler,使其继承InitializingBean

import org.springframework.beans.factory.InitializingBean;

public interface Handler extends InitializingBean {

    public void doThings() ;
}

然后我们的策略类也是需要进行修改,每个策略类都需要实现afterPropertiesSet这个方法,同时在类上添加@Component这个注解表示将自己交由Spring容器去管理,这样在加载策略类的时候才会将自己注册到strategyMap中。 

import org.springframework.stereotype.Component;

@Component
public class ZhangSanHandler implements Handler{

    @Override
    public void doThings() {
        // 张三的业务逻辑部分
        System.out.println("张三完成任务");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Factory.register("张三",this);
    }
}

import org.springframework.stereotype.Component;

@Component
public class LiSiHandler implements Handler{

    @Override
    public void doThings() {
        // 李四的业务逻辑部分
        System.out.println("李四完成任务");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Factory.register("李四",this);
    }
}

import org.springframework.stereotype.Component;

@Component
public class WangWuHandler implements Handler{

    @Override
    public void doThings() {
        // 王五的业务逻辑部分
        System.out.println("王五完成任务");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Factory.register("王五",this);
    }
}

此时我们就能使用上述所创建的工厂类来消除多个if/else语句:

import com.bc.work.design.*;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class DemoApplicationTests {

    @Test
    void noDesign(){
        String name="张三";
        Handler invokeStrategy=Factory.getInvokeStrategy(name);
        invokeStrategy.doThings();
    }
}

4、使用模板方法进行优化

上述业务中张三、李四、王五执行的是同一种类型的业务逻辑,假设现在张三执行的业务逻辑B,李四和王五执行的是业务逻辑A,业务逻辑A和业务逻辑B是两种业务逻辑:

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class DemoApplicationTests {

    @Test
    String noDesign(){
        String name="张三";
        if(name.equals("张三")){
            // 业务逻辑B
            return "张三完成任务B";
        }else if(name.equals("李四")){
            // 业务逻辑A
            System.out.println("李四完成任务A");
        }else if(name.equals("王五")){
            // 业务逻辑A
            System.out.println("王五完成任务A");
        }
        return "end";
    }
}

那么如果按照前面的套路的话我们在策略类的总接口中需要创建两个抽象方法:

import org.springframework.beans.factory.InitializingBean;


public interface Handler extends InitializingBean {

    public void A(String name);

    public String B(String name);
}

接着不管每一个策略类是否都需要去执行这两个方法,在策略类中都要实现这两个抽象方法。我们知道有的策略类只需要执行其中的一个方法就可以,不需要执行的方法在策略类中进行了实现,这样就会出现空白代码块,代码看起来不够美观,因此上面这种方案并不是非常好。

像上面这个例子这样,如果我们出现了两种业务逻辑,它的业务逻辑大体上都不相同的话,我们就可以考虑使用模板方法这种设计模式。 

 我们将之前的策略类的总接口修改为一个抽象类:

import org. springframework.beans.factory.InitializingBean ;

public abstract class AbstractHandler implements InitializingBean{

    public void A(String name){
        throw new UnsupportedOperationException();
    }

    public String B(String name) {
        throw new UnsupportedOperationException();
    }
}

可以看到抽象类中的两个方法体中都是抛出一个UnsupportedOperationException异常,这里抛出异常的目的就是为了告诉这个调用者,如果子类没有进行重写A方法,则子类是不支持A方法。

各个策略类继承AbstractHandler抽象类,并选择性的去重写父类里面方法(引入了模板设计模式之后了,不同的子类就可以去实现不同的方法):

import org.springframework.stereotype.Component;

@Component
public class ZhangSanHandler extends AbstractHandler{

    @Override
    public String B(String name) {
        // 业务逻辑B
        return "张三完成任务B";
    }

    @Override
    public void afterPropertiesSet() {
        Factory.register("张三",this);
    }
}

import org.springframework.stereotype.Component;

@Component
public class LiSiHandler extends AbstractHandler{

    @Override
    public void A(String name) {
        // 业务逻辑A
        System.out.println("李四完成任务");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Factory.register("李四",this);
    }
}

import org.springframework.stereotype.Component;

@Component
public class WangWuHandler extends AbstractHandler{

    @Override
    public void A(String name) {
        // 业务逻辑A
        System.out.println("王五完成任务");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Factory.register("王五",this);
    }
}

策略工厂类也需要进行相应的改动,如下所示: 

import cn.hutool.core.util.StrUtil;
import java.util.HashMap;
import java.util.Map;

public class Factory {

    private static Map<String, AbstractHandler> strategyMap = new HashMap<>();

    public static AbstractHandler getInvokeStrategy(String str) {
        return strategyMap.get(str);
    }

    public static void register(String str, AbstractHandler handler) {
        if (StrUtil.isBlank(str)|| null == handler) {
            return;
        }
        strategyMap.put(str, handler);
    }
}

 最后改造后的业务逻辑处理代码如下所示:

import com.bc.work.design.AbstractHandler;
import com.bc.work.design.Factory;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class DemoApplicationTests {

    @Test
    String noDesign(){
        String name="张三";
        AbstractHandler strategy = Factory.getInvokeStrategy(name);
        if(name.equals("张三")){
            return strategy.B(name);
        }else if(name.equals("李四")){
            strategy.A(name);
        }else if(name.equals("王五")){
            strategy.A(name);
        }
        return "end";
    }
}

总结:设计模式是一种思想,它并不是一成不变的,我们可以根据真实的业务场景去改变这个设计模式,使用时需要灵活变通。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐