设计原则

Posted by Kerwen Blog on January 12, 2017

单一职责原则

就一个类而言, 应该仅有一个引起它变化的原因。
如果一个类承担的职责过多, 就等于把这些职责耦合在一起, 一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。 这种耦合会导致脆弱的设计, 当变化发生时, 设计会遭受意想不到的破坏。

开放-封闭原则

软件实体(类、模块、函数等等) 应该可以扩展, 但是不可修改。 即对于扩展是开放的, 对于更改是封闭的。

依赖倒转原则

高层模块不应该依赖底层模块。 两个都应该依赖抽象。

面向过程的开发时, 为了使得常用代码可以复用, 一般都会把这些常用代码写成许许多多函数的程序库, 这样我们在做新项目时, 去调用这些低层的函数就可以了。 比如我们做的项目大多要访问数据库, 所以我们就把访问数据库的代码写成了函数, 每次做新项目时就去调用这些函数。 这也就叫做高层模块依赖低层模块。
如果我们要做一个新的项目, 发现业务逻辑的高层模块都是一样的, 但客户却希望使用不同的数据库或存储信息方式,这时就出现麻烦了。 我们希望能再次利用这些高层模块, 但高层模块都是与低层的访问数据库绑定在一起的, 没办法复用这些高层模块。

抽象不应该依赖细节, 细节应该依赖于抽象。

针对接口编程, 不要对实现编程。(无论主板、CPU、内存、硬盘都是针对接口设计的, 如果针对实现来设计, 内存就要对应到具体的某个品牌的主板, 那就会出现换内存需要把主板也换了的尴尬)

依赖倒转其实可以说是面向对象设计的标志, 用哪种语言来编写程序不重要, 如果编写时考虑的都是如何针对抽象编程而不是针对细节编程, 即程序中所有的依赖关系都是终止于抽象类或者接口, 那就是面向对象的设计, 反之那就是过程化的设计了。

里氏代换原则

子类型必须能够替换掉它们的父类型。
一个软件实体如果使用的是一个父类的话, 那么一定适用于其子类, 而且它察觉不出父类对象和子类对象的区别。 也就是说, 在软件里面, 把父类都替换成它的子类, 程序行为没有变化。

迪米特法则

如果两个类不必彼此直接通信,那么这两个类就不应当放生直接的相互作用。 如果其中一个类需要调用另一个类的某一个方法的话, 可以通过第三者转发这个调用。
迪米特法则其根本思想, 是强调了类之间的松耦合。 类之间的耦合越弱, 越有利于复用, 一个处在弱耦合的类被修改, 不会对有关系的类造成波及。

合成/聚合复用原则(CARP)

尽量使用合成/聚合, 尽量不要使用类继承。

类继承的缺点:
对象的继承关系是在编译时就定义好了, 所以无法在运行时改变从父类继承的实现。 子类的实现与它的父类有非常紧密的依赖关系, 以至于父类实现中的任何变化必然会导致子类发生变化。 当你需要复用子类时, 如果继承下来的实现不适合解决新的问题, 则父类必须重写或被其他更适合的类替换。 这种依赖关系限制了灵活性并最终限制了复用性。

聚合表示一种弱的‘拥有’关系, 体现的是A对象可以包含B对象, 但B对象不是A对象的一部分; 合成则是一种强的‘拥有’关系, 体现了严格的部分和整体的关系, 部分和整体的声明周期一样。
优先使用对象的合成/聚合将有助于保持每个类被封装, 并被集中在单个任务上。 这样类和类继承层次会保持较小规模, 并且不太可能增长为不可控制的庞然大物。