面向过程和面向对象
面向对象的系统由类组成,但是有类/对象并不意味着一定就是面向对象设计。
面向对象代码和面向过程代码的根本区别在于职责的分配方式。
面向过程
采取的是一系列命令和方法调用的形式处理任务,这种自上而下的方式会导致项目中出现重复代码以及依赖关系。
面向对象
通过从要处理的任务中细化职责,将不同职责从客户端代码转移到系统中的不同对象上,可以尽量减少这些依赖关系
模块
软件设计的目的最终是为了实现功能,随着软性系统越来越庞大,面向过程的代码会变得越来越复杂。
复杂性的问题,比较难解决,但是可以将复杂问题拆解为若干简单问题,逐个击破地解决这些简单问题,最后实现解决复杂问题的效果。
模块就是从逻辑上将系统分解为更细微的部分,分而治之。
模块的粒度划分可大可小,可以是函数,类或功能块等等。
那么模块如何分?划分后的模块之间如何协作?
内聚与耦合
内聚性关注的是单个模块内部的关联紧密程度。因而高内聚追求的是关联紧密的事物应该被放在一起,并且只有关联紧密的事物才应该被放在一起。简单说,就是Unix的设计哲学:Do One Thing, Do It Well
耦合性则是强调两个或多个模块之间的关联紧密程度。因而低耦合追求的是,模块之间尽可能不要相互影响。
SOLID 五大原则
软件设计的核心思想是对抽象编程,而不对具体编程
单一职责和开放封闭,更多的在强调类划分时的高内聚;而里氏替换,接口隔离,依赖倒置则更多的强调类与类之间协作的低耦合。
单一职责原则 (Single Responsibility Principle, SRP, S)
单一职责有两个含义:
避免相同的职责分散到不同的类中
消除重复以达到单一职责
避免一个类承担太多职责
一个类,最好只做一件事,只有一个引起它变化的原因。通过对变化原因的识别,将一个承担多重职责的类,不断分割为更小的,只具备单一变化原因的类。
分离不同变化方向以达到单一职责
开放封闭原则 (Open-Closed Principle, OCP, O)
需求总是变化的,没有不变的软件,软件实体应该是可扩展的,而不可修改的。也就是对扩展开放,对修改封闭
让客户端代码依赖于稳定的抽象,对实现抽象的具体类做修改时,不对客户端代码造成影响,以此达到封闭变化的目的。
通过面向对象的继承机制,可以对抽象类进行继承,或覆写抽象类的方法来改变固有行为,或增加新的方法来支持更多行为,以此达到扩展的目的
通过面向对象的多态机制,可以增加实现接口的类,以达到扩展的目的
里氏替换原则 (Liskov-Substituion Principle, LSP, L)
子类应当可以替换父类出现在父类能够出现的任何地方,但是父类不一定能替换子类
接口隔离原则(Interface Segregation Principle,ISP,I)
使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些它不需要的接口。
依赖倒置原则(Dependecy-Inversion Principle,DIP,D)
抽象不应该依赖于具体,具体应当依赖于抽象。
依赖一定会存在于类与类、模块与模块之间。 当两个模块之间存在紧密的耦合关系时,最好的方法就是分离接口和实现: 在依赖之间定义一个抽象的接口使得高层模块调用接口, 而底层模块实现接口的定义,以此来有效控制耦合关系, 达到依赖于抽象的设计目标