基于接口而非实现编程
这句话实际表达的是,基于抽象而非实现编程,接口是自上而下的,要有抽象意识,越抽象,越灵活,可拓展,
定义接口时,命名要足够通用,不包含具体实现相关字眼。
如果在我们的业务场景中,某个功能只有一种实现方式,未来也不可能被其他实现方式替换,那我们就没有必要为其设计接口。
组合优于继承
继承表示的是类之间的is-a
关系,可以解决代码复用的问题。但是继承层次太深,过于复杂,
比如,鸟类是否会飞,是否会叫,是否会下蛋等各种行为,都会引出类似纲目科属种这样的继承关系,也将影响到代码的可维护性。
我们可以通过组合来优化这些问题。这里的组合是组合,接口,委托三种的统称。
之前我们说过,接口表示某种行为特性。但是接口只声明了方法,没有实现,那每一个实现类都要实现一遍相同逻辑的代码,也会导致代码重复。那如何解决呢?
我们可以针对接口再定义实现类,来表示拥有这种行为的能力,例如,Flyable
接口,其实现类可以定义为FlyAbility
,然后通过组合和委托来消除代码重复。
例如:
1 | public class Ostrich implements Tweetable, EggLayable {//鸵鸟 |
理论上,通过接口,委托和组合就可以完全替换继承,达到简化代码的效果,增加了可维护性。
但其并不完美,我们相应的增加了很多其他的类和接口,在实际开发中,还是要根据实际需求来选择继承,还是组合。
装饰者模式(decorator pattern)、策略模式(strategy pattern)、组合模式(composite pattern)等都使用了组合关系,而模板模式(template pattern)使用了继承关系。
领域驱动设计(DDD)
要讲这个之前先讲讲什么是贫血模型和充血模型。
贫血模型:只包含数据,不包含业务逻辑;
充血模型:数据和类都被封装到同一个类中。
领域驱动设计(DDD)是基于充血模型的, 主要用于如何解耦业务系统。划分业务模块,定义业务领域模型以及其交互。
我们所说的复杂系统,更看重业务的复杂度,将复杂度降低的方法则是分而治之。这样可以降低复杂度。
复杂要解决三个问题:规模问题,结构问题和需求变化问题。无论是技术复杂度或业务复杂度,只要能解决这三种复杂度问题就是好的方法。
基于充血模型的 DDD 开发模式跟基于贫血模型的传统开发模式相比,主要区别在 Service 层。DDD模型其实是将Model层做重。因为业务核心是技术无关的。传统MVC用于C/S模型,也依然是重Model层的。在基于充血模型的开发模式下,我们将部分原来在 Service 类中的业务逻辑移动到了一个充血的 Domain 领域模型中,让 Service 类的实现依赖这个 Domain 类。
在基于充血模型的 DDD 开发模式中,将业务逻辑移动到 Domain 中,Service 类就变得很薄。Service 主要负责于Repository交互,负责跨领域模型的业务聚合。负责非功能性及与第三方系统交互的工作。