设计模式|if/else我该如何和你说分手

if和else的难解难分

       实际的编程中,我们总是需要很多的逻辑判断帮助我们去做各种分支下的事情,无论怎么样都需要if和else帮我们解决嘛?答案是不一定的,对于java而言,设计模式就是为了更好的封装、解耦、内聚,对于一个复杂的系统,更加需要有设计模式的支持,不然费时费劲的维护,只会把我们的身体淘空,以下是博主总结的一些巧妙减少if和else的方法。

应用场景
在这里插入图片描述

       一个车联网项目中,会接收到各种类型不同的数据包,有关于车上的各种数据,比如驱动电机数据、发动机数据、车辆位置数据(GPS)、报警数据等数据包,为此我们定义了一个枚举去表示不同类型的数据包:

枚举

       消息枚举MsgTypeEnum

public enum MsgTypeEnum {
    /**
     * 驱动电机数据
     */
    DRIVE_MOTOR,
    /**
     * 发动机数据
     */
    ENGINE,
    /**
     * 车辆位置数据
     */
    VEHICLE_POSITION,
    /**
     * 报警数据
     */
    ALARM
}

       消息解码类MsgDecoder

  public class MsgDecoder {
    /**
     * 解码消息体
     * @param msgTypeEnum 消息类型
     * @return 协议具体类型
     */
    public String decodeMsg(MsgTypeEnum msgTypeEnum) {
        //开始解析并存储消息
        switch (msgTypeEnum) {
            case DRIVE_MOTOR:
                return "驱动电机数据";
            case ENGINE:
                return "发动机数据";
            case VEHICLE_POSITION:
                return "车辆位置数据";
            case ALARM:
                return "报警数据";
            default:
                return null;
        }
    }

       可以看到代码还是很整洁哈(自我表扬下),有人说换成if和else也是一样的,是的,一样的,并没有什么不同,代码也很清爽,但是这只是普通的四种解码类型,若他的消息种类多达10种以上呢,代码罗在一起,是不是很更“清爽”了?我们知道车上零部件很多,完全存在大量的消息种类来描述他们,为此我们该用枚举的方法改进。

       公共接口类 Decoder

public interface Decoder {
    /**
     * 解码方法
     */
    String decode();
}

       改进后的枚举类MsgTypeEnum

public enum MsgTypeEnum implements Decoder {
    /**
     * 驱动电机数据
     */
    DRIVE_MOTOR{
        @Override
        public String decode() {
            return "驱动电机数据";
        }
    },
    /**
     * 发动机数据
     */
    ENGINE{
        @Override
        public String decode() {
            return "发动机数据";
        }
    },
    /**
     * 车辆位置数据
     */
    VEHICLE_POSITION{
        @Override
        public String decode() {
            return "车辆位置数据";
        }
    },
    /**
     * 报警数据
     */
    ALARM{
        @Override
        public String decode() {
            return "报警数据";
        }
    }
}

       改进后的枚举类MsgDecoder

public class MsgDecoder {
    /**
     * 解码消息体
     * @param msgTypeEnum 消息类型
     * @return 协议具体类型
     */
    public String decodeMsg(MsgTypeEnum msgTypeEnum) {
        return MsgTypeEnum.valueOf(msgTypeEnum.toString()).decode();
    }
}

       可以看到改进后的具体业务代码实现都到了消息枚举类MsgTypeEnum,而解码类MsgDecoder只用了枚举里的valueOf方法就“自动”判断了是那种类型,但是这样确实减少了MsgDecoder一个类的压力,switch/case(if/else)也说分手了,但是消息枚举MsgTypeEnum会变得臃肿,若后期消息解码种类增多,一个类何以海纳百川?所以枚举方法不适合,但种类少的判断,还是推荐使用的,毕竟枚举是“最终类”,拥有和其他类不一样的性质,他的使用会比其他类更高效。

       既然枚举依旧不适合,那么还有什么办法呢,下面我们来看工厂设计模式是如何改造上面方法的:

工厂模式

       公共接口类 Decoder

public interface Decoder {
    /**
     * 解码方法
     */
    String decode();
}

       消息枚举MsgTypeEnum

public enum MsgTypeEnum {
    /**
     * 驱动电机数据
     */
    DRIVE_MOTOR,
    /**
     * 发动机数据
     */
    ENGINE,
    /**
     * 车辆位置数据
     */
    VEHICLE_POSITION,
    /**
     * 报警数据
     */
    ALARM
}

       各种消息种类(为了便于说明,就罗列到一起,实际是四个类)

//报警数据
public class Alarm implements Decoder {
    @Override
    public String decode() {
        return "报警数据";
    }
}
//驱动电机数据
public class DeviceMotor implements Decoder {
    @Override
    public String decode() {
        return "驱动电机数据";
    }
}
//发动机数据
public class Engine implements Decoder {
    @Override
    public String decode() {
        return "发动机数据";
    }
}
//车辆位置数据
public class VehiclePosition implements Decoder {
    @Override
    public String decode() {
        return "车辆位置数据";
    }
}

       消息类型工厂

public class MsgTypeFactory {
    /**
     * 消息种类的容器
     */
    private final static Map<String,Decoder> msgTypeMap = new HashMap<>(4);
    static {
        msgTypeMap.put(MsgTypeEnum.VEHICLE_POSITION.toString(),new VehiclePosition());
        msgTypeMap.put(MsgTypeEnum.ALARM.toString(),new Alarm());
        msgTypeMap.put(MsgTypeEnum.DRIVE_MOTOR.toString(),new DeviceMotor());
        msgTypeMap.put(MsgTypeEnum.ENGINE.toString(),new Engine());
    }

    /**
     * 获取对应的解码器
     * @param msgType 消息类型
     * @return 解码器
     */
    public static Decoder getDecoder(String msgType){
        return msgTypeMap.get(msgType);
    }
}

       改进后的枚举类MsgDecoder

public class MsgDecoder {
    /**
     * 解码消息体
     * @param msgTypeEnum 消息类型
     * @return 协议具体类型
     */
    public String decodeMsg(MsgTypeEnum msgTypeEnum) {
        return MsgTypeFactory.getDecoder(msgTypeEnum.toString()).decode();
    }
}

       从工厂模式可以看出,我们可以很好的把问题规定到特定的类,也和switch/case(if/else)说分手了,但是要提一嘴的就是,类增多了,初始化了几个解码对象并存储在map里面,初始化的几个对象都是相对较小的对象,也是经常要用的,提前new可以提高解码速度,不用临时解码临时new,要用设计模式,相对的就是会变多,设计模式从另一个方面来说,就是“拆”,把一个大而重的代码拆分成一个个单独的类,但又紧密的联系在一起。

       工厂其实分为三种(简单工厂,工厂方法,抽象工厂)他们的区别是简单工厂是交由一个类去创建客户端所要的对象,工厂方法和抽象工厂都是交由他们具体要实现的子类是创建对象,但工厂方法和抽象工厂的重点由不同,工厂方法侧重于抽象方法,抽象工厂侧重于抽象工厂(类)。工厂简而言之都是属于 创建型的设计模式,属于对对象的创建和使用上的一种设计模式,本文的工厂模式属于简单工厂的升级版,普通的简单工厂模式,进入工厂类后都会依据类型判断再new对应的对象,但本文是提前就加载好的,判断好后就直接返回该对象,对频繁的解码程序来说,是有较高的效率提升的。

策略模式

       公共接口类 Decoder、各种消息种类(为了便于说明,就罗列到一起,实际是四个类) 代码都与工厂相同,也是实现了同样的Decodor接口

       DecodeContext统一调度类

public class DecodeContext {
    private Decoder decoder;

    public DecodeContext(Decoder decoder) {
        this.decoder = decoder;
    }

    public String decode(){
        return decoder.decode();
    }
}

       客户端使用StrategyTest:

public class StrategyTest {
    public static  void main(String []args){
        DecodeContext decodeContext = new DecodeContext(new VehiclePosition());
        System.out.println(decodeContext.decode());
    }

}

       可以看出策略模式是定义一系列算法,封装每个算法,并使它们可以互换。策略模式可以让算法独立于使用它的客户端

总结

       细心的读者可能会发现,其实策略模式没有减少if和else,需要客户端在进入每个策略前就要new出对应的对象,那么还是需要逻辑判断if和else的,那么我们可不可以把逻辑判断转移到一个类上呢,进一步降低他们的耦合度(工厂)呢,其实中间的变种工厂模式就是策略+工厂的结合,他把判断转移到一个类上,但是通过map的get方法直接抵消了if和else。

发布了8 篇原创文章 · 获赞 5 · 访问量 693

猜你喜欢

转载自blog.csdn.net/xianghe_qqq/article/details/104446840