面向对象的七大设计原则有:1 单一原则 2 接口隔离原则 3 组合原则 4 开闭原则 5 替换原则 6 依赖倒置原则 7 最少知识原则 。
程序设计的基本元素有:语句、语句块、方法、属性与变量、结构与类,其中语句有赋值、条件、算术等等,语句块有{},循环、if-else等等。
基本的软件结构有:1 创建结构、2 交互结构、3 组织结构。
1 创建结构捕捉对象创建过程的复杂性,增加一层,把创新需求从复杂的创建过程中解放出来。
2 交互结构着眼于模块对外交互的复杂性,增加一层,或是降低类型内部的复杂性,如状态模式、策略模式、模板方法、备忘录模式;或是协调外部交互的复杂性,如观察者模式、命令模式、;或是规范多个同类型不同类型元素协同工作的方式,如责任链模式、迭代器模式、访问器模式和调解模式。
3 组织结构隐藏现有结构的复杂性性,增加一层,化简元素内部结构或是外部结构的复杂性。如适配器模式、装饰模式、门面模式和代理模式都是着眼于对外接口的复杂性,其中适配器模式变更已有接口的调用方式,装饰模式在隐藏内部复杂性的情况下提供更丰富的特性,门面模式简化已有接口。桥接模式使抽象与实现相分离。 组合模式统一集合与单个元素的对外接口。
1单一原则
单一原则是第一原则,是软件系统的基石。单一是指软件元素职责或是功能上的单一性,是设计与实现的基础性原则。消除系统内各元素的相互干扰,确保各个元素的独立性,使干扰变成有序可控的接口交互或是同层次元素,提高各层次元素的复用率。
1.1 如何做到单一
软件设计中有个词叫做过度设计,它是指软件实施中,使用组织结构过于复杂,超出目前需求对软件复杂性的要求。但是,单一原则是永远不存在过度单一之说。单一原则是软件设计、重构的基础,是可测试的基础。一个充分单一的模块、类、子系统,在各种重构的要求下都可以保存非常好的健壮性,重构完全不影响单元测试的有效性。
描述的单一性是保证软件实施中单一原则的最有效的法则。对每一个基本元素、每一个结构元素,都可以用单一明确的名词、动词、或是形容词等词汇来描述。如果可以用两个不同的词汇来描绘一个元素,说明违反了单一原则。当然在时间软件实施过程中,做到这一点是很困难的,但是至少在一些重要位置一定要确保单一原则的实施。
1.2 对软件系统的影响
以有序的交互代替不可控的干扰,以多个平行的元素代替各种条件分支,功能的高度单一性可保证系统高度可重构水准。对于设计、实现、与测试都是基础性原则。单一原则在多种设计模式与架构上都有充分的体现:如在经典的MVC模式中,M是业务逻辑的模型,V负责展示,C协调M与V的交互;在严格的分层架构体系中,每一上层只能调用其临近下层提供的服务而不能隔层调用,下层只为上层提供服务不能主动发起对上层的调用。单一原则要求每一层只为上层提供并行的服务,各个服务之间不能有互相的影响,单一原则是确保分层有效的基础;在微内核架构中,内核只是具有通信功能的通道,以协调子系统的通信,最大限度降低了内核变动的可能性。
2 接口隔离原则
如果说单一原则是保证软件质量的基石,那么接口隔离原则就是抽象的第一法则。单一原则为抽象提供元素,并指导接口抽象的单一性,即接口隔离。
单一原则为接口隔离把复杂的系统打碎为各个功能单一的部分。摒弃各部分之间不可控的干扰,明晰其间的交互需求。为接口的组织与重构提供可靠的基础——接口可以随意的组合、变化,底层实现保存不变、实现模块的测试始终保持有效。底层实现不随着组合需求的变化而变化,基础功能变化是很少的而且具有组合特性,但是对基础功能的组合需求是易变的(这就是桥接模式的理性存在,组合原则的可实现性哲学基础)。
充分隔离的接口元素,可能包含多个单一的元素。它们是通过组合,以成员的形式存在于各个粒度的接口元素中。现在,我们同时具有了底层单一元素的稳定性,隔离元素的组合多样性,极大的提高了表达的能力。隔离接口的组合特性,把复杂的需求变动与单一性隔离开来——这就是接口隔离原则的哲学基础,组合原则的指导原则。
3 组合原则
组合原则着眼于基于单一原则与接口隔离原则的构造,组合结构或是组合对象可具有非常复杂的组合特性,远远超出了组成元素的特性。如车子的各个部件组合在一起有了可以飞驰的汽车,在软件领域也有类似的现象。世界之所以如此多样、美丽,并不在于基本组成单元的无限多,各个层次元素在一起所组合产生的特性为我们提供了无限的可能性。单一元素的组合应用大大提高了软件的复用度,为系统的实现提供了可能。各个粒度的接口隔离元素为上层变易需求提供快速实现的方式,只有通过单一元素的组合与各个粒度隔离接口元素的组合才能向上层提供底层所不具有的新特性——这是组合原则存在的必然性与哲学基础。
组合是接口的组合,即对抽象的组合,要避免对实现的组合,即对单一元素的组合——对接口编程,单一元素与隔离元素的组合的特性是对接口编程的哲学基础。对抽象(接口)编程从来不是为了抽象,而是为了获得更高一层抽象——组合及其特性。所以对接口编程这个词汇从系统论的角度是有些许不当的——应该说对组合抽象编程,以强调一般接口的组合特性,毕竟单一元素的抽象接口使用场景非常有限。
4 依赖倒置原则
以上三大原则都是从,基本元素、抽象组合元素的结构角度出发,剖析系统的组织,是单向性的功能构造与结构组织,单向性的交互——上层调用下层为其提供服务。
依赖主要表现在抽象对实现的依赖,高层次模块对低层次模块的依赖。只有接口之间的交互,即对接口编程,是若依赖。可以使用解耦与反转处理依赖,存在的编程技术有各种成熟的设计结构、对抽象编程和依赖注入。通过对接口编程技术与注入机制,保证了依赖元素之间的独立变化。
解耦是通过在依赖方与被依赖方中间加入一解耦层,断开依赖双方的直接交互。如在消息处理系统中用来解耦消息的发送方与接收方的MQ。
依赖倒置原则不同于上面的三大原则。它着眼于相对独立的组合元素间的交互特性,如结构中含其它结构的结构变量及其操作函数之间的调用关系。调用可以是单向的,如对sqlite数据库的读写操作;亦可以是双向的,如生产-消费者模型。通过依赖注入技术实现被依赖方的主动注入,利用抽象编程实现依赖方以被依赖方变化的分离,使双方的注入元素与依赖功能调用都可以独立于实现细节而变化。只要注入接口与交互接口不变,实现细节的变化不会影响到系统的外部特性。
5 最少知识原则
这个原则比较简单,是接口隔离原则在交互中的使用规则:一方面要求交互接口的最小化,不添加不必要的接口;另一方面不要通过任何方式直接调用操作于组成元素的方法。在面向对象设计中,这一点比较容易做到,但是在面向过程设计中,不直接调用操作于组成元素的方法是十分困难的。
封装、继承和多态是面向对象程序设计的三大原则。开闭原则是面向对象程序设计所独有的设计原则,是对继承原则使用方式的约束——对修改封闭,对添加开放。不允许修改已存在的类,可以添加相关的新子类或是其他结构元素来满足新的需求。具体的添加的方式可参考设计模式中结构模式。
单一原则、接口隔离原则和组合原则决定了封装的方式。封装是数据与行为的封装,同时涉及到了单一原则与接口隔离原则。组合原则是存在的多样性的基石,而类就是对存在的抽象表达,不灵活运用组合原则将导致大量重复代码的存在。三大原则对继承与多态原则没有直接的指导作用,开闭原则填补了这一缺失。由此看来继承与多态才是面向对象独有的原则。
开闭原则要求对已有类修改封闭。开闭原则要求对目标类添加新子类是开放的。
在非面向对象编程范式中,当新的需要求要求改动已有数据结构时,我们只能(1) 改变数据结构并增加关联的函数或是修改已有函数,(2) 或是增加新的结构。(1)改变已有的结构必然导致对已有函数的修改,引入过多的条件语句,长时间的结构变更性维护一方面存在对已成熟的业务代码引入新的bug,另一方面新加入的修改导致原来测试的无效,同时过多的条件将导致版本发布的延期,工作陷入各种逻辑的诡辩中。(2) 由于数据结构之间没有内在的关系,创建新的数据结构需要从写所有对元数据结构存在操作的代码,导致相应代码量的倍增,给维护、测试代来极大的困难,代价是极高的很不现实。
在面向对象程序设计中,源于类的继承特性,把变易控制稳定的结构中——子类中,只需要修改或添加变动的部分,复用父类大部分已有代码,使已有类免于变更的要求,保存了对已有类测试的有效性。
7 替换原则
替换原则是多态特性的实现方式指导法则,是面向对象设计独有的原则。通俗的讲就是抽象接口变量变量可以用任何实现该接口的类实例来替代、父类实例都可以用子类的实例去替换。替换原则的可行性基础是类、接口的继承特性,没有继承就无从替换,更谈不上多态的可能性。开闭原则为替换原则提供可替换子类,是多态性的存在的基础。
8 小结
文章以单一原则为基础,深入讨论了程序设计7大原则的关系,及其在软件设计中的作用、地位。
在深入分析了单一原则和接口隔离原则两个基础性原则后,逐步分析了组合原则与依赖倒置原则,深刻剖析了它们在软件设计中的统治地位。
在开闭原则与替换原则中,指出它们与面向对象的继承与多态特性的关系。
尤其文章中是对接口隔离原则和组合原则的分析,为复杂系统的设计与特性的实施提供了思路。
以后还将不断丰化这篇文章的内容。