设计原则理解记录(一)

设计模式与设计原则都是为了提高代码复用性,维护性,可读性。但设计模式往往只实现一种原则或多种原则的整合,也就是说设计原则可能有多种设计模式实现。需要注意设计原则再实际开发中并不能做到完全遵守,往往是打破一些原则,遵守一些原则,来实现设计的合理性。所以看情况分析

简述

本章主要讲解七大设计原则中容易理解的三个原则,他们是单一职责原则、开闭原则、接口隔离原则。

单一职责原则

原则的名字就是这个原则想表达的观点,这个原则应该是最好理解的原则了,但并不意味着它实现起来很简单。对我们而言在实现这个原则最关心的应该是”范围“,不能单纯的只把颗粒度拉到最细,你需要明白最细的颗粒度在你的这个“范围”重不重要能不能看到?

举几个例子:如果你看到的范围是一家小科技公司,那么从人员角度产品,测试,研发,运维,财务,客服,老板就是一个个单一职责。如果你看到的范围是一个 分布式架构产品,那么从服务角度支付,订单,商城,客户,广告等就是一个个单一职责。如果你看到范围是前端大屏接口,那每个小模块都应该是一个单独的接口,每个接口提供一个模块或者图形所需的数据。如果你看到的范围是一个应用,那 util、config、 dao、service、controller等目录个个都是单一的职责,如果你看到的范围是 util 目录,那 StringUtil、FileUtil、HttpUtil、SftpUtil 等每个类都是一个个单一职责。如果你看到范围是 StringUtil 类,那 toString()、length()、indexOf() 等方法都是一个个单一职责。

通过上面例子,我们就会发现单一职责实现起来好像并不简单,很多时候我们能将各种职责划分到最细,但却不知道应该站在哪个“范围”去看待它,又或者因为越拆越细导致东西越来越多,短期单人维护不方便。但好处是长期或者多人维护的时候耗时是比较稳定的。

代码反例 (范围: 类下的方法):

public class ServiceImpl implements Service{
    @Autowired
    private RecordDao recordDao;
    public List<Record> listPage(RecordBo recordBo){
        // 组装查询条件
        RecordCriteria criteria = new RecordCriteria();
        if(StringUtils.isNotBlank(recordBo.getId())){
            criteria.andIdEqualTo(recordBo.getId());
        }
        if (StringUtils.isNotBlank(recordBo.getAssetOrgNo())){
            criteria.andAssetOrgNoEqualTo(recordBo.getAssetOrgNo());
        }
        if (StringUtils.isNotBlank(recordBo.getProjectNo())){
            criteria.andProjectNoEqualTo(recordBo.getProjectNo());
        }
        // 查询记录
         return recordDao.selectByCriteria(criteria);
    }
}

代码正例

public class ServiceImpl implements Service{
    @Autowired
    private RecordDao recordDao;
    public List<Record> listPage(RecordBo recordBo){
        // 组装查询条件
        RecordCriteria criteria = this.buildCriteria(recordBo)
        // 查询记录
         return recordDao.selectByCriteria(criteria);
    }
    
    // 组装查询条件
    public RecordCriteria buildCriteria(RecordBo recordBo){
    	RecordCriteria criteria = new RecordCriteria();
        if(StringUtils.isNotBlank(recordBo.getId())){
            criteria.andIdEqualTo(recordBo.getId());
        }
        if (StringUtils.isNotBlank(recordBo.getAssetOrgNo())){
            criteria.andAssetOrgNoEqualTo(recordBo.getAssetOrgNo());
        }
        if (StringUtils.isNotBlank(recordBo.getProjectNo())){
            criteria.andProjectNoEqualTo(recordBo.getProjectNo());
        }
        return criteria;
    }
}

设计模式:所有设计模式里里外外都有这原则的味道

总结:要学会划分职责范围,避免重复造轮子,避免多个职责耦合在一起会造成职责的相互影响,导致原则在开发中被打破。

开闭原则

这个原则的观点是一个类应该对拓展开放,对修改关闭,要求是类的行为可拓展,而且是在不修改现有代码的前提下进行拓展,这意味着类要合理的抽象分离出变化的与不变的的部分,为变化部分预留下可拓展的方式,比如钩子方法,或动态组合对象等。像 Spring Bean 的初始化前后这就很符合开闭原则。

开闭原则的遵循一般可分为两类人,类的创造者,从头到尾都是自己编写的且类变动了你知道影响到哪里,那你可以修改源代码。第二种人就是后期接手的,那就应该尽量避免修改别人的代码,对原有类通过继承,包装,使用原有类提供的钩子等技术拓展实现功能。

所以在开发过程中,第一类人,类的创造者在开发这个类的时候就应该尽量多想,尝试站在第二类人看待类的拓展性,为后来者提供拓展功能的入口,比如将不变的部分弄成抽象类,变的部分是具体实现类的行为,又比如通过责任链设计的方式,将每个具体行为解耦,降低影响,通过排序责任链,使别人更好的开发功能兼容。

代码反例:(汽车价格打八折活动)

public class Car{
	private double price;
	public double getPrice(){
		// 这是反例,违背开闭原则,因为这里修改了源代码
		return price*0.8;
	}
}

代码正例

public class ActivitCar extends Car{
	
	@Override
	public double getPrice(){
		// 这是反例,违背开闭原则,因为这里修改了源代码
		return super.getPrice()*0.8;
	}
}

设计模式:模板方法模式、策略模式、状态模式、观察者模式、责任链模式等都能在不改变原有类结构上灵活拓展功能,但他们之间注重变不不变的划分不一样。

总结:开闭原则最难点在于如何区分出变化的与不变的地方,并为变化的地方留下入口,为以后的开发提供便利,提高系统的成长性,不易于产生臃肿的代码。

接口隔离原则

接口隔离原则的含义就是:接口不应该定义太大,应该切分为更细的接口,子类不应该依赖它不需要的接口。突出的是那种隔离性,比如继承也可以遵从这种思路原则,多层次继承剔除不需要的臃肿的实现逻辑。

像高内聚,低耦合是我们熟悉的设计思路,而在代码开发中,低耦合低到什么程度就需要考虑单一职责原则,它和接口隔离原则息息相关,甚至可以说接口隔离原则底层原则也是单一职责原则。

代码反例

public interface Animal{
    void eat();
    void swim();
    void fly();
}

代码正例

public interface Eat{
    void eat();
}
public interface Swim{
    void swim();
}
public interface Fly{
    void fly();
}

设计模式:没有专门一定要使用它的,但在设计模式使用的时候可以最好考虑下这个原则,

总结:这个原则也是非常容易理解的,但还是需要考虑项目实际情况,在项目越来越大的时候,应该对有些功能尽早的遵循这个原则。以免代码越来越难以把控。

总结

想为自己写一篇对设计原则的理解,但写的过程总是让人难受,明明网上有非常标准的答案了,但我还是想将这些答案用自己的理解表述出来,但就是这三个最容易表述的原则,我表述的都还是很艰难。希望通过这次思考能让我对设计原则理解更加深刻。