0%

设计原则

贫血模型

贫血模式主要就是把数据和行为分隔开,会破坏面向对象中的封装特性,是一种面向过程的编程风格

比如现在的Web项目一般都是基于MVC三层架构来设计的,项目分为Repository层、Service层、Controller层。

Entity和Repository负责数据访问层、BO和Service负责业务逻辑层、VO和Controller负责接口层。

但是实际上,BO通常是一个纯粹的数据结构,只包含数据,不包含任何业务逻辑,逻辑主要集中在Service层,然后通过Service来操作BO,这种被称为 贫血模型。还有Entity和VO也都是基于贫血模型设计的。

充血模型

充血模型是一种基于的DDD开发模式,贫血模型是把数据和业务逻辑分割到不同的类中,充血模型是相反的,是把数据和业务逻辑封装到同一个类中,这种模型可以满足面向对象的封装特性

实际上,基于充血模型的DDD设计模式,也还是按照MVC三层架构设计的。Controller负责暴露接口,Service负责业务逻辑,Repository负责数据存取。只不过在充血模型中,Service层包含Service和Domain两个部分,Domain相当于实体类,只不过它既包含数据,也包含业务逻辑。Service作为Repository层和Domain之间的连接层,用来获取数据库中的数据,然后再转换成Domain,用Domain来完成业务逻辑,然后调用Repository将数据写入到数据库

Entity被设计成贫血模型,虽然违背了面向对象的封装特性,但Entity的生命周期是有限的,一般被传递到Service层就会转换成BO或者Domain来完成后面的逻辑,所以Entity通常不会被任意修改。

VO主要是作为接口传输数据的载体,从功能上来讲,它不应该包含业务逻辑,只包含数据,所以设计成贫血模型也是比较合理的。

贫血模型的开发模式,比较适合业务简单的系统,充血模型更适合业务复杂的场景。

单一职责原则(SRP)

单一职责的意思是:一个类或者一个模块只负责完成一个功能

也就是说,要设计粒度小、功能单一的类,如果一个类包含了多个完全不相干的功能,这种类的职责就不够单一,应该把他拆分成多个简单的类。

开闭原则(OCP)

开闭原则的意思是:添加一个新的功能,应该是在已有代码的基础上拓展代码,而不是修改已有代码

开闭原则的关键是预留拓展点,需要针对业务尽量识别出所有拓展点,不过也没有必要为一些遥远的、不一定发生的需求提前买单,做过度设计。合理的做法是,对一些短期内、比较确定的需求做一些拓展性的设计。

通常拓展性和可读性是相互冲突的,拓展性高的代码可能会复杂很多,有时候就需要在可读性和拓展性之间做权衡。

里氏替换原则(LSP)

里氏替换原则的意思是:在继承关系中子类要保证替换父类的时候,不改变原来的程序逻辑和正确性

比如在父类中运行出错的时候返回null,数据为空返回空集合。子类重写函数之后,变成了运行出错返回空集合,数据为空返回null。这种设计就违背了里氏替换原则。

再比如父类中只会抛出IOException异常,如果子类再抛出其它异常,也会违背里氏替换原则。

接口隔离原则(ISP)

接口隔离原则的意思是:不应该强迫调用者依赖它不需要的接口如果部分接口只被一部分调用者使用,那就可以把这部分接口拆分出来,单独给对应的调用者使用,而不是强迫其它调用者也依赖这部分不会被调用的接口

比如有一个管理用户的UserService,现在需要提供一个删除用户的方法,如果直接在UserService中添加删除用户的方法,那所有依赖UserService的类都可以调用这个接口,就有可能导致误删用户。

通过接口隔离原则,可以把删除相关的操作单独放到另外一个DeleteUserService中,然后让某些特定的类来依赖这个Service。

依赖反转原则(DIP)

依赖反转原则的意思是:外层模块不要直接依赖底层模块,应该通过抽象层来实现底层模块的依赖。这样就可以通过抽象层动态切换底层模块的实现细节。

比如Java中的反射调用,就是通过委派层动态切换本地实现动态实现