How can I break up with you

Posted by threaders on Tue, 25 Feb 2020 10:07:21 +0100

The indissoluble distinction between if and else

In the actual programming, we always need a lot of logic judgment to help us to do things under various branches. In any case, we need if and else to help us solve it? The answer is not necessarily. For java, design pattern is to better encapsulate, decouple and cohere. For a complex system, it needs more support of design pattern. Otherwise, time-consuming and hard maintenance will only empty our body. Here are some ways for bloggers to reduce if and else cleverly.

Application scenario

In an Internet of vehicles project, various types of data packets will be received, including various data packets on the vehicle, such as driving motor data, engine data, vehicle position data (GPS), alarm data and so on. Therefore, we define an enumeration to represent different types of data packets:

enumeration

Message enumeration MsgTypeEnum

public enum MsgTypeEnum {
    /**
     * Drive motor data
     */
    DRIVE_MOTOR,
    /**
     * Engine data
     */
    ENGINE,
    /**
     * Vehicle location data
     */
    VEHICLE_POSITION,
    /**
     * Alarm data
     */
    ALARM
}

Message decoding class MsgDecoder

  public class MsgDecoder {
    /**
     * Decode message body
     * @param msgTypeEnum Message type
     * @return Specific types of agreements
     */
    public String decodeMsg(MsgTypeEnum msgTypeEnum) {
        //Start parsing and storing messages
        switch (msgTypeEnum) {
            case DRIVE_MOTOR:
                return "Drive motor data";
            case ENGINE:
                return "Engine data";
            case VEHICLE_POSITION:
                return "Vehicle location data";
            case ALARM:
                return "Alarm data";
            default:
                return null;
        }
    }

It can be seen that the code is still very neat (under self praise). Some people say that if and else are the same, yes, the same, there is no difference, and the code is also very refreshing, but this is just the four common decoding types. If there are more than 10 kinds of messages, is the code more "refreshing" when it is combined? We know that there are many parts on the car, and there are a lot of message types to describe them, so we should use enumeration method to improve.

Public interface class Decoder

public interface Decoder {
    /**
     * Decoding method
     */
    String decode();
}

Improved enumeration class MsgTypeEnum

public enum MsgTypeEnum implements Decoder {
    /**
     * Drive motor data
     */
    DRIVE_MOTOR{
        @Override
        public String decode() {
            return "Drive motor data";
        }
    },
    /**
     * Engine data
     */
    ENGINE{
        @Override
        public String decode() {
            return "Engine data";
        }
    },
    /**
     * Vehicle location data
     */
    VEHICLE_POSITION{
        @Override
        public String decode() {
            return "Vehicle location data";
        }
    },
    /**
     * Alarm data
     */
    ALARM{
        @Override
        public String decode() {
            return "Alarm data";
        }
    }
}

Improved enumeration class MsgDecoder

public class MsgDecoder {
    /**
     * Decode message body
     * @param msgTypeEnum Message type
     * @return Specific types of agreements
     */
    public String decodeMsg(MsgTypeEnum msgTypeEnum) {
        return MsgTypeEnum.valueOf(msgTypeEnum.toString()).decode();
    }
}

It can be seen that the improved specific business code implementation has reached the message enumeration class MsgTypeEnum, and the decoding class msgtoder only uses the valueOf method in the enumeration to "automatically" determine the type, but this does reduce the pressure of a class of msgtoder, and switch/case(if/else) also said to break up, but the message enumeration MsgTypeEnum will become bloated, if later How can a class contain all kinds of information? Therefore, enumeration method is not suitable, but it is recommended to use when there are few types. After all, enumeration is the "final class". It has different properties from other classes, and its use will be more efficient than other classes.

Since enumeration is still not suitable, what else can we do? Let's see how the factory design pattern transforms the above method:

Factory mode

Public interface class Decoder

public interface Decoder {
    /**
     * Decoding method
     */
    String decode();
}

Message enumeration MsgTypeEnum

public enum MsgTypeEnum {
    /**
     * Drive motor data
     */
    DRIVE_MOTOR,
    /**
     * Engine data
     */
    ENGINE,
    /**
     * Vehicle location data
     */
    VEHICLE_POSITION,
    /**
     * Alarm data
     */
    ALARM
}

Various message types (listed together for easy explanation, actually four classes)

//Alarm data
public class Alarm implements Decoder {
    @Override
    public String decode() {
        return "Alarm data";
    }
}
//Drive motor data
public class DeviceMotor implements Decoder {
    @Override
    public String decode() {
        return "Drive motor data";
    }
}
//Engine data
public class Engine implements Decoder {
    @Override
    public String decode() {
        return "Engine data";
    }
}
//Vehicle location data
public class VehiclePosition implements Decoder {
    @Override
    public String decode() {
        return "Vehicle location data";
    }
}

Message type factory

public class MsgTypeFactory {
    /**
     * Container for message types
     */
    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());
    }

    /**
     * Get the corresponding decoder
     * @param msgType Message type
     * @return Decoder
     */
    public static Decoder getDecoder(String msgType){
        return msgTypeMap.get(msgType);
    }
}

Improved enumeration class MsgDecoder

public class MsgDecoder {
    /**
     * Decode message body
     * @param msgTypeEnum Message type
     * @return Specific types of agreements
     */
    public String decodeMsg(MsgTypeEnum msgTypeEnum) {
        return MsgTypeFactory.getDecoder(msgTypeEnum.toString()).decode();
    }
}

It can be seen from the factory pattern that we can well define the problem to a specific class and break up with switch/case(if/else). However, it should be mentioned that the number of classes has increased, several decoding objects have been initialized and stored in the map. The initialized objects are relatively small objects, which are often used. New in advance can improve the decoding speed , instead of decoding temporary new temporarily, using design patterns will change a lot. Design patterns, on the other hand, are "breaking" to separate a large and heavy code into individual classes, but closely linked.

Factories are actually divided into three types (simple factory, factory method, abstract factory). The difference between them is that simple factories are assigned to a class to create the objects required by the client. Factory methods and abstract factories are both assigned to their specific subclasses to be implemented. However, the emphasis of factory methods and abstract factories is different. Factory methods focus on abstract methods and abstract factories Like factories focus on abstract factories (classes). In short, factories belong to The creation design mode is a design mode for the creation and use of objects. The factory mode in this paper is an upgraded version of the simple factory, a common simple factory mode. After entering the factory class, the corresponding object will be judged again according to the type. However, this paper loads the object in advance and returns the object directly after the judgment. For frequent decoding programs, it is With higher efficiency.

Strategy mode

The common interface class Decoder and various message types (listed together for convenience of explanation, actually four classes) code are the same as the factory and implement the same Decoder interface

DecodeContext unified scheduling class

public class DecodeContext {
    private Decoder decoder;

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

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

The client uses StrategyTest:

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

}

It can be seen that the policy pattern is to define a series of algorithms, encapsulate each algorithm and make them interchangeable. The policy pattern allows the algorithm to be independent of the client using it

summary

Careful readers may find that the policy mode does not reduce if and else. The client needs to generate new corresponding objects before entering each policy. So it still needs to judge if and else logically. Can we transfer the logic judgment to a class to further reduce their coupling degree (factory), in fact, the intermediate variety factory Pattern is the combination of policy and factory. It transfers the judgment to a class, but directly offsets if and else through the get method of map.

Published 8 original articles, won praise 5, visited 636
Private letter follow

Topics: Programming Java