开放封闭原则实践

275 阅读3分钟

开放封闭原则有两个特性:

1.面对扩展开放。可以以最小的代价支持需求变更。

2.面对修改封闭。需求的变更不会对原代码造成侵略。

这篇文就来记录一下关于开放封闭原则的实践。

使用场景

在业务中存在某一领域的实体会被其实多个实体引用的情况,在删除这一实体时需要校验是否有被引用,如果被引用则不可以被删除。

比如这种情况:在一个培训系统中,存在N个某个站点的老师,和N个这一站点的班级。那站点就是同时被老师与班级引用了,当在系统中需要删除站点的时候,就需要判断这个站点上是否还存在有效的老师和班级。

创建实体

/**
 * 站点实体
 */
public class Site{
    // 站点ID
    private Long siteId;
    // 站点名称
    private String siteName;
}

/**
 * 老师实体
 */
public class teacher{
    // 老师ID
    private Long teacherId;
    // 老师名称
    private String teacherName;
    // 所属站点
    private Long siteId;
}

/**
 * 班级实体
 */
public class classes{
    // 班级ID
    private Long classId;
    // 班级名称
    private String className;
    // 所属站点
    private Long siteId;
}

传统做法

在这种情况下的传统做法可能是这样的。在删除站点的接口,依次查询当前站点下的老师和班级,存在即不可删除。

创建查询方法

/**
 * 查询当前站点下的老师
 */
public class teacherManager{
    public Boolean checkExistTeacherBySite(Long siteId){
        // 根据站点查询老师 
        // select count(*) from teacher_table where site_id = siteId and status =0;
        Long count = 查询结果;
        return count > 0 ;
    }
}

/**
 * 查询当前站点下的班级
 */
public class classesManager{
    public Boolean checkExistClassesBySite(Long siteId){
        // 根据站点查询班级
        // select count(*) from class_table where site_id = siteId and status = 0;
        Long count = 查询结果;
        return count > 0 ;
    }
}

删除站点

public class siteManager{
    public Boolean deleteSite(Long siteId){
        if (!checkExistTeacherBySite && !checkExistClassesBySite){
            // 一般为逻辑删除,这里为了示意。
            return deleteByPrimaryKey(siteId);
        }else {
            log.warn("站点已被引用,不能删除");
            return false;
        }
    }
}

缺点

这种方法的弊端相信很容易就看出来了,就是不利于扩展,如果以后又增加例如实操训练、考试任务等需求需要关联站点,那就还需要更改删除站点的这一方法。不光如此,站点这一实体的特性属于基础信息,在实际项目中可能存在关联很多实体,那造成的结果就会if中的判断一长串,非常不美观。

这种做法既不能保持代码的整洁,也不满足java的面向对象原则。

优化做法

创建实体的部分是一样的,区别在于查找被引用的实体。

创建接口

public interface SiteReferceChecker{
    /**
      * 查看站点是否被引用
      */
    Boolean checkSiteUsed(Long siteId);
}

引用实现

/**
 * 在老师实体中查看当前站点是否被引用
 */
public class teacherManager implements SiteReferceChecker{
    public Boolean checkSiteUsed(Long siteId){
        // 根据站点查询老师 
        // select count(*) from teacher_table where site_id = siteId and status =0;
        Long count = 查询结果;
        return count > 0 ;
    }
}

/**
 * 在班级实体中查看当前站点是否被引用
 */
public class classesManager implements SiteReferceChecker{
    public Boolean checkSiteUsed(Long siteId){
        // 根据站点查询班级
        // select count(*) from class_table where site_id = siteId and status = 0;
        Long count = 查询结果;
        return count > 0 ;
    }
}

删除站点

public class siteManager{
    // 通过自动注入找到所有被引用的点
    @Resouse
    private List<SiteReferceChecker> checkers;
    
    public Boolean deleteSite(Long siteId){
        if (isSiteUsed(siteId)){
            log.warn("站点已被引用,不能删除");
            return false;
        }
        // 一般为逻辑删除,这里为了示意。
        return deleteByPrimaryKey(siteId);
    }
    
   
    private isSiteUsed(Long siteId){
        if (CollectionUtils.isEmpty(checkers)){
            return false;
        }
        // 循环所有引用点,查看是否被引用
        for (SiteReferceChecker checker : checkers) {
         if (checker.checkSiteUsed(siteId)) {
         	   return true;
         	}
        }
        return false;
    }
}

总结

在根据开放封闭原则优化后的实现的优化是显而易见的,当有需求变化,例如增加了考试关联站点时,那只需要在考试的类中实现SiteReferceChecker的checkSiteUsed方法,就可以了,站点中的删除方法会自动查找所有引用点,完成校验。