改造屎山,造福后人

199 阅读2分钟

场景一、各处取值判断逻辑不统一,精度不统一,最终结果不一样导致的bug

获取一个人的label属性, 其属性具有时效性

之前:

// 实体类
@Data
public class Person {
    private Integer label;

    private Date labelLimit;
}

public class ServiceTest {
    // 获取 Person的label属性, 存在时效性, 过期则不展示
    public static void main(String[] args) {
        Person person = new Person();
        person.setLabel(2);
        person.setLabelLimit(new Date());

        // `代码一`
        // 使用Date 类判断是否有效,精确到时分秒
        Integer rawLabel1 = null;
        if (new Date().before(person.getLabelLimit())) {
            rawLabel = person.getLabel();
        }
        
        // `代码二`
        // 使用LocalDate判断是否有效,可以精确到日
        Integer rawLabel2 = null;
        LocalDate limit = person.getLabelLimit().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        if (LocalDate.now().isBefore(limit)) {
            rawLabel2 = person.getLabel();
        }
        // 等等等等
        // 代码一、二 出出现在多处使用 甚至还会有其他写法
            
    }
}

进行规整统一:

// label属性 访问接口
public interface LabelAccess {
    // label属性是否还有效
    boolean valid();

    // 若属性有效返回属性
    Integer getLabel();
}
// person包装类, 实现LabelAccess接口
public class PersonWrapper implements LabelAccess {
    private Person person;
    
    // 需要传入person实例构造函数
    public PersonWrapper(Person person) {
        this.person = person;
    }

    @Override
    public boolean valid() {
        return new Date().before(person.getLabelLimit());
    }

    @Override
    public Integer getLabel() {
        if (!valid()) {
            return null;
        }

        return person.getLabel();
    }
}

public class ServiceTest {
    public static void main(String[] args) {
        Person person = new Person();
        person.setLabel(2);
        person.setLabelLimit(new Date());
        
        // 改造后
        LabelAccess labelAccess = new PersonWrapper(person);
        Integer label = labelAccess.getLabel();
    }
}

场景二、某个方法多处调用,但是每个场景前戏略微有所差别

调用方控制校验逻辑, 又想要代码复用

public class FlowService {
    // 根据id 创建数据并保存到数据库
    static void createStep(Long id) {
        // todo 保存数据
    }
}

public class ServiceTest {
    
    // 场景一
    public static void call1() {
        // 需要进行 “校验一”
        boolean f1 = true;
        if (!f1) {
            throw new RuntimeException("校验一不通过");
        }

        FlowService.createStep(1L);
    }

    // 场景二
    public static void call2() {
        // 需要进行 “校验二”
        boolean f2 = true;
        if (!f2) {
            throw new RuntimeException("校验二不通过");
        }

        FlowService.createStep(2L);
    }

    // 场景三
    public static void call3() {
         // 先进行 “校验一”
        boolean f1 = true;
        if (!f1) {
            throw new RuntimeException("校验一不通过");
        }

         // 再进行 “校验二”
        boolean f2 = true;
        if (!f2) {
            throw new RuntimeException("校验二不通过");
        }

        FlowService.createStep(3L);
    }
}

弊端在于校验一二逻辑如果多次使用需要多次编写,容易出错,且不好复用,后续需求变动也难以应对,所以 使用策略模式改造

// 抽象基类
public abstract class CreateStepChecker {
    public Long id;

    public void setId(Long id) {
        this.id = id;
    };

    abstract public void check();
}
// “校验一” 的逻辑, 只需要重写check方法
public class CreateStepCheckerOne extends CreateStepChecker {
    @Override
    public void check() {
        System.out.println(id);
        // 校验一
        boolean f1 = true;
        if (!f1) {
            throw new RuntimeException("校验一不通过");
        }
    }
}
// “校验二” 的逻辑, 只需要重写check方法
public class CreateStepCheckerTwo extends CreateStepChecker {
    @Override
    public void check() {
        System.out.println(id);

        // 校验二
        boolean f2 = true;
        if (!f2) {
            throw new RuntimeException("校验二不通过");
        }
    }
}
// 略微修改createStep方法, 允许传入多个CreateStepChecker
public class FlowService {

    static void createStep(Long id, CreateStepChecker... checkers) {
        // 进行校验
        for (CreateStepChecker checker : checkers) {
            checker.setId(id);
            checker.check();
        }
        
        // todo 保存数据
    }
}

public class ServiceTest {
    
    // 调用方根据实际需要传入 CreateStepChecker 的实现类
    public static void call1() {


        FlowService.createStep(1L, new CreateStepCheckerOne());
    }

    public static void call2() {

        FlowService.createStep(2L, new CreateStepCheckerTwo());
    }

    public static void call3() {

        FlowService.createStep(3L, new CreateStepCheckerOne(), new CreateStepCheckerTwo());
    }
}