association 对象为null_面向对象设计模式分析

SOLID 是缺乏灵魂的,难以记忆更难以灵活运用。 Robert Martin 在 Agile Software Development, Principles, Patterns, and Practices 中对 SOLID 的描述过于松散,原则之间缺乏逻辑的一致性和一贯性,缺少理性的分析,导致夸夸其谈者甚多。为此我们大多局限在经验的泥潭中无法自拔,在面对新的场景时却束手无策。

本文认为在面向对象的软件设计中,抽象是一种实在,而对应的多种实现则是抽象的一个个现象。基于实在的抽象和现象的实现,本文尝试构造一个逻辑上连贯且完备的面向对象设计原则模型,并基于此分析已有原则和设计模式的完备性。抽象、封装、继承和多态是面向对象设计的四大基石,其中抽象以接口的形式展现,封装以类的形式体现。

抽象是一种实在,是复杂的事物或虚拟概念在软件中的一个映射。它抓住部分侧重点并有意识地忽略无关紧要的细节,是面向对象设计中接口和类的本质性规定,可以说抽象就是接口。

b766258e2a40fe2ff7434cae4c5abe3a.png

实现是抽象的现象,表现为一个个具体的类,实现遵从抽象的本质性限定,

认识模型

8e169635e082221da44afc7101188b73.png

面向对象设计原则模型

目前已知的有六个面向对象设计原则:接口隔离原则、依赖倒置原则、里氏替换原则、单一职责原则、开闭原则和最少知识法则。其中接口隔离原则、依赖倒置原则、里氏替换原则,属于抽象的范畴;单一职责原则、开闭原则、最少知识法则,属于对具体现象的约束。

f1cf6000d25ea50e88b3a90d5bfa1a5f.png

抽象和现象都遵守上面的模型,即一个研究的目标都需要一个本质的界定、一个清晰的边界和与外交交互的法则。其中本质决定研究目标的存在性,边界限定存在的形式限定,交互限定目标之间的关联准则。

抽象原则

2d1766947e1cc65e693f84d19ea69eba.png

实现原则

8024d6b833604ea660f08795068733b3.png

面向对象的六大设计原则,三个接口设计原则和三个对象设计原则。

三个接口设计原则为:接口隔离原则(存在准则)、依赖倒置原则+最小依赖原则(调用准则)、里氏替换原则(抽象不变性准则)。

三个对象设计原则为:单一职责原则(存在准则)、开闭原则(变更准则)、迪米特原则(最少知识原则,关联准则)。

模版方法 VS 方法覆盖

最小依赖原则

下面我们从接口和类两个概念出发,在剖析六大设计原则的完备性基础上,提出了最小依赖原则

接口设计原则

在接口设计原则中,接口是我们考察的基本概念。在考察接口概念定义的基础上,剖析其四个原则的合理性和完备性。

什么是接口?接口是抽象行为能力的定义,

接口隔离原则

接口尽可能的细

依赖倒置原则

实体或接口对外要依赖抽象,依赖接口

里氏替换原则

继承确保语义不变,接口语义和属性语义(KVO)

最小依赖原则(新原则)

注释:接口是对能力的抽象 抽象隔离(存在)-依赖抽象(使用方式)-抽象语义不变(运行结果)

对象设计原则

单一职责原则

一个实体只能有一个维度的变因

开闭原则

类是一个坚实的实体,可对其使用装饰模式,但不能直接修改它。

迪米特法则(最少知识法则)

两个实体不直接通信,通过一个中间的人,即用一个皮条客来协助两个实体之间的通信,比较著名的 MVC 模式就遵守了该原则。

注释:类是对特定业下公有接口和自定义接口的实现,变因-如何变-如何通信

小结

读一读黑格尔的小逻辑,把握原则背后的思想,更灵活运用这些设计原则,功夫在诗外。思维僵化、知识面太窄、缺乏批判精神,便无法对已有元素做出合理性的考察。良好的技术敏感性是局部设计中能够兼顾整体架构的关键。


设计模式

软件模式分为:设计模式、架构模式、分析模式、过程模式等。在软件生存期的每一 个阶段,都存在着一些被认同的模式。设计模式主要分为三类,创建模式、结构模式和行为模式:

  • 创建模式,解决类实例化的复杂性,解决存在性
  • 结构模式,封装类的各个组合组件在类实例化过程中的复杂性,反应类的自身结构特性
  • 行为模式,解决两个或是多个对象之间的交互复杂性

在做分析的时候,存在性、结构性、交互性,是我们对所有的软件实体在考察过程中必须使用的形而上方法论。下面我们分别从存在性、结构性、交互性三个概念出发,考察创建模式、结构模式和行为模式所包含的具体设计模式,及其在形而上下的完备性。


创建模式


行为模式(交互性12)

行为模式可化分为四个类别,行为封装(点),行为委托(线),分配行为(图),调用职责(调用)

行为封装(在对象内部解决复杂性,使对象体现出多态的特性):策略模式、状态模式、模版方法、NULL 对象

行为委托(多个对象协作完成一个行为,自己搞不定就委托给小伙伴):责任链、备忘录模式

分配行为:观察者、中介器模式、命令模式、解释器?、

调用行为(正交化):迭代器(分离遍历过程)、访问器(分离访问过程)

观察者模式

a7aba3d264bb84fa7d4e791a3b19c283.png

43372f87d8df0fc13dbdf6cd83ae3041.png

命令模式

ae05fe0b1a62c3e5020d7ba8d180d9f8.png

4db9de9263266136704a8ab6bee85e07.png

责任链

cfac956088cf83ec7540f099b4e6711e.png

策略模式

抽象的语意可以用不同的策略,即实现方案,来实现,它们语意相同(相同的输入给出同样的输出)但适用的场景、资源、问题规模等肯能各有千秋。

策略模式封装的复杂性:简化版的桥接模式。

5c64efe06e0793e87de34d0340ddf032.png

7c5f14621020738beb8eb67424199981.png

b2197f7805ccff6dd1279dcf440fda28.png

状态模式

状态变更,引起行为变更,是一个数据(状态)驱动的策略模式。

20dfd01034a433786c2fc5a5f93396a2.png
状态变更

结构模式(结构性8)

实例复用:享元模式。

结构复用(接口保持不变):代理模式、装饰模式(增强已有接口)、组合(Composite 增强已有接口)模式。

调用复杂性(接口改变):适配器模式(接口重构)、外观即门面模式(为子系统提供新的抽象层,降低调用复杂性)。

实现复杂性:桥接模式。

组合设计模式

0799a25d15583ecd4eac7d0a1797b4ad.png

桥接模式

隔离抽象与实现,即隔离接口与实现,实现编程与实现无关。

桥接模式封装的复杂性:把抽象与实现构造为两个独立的正交类体系,抽象封装语意(提供实现的上下文),实现封装具体的过程。

267fa703841b943eb1be9c090c3cc9ac.png

393ab6fb0efcd6afaa64d4eef77ee12e.png

d961f50b9079281a1e826d28e51e0328.png
使用继承-类爆炸

c26b0a90866f95c577577bb33ad8b447.png
使用桥接模式-正交的两个类体系

适配器模式

重构已有类的接口格式,但保持严格的一对一原则,如参数传递的方式、参数的类型、函数名称等。

适配器模式封装的复杂性:在系统演化或是引入第三方库时,某些接口可能与当前的设计原则和架构风格相背,用适配器统一接口的风格,简化调用成本,统一调用风格。

8c3022faafdbcc7ed59af42bff5f145d.png

f01d213975e3d20384437cac79fc0c20.png

2e43ddd062213b28de12a5eda97553c3.png
适配参数格式

外观模式

外观模式封装的复杂性:屏蔽子系统的复杂性,为调用者提供简洁的接口,降低调用复杂性,可为特定的业务场景定义特定的外观模式。

933480c8039f5423714e792f359cdaa1.png

dcbd3292e775ea2f0952aec92c14d1bf.png

装饰模式(非常强大的模式)

用组合和关联关系,替代继承,消除为给已有对象增加新特性中的复杂性。

装饰模式封装的复杂性:解除类继承过程中失去的灵活性(比如class A 和 class B 都是 class C 的具体直接子类,如果用继承给A和B增加新特性,那么需要创建两个子类),是一个反特化的过程。

faa56c559cd413f2864214eb8855912f.png

d2e7681acf42696f20832dbaaa656c72.png

享元模式(轻量级模式)

享元模式解决细粒度对象复用的问题,复用引起的改变必须足够小。

细粒度对象在复用过程中,使用享元模式封装变更的大小。不同的值能够引起享元状态发生巨大的改变(如 iOS cell 中文本的长度引起的布局上的巨大改变,即 iOS cell 复用的是布局不是模型)就不适合使用相同的享元。

享元模式封装的复杂性:根据变更的大小,返回复用的对象。

57b97c54505cbd15ca3140835e61511e.png

e43a78c7f1a236f083ccd07325809011.png

代理模式

每个类存在单一维度的变因,但在特定的依赖关系中,依赖方与被依赖方会引入特定的一个或是多个变因(如远程代理中距离的因素),代理模式封装这种因依赖而引入的变因。它本质上与被依赖对象的无关,变因的引入更多的是因为依赖者或依赖者所处的环境所具有的限制。

代理模式封装的变因:因调用者或是调用者所处的环境,而引入的复杂性。

795ed2a549956e75b8adb175fd0e5f2f.png
代理对象的对外接口与被代理对象 保持一致

78b5eb5bcec1dda26dc54e09efa61414.png

类图

d600ae2ce4da053f0f0fb513424dc7e1.png

继承关系 is-a

泛化关系,空心箭头+直线表示;实现关系,空心箭头+虚线表示。

b6f62698d9c6e6f4fb044c2fda2d1746.png
实现关系

d5d29ed0e2d01b8a493ea5aa34ed437a.png
泛化关系

聚合关系(aggregation)

空心菱形箭头+直线表示,整体由部分构成;整体不存在部分仍然存在

75073146f9e0962a651f0b8a9fa2bb0f.png

组合关系(composition)

用带实心菱形箭头+直线表示;整体不存在,则部分就不存在

2062104dfe706c5e42164e2ea43edd87.png

关联关系(association)

直线表示,对象之间的结构关系、一种静态关系,是一种“强关联”的关系;默认不强调方向,表示对象间相互知道;如果特别强调方向,表示A知道B,但 B不知道A。

4aa3986fa096256fbeea08184b1e4021.png
以成员变量的形式实现

依赖关系(dependency)

带箭头的虚线表示的;描述运行期间会用到另一个对象,体现为参数的传递。

01b2d69f26f4370e17386879db892042.png
应该避免双向依赖
  • https://design-patterns.readthedocs.io/zh_CN/latest/ 图片来源
  • https://www.freecodecamp.org/news/the-basic-design-patterns-all-developers-need-to-know/
  • https://sourcemaking.com/design_patterns/adapter
  • Dive Into DESIGN PATTERNS
  • https://sourcemaking.com/antipatterns/software-development-antipatterns
  • https://sourcemaking.com/refactoring
  • 《哲学问题》 罗素 英
  • Agile Software Development, Principles, Patterns, and Practices
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值