Title Date Modified Category
design 2019-11-20 12:00 2019-11-20 12:00 design

1. 设计

设计没有标准,只有目标。如果硬要指定一个标准,那么标准就是快捷,适用与优雅。

1.1. 原点 - 程序设计。

不弄清楚本质怎么行呢?

1.1.1. 程序

关于我对程序的看法

程序,静态的时候是躺在磁盘里的可执行文件。动态的时候是运行着的进程。内核里对应的是个内核线程。

程序,本质,就是数据加逻辑的组合。是数据+算法。

逻辑可以用来改变数据。

逻辑可以用数据做参数,通过计算产生新的数据

数据可以用来控制逻辑的走向。

看不清本质,就会被眼花缭乱的各种思想,规则所迷惑。

1.1.2. 设计

设计的目的是如何驾驭复杂的程序逻辑。

所有的思想,规则,都是想要以一种思路去驾驭复杂的运行流程。设计算法的手段。

1.2. 来自《人月神话》一书中的观点

所有软件活动包括

  • 根本任务:打造由抽象软件实体构成的复杂概念结构,
  • 次要任务:使用编程语言表达这些抽象实体,在空间和时间限制内将它们映射成机器语言。

面向对象主要是在解决次要任务上面有作用。

而根本性问题依旧复杂,没有银弹。

1.3. 程序设计思想进化史

1.3.1. 史前时代:面向机器

起初,人们写01串,输入机器。

然后,创建了汇编语言做标记。

1.3.2. 脱离机器第一步:面向过程

COBOL, FORTRAN, BASIC, C语言等。

面向过程是一种以“过程”作为中心的编程思想,其中过程的含义就是“完成一件事情的步骤”。

即使我们使用面向对象的语言进行开发,最后转换为CPU能执行的指令,也还是面向过程的。所以说,面向过程无处不在,是计算机的基石。

1.3.3. 第一次软件危机:结构化程序设计

结构化程序设计的主要特点是抛弃goto语句,采取“自顶向下,逐步细化,模块化”的指导思想。

结构化程序设计本质上还是一种面向过程的设计思想,但通过“自顶向下,逐步细化,模块化”的方法,将软件的复杂度控制在一定范围内,从而从整体上降低了软件开发的复杂度。

科学研究证明,人脑存在人类短期记忆,一般一次只能记住5~9个事物,这就是著名的7+-2原理。

1.3.4. 第二次软件危机:面向对象程序设计

面向对象开始时也被当作解决软件危机的银弹,但事实证明,和软件工程一样,面向对象也不是银弹,而只是一种新的软件方法而已。

虽然面向对象并不是解决软件危机的银弹,但和面向过程相比,面向对象的思想更加贴近人类思维的特点,更加脱离机器思维,也是软件设计思想上的一次巨大的飞跃。

1.4. 计划的设计与演进的设计

1.4.1. 计划的设计

提前设计好,再进行开发。

  • 要求我们首要考虑的不是编码,而是整个系统的架构。了解客户的需求。把握开发的进度。遵循统一的设计规范与原则,并对可能存在的技术难点进行预研。确定每一个模块的功能,以及模块间的关系和系统分布的层次。
  • 计划的设计则需要全面地把控系统的整体需求,衡量所有设计要素与约束,以求满足系统所有的功能性需求与非功能性需求。
  • 计划的设计会充分地考虑系统的可扩展性,将未来的变化纳入到一个可控的范围内。这种前瞻式设计需要软件架构师的匠心独运,然而一不小心,就会导致设计过度

  • 设计阶段需要花费大量的时间精力. 如果需求有变动,又要重新进行设计。

1.4.2. 演进的设计

写代码吧,有新的需求,重构。

  • 是一个渐进的过程。不要求前期的设计有多么的完美,实现的需求有多么的完整,仅需要把现阶段考虑的问题通过编码实现就可以了。随着演进的深入,对需求更加准确的理解,编码也会随之而修正,整个设计会逐渐丰满起来,经过逐步地演化,最后趋于完美。
  • 演进的设计更接近敏捷的开发理念,提倡简单设计与设计重构,从而应对快速开发的要求,满足可能的需求变化。
  • 演进的设计可以有效地避免这种设计上的浪费,但我们却不可忽视它可能带来的重构成本

1.4.3. 计划的设计 vs 演进的设计

  • 计划的设计,从一开始就全盘考虑,仔细设计。也有可能导致过度设计。
  • 演进的设计,类似于敏捷开发。是一个渐进的过程。提倡简单设计与设计重构。可以有效避免设计上的浪费。却不能忽视重构成本。
  • 两者并非水火不容,应当取长补短,配合使用。

  • 无论采用何种设计方式,最关键的还是在于客户的需求。

  • 软件开发中,唯一不变的就是变化。变化是常态,我们无法规避。

1.5. 敏捷开发

敏捷开发告诉我们,为当下功能进行设计,不要假设未知的变化。否则会导致过度设计。

当变化真正来临的时候,重构当前设计,以满足变化的需求。

1.5.1. 过度设计,还是简单设计

Kent Beck在《解析极限编程-拥抱变化》中为简单系统制定了4个评价标准,依次为(最重要的排在最前面):

  • 通过所有测试
  • 体现所有意图
  • 避免重复
  • 类或者方法数量最少

1.5.2. 代码中的坏味道

  • 僵化性(Rigidity):设计难以改变。
  • 脆弱性(Fragility):设计易于遭到破坏。
  • 牢固性(Immobility):设计难以重用。
  • 粘滞性(Viscosity):难以做正确的事情。
  • 不必要的复杂性(Needless Complexity):过分设计。
  • 不必要的重复(Needless Repetition):滥用鼠标。
  • 晦涩性(Opacity):混乱的表达。

1.5.3. 原则

  • 单一职责原则(The Single Responsibility Principle,简称SRP)
  • 开放-封闭原则(The Open-Close Principle,简称OCP)
  • Liskov替换原则(The Liskov Substitution Principle,简称LSP)
  • 依赖倒置原则(The Dependency Inversion Principle,简称DIP)
  • 接口隔离原则(The Interface Segregation Interface,简称ISP)

1.5.4. 臭味与原则

设计中的臭味是一种症状,是可以主观(如果不能客观的话)进行度量的。这些臭味常常是出于违反了这些原则中的一个或者多个而导致的。

敏捷团队应用这些原则来除去臭味。当没有臭味时,他们不会应用这些原则。仅仅因为是一个原则就无条件的去遵循它的做法是错误的。
这些原则不是可以随意在系统中到处喷洒的香水。过分遵循这些原则会导致不必要的复杂性(Needless Complexity)的设计臭味。

敏捷开发人员不会对一个庞大的预先设计应用那些原则和模式。
相反,这些原则和模式被应用在一次次的迭代中,力图使代码以及代码所表达的设计保持干净。

1.5.5. 需要设计模式吗

如果仅考虑实现当前的功能需求,还需要设计模式吗?坦白的说,我并不认为设计模式与过度设计有关。过度设计的导火索是设计模式的滥用。很多时候,合理的利用设计模式反而能使程序结构简单化,特别是,它能够让开发过程更简单。

需要设计模式吗?答案看来是不言而喻。关键一点是需要确定模式的应用是否过度?

世界上很多天才横溢的程序员,可以在一段代码中包含6种设计模式,也可以不利用模式就能把设计做得很好。

我们需要的是有效的设计。学习设计模式,不是为了炫耀,吹嘘,不是为了故作艰深,而是为了改善我们的设计,它可以为某种功能实现提供参考模型,设计方法以及应用范例。

我们不需要奉GoF的设计模式为圭(gui)臬(nie),盲目地膜拜它,合理地运用设计模式,才是明智的抉择。

1.6. 变化

  • 软件开发中,唯一不变的就是变化。

1.7. 重构是必然的

适时地重构不仅能改善系统的整个架构,也能为今后的设计提供指引。甚至在数度重构之后,因为设计的合理性,会成为今后项目开发的框架平台或者公共类库。

如果从纯技术的角度来看,重构非但必然,而且重要。重构是必然的。重构是不可避免的。

引入重构技术,完全符合简单设计的原则。重构让设计变得简单直接 的 表达需求。

对于系统架构师而言,重构技术能够降低糟糕的设计给软件开发带来的风险。

软件设计难以一蹴而就,演进的设计证明了这一点。 即使采用计划的设计,同样需要在设计过程中对架构完成重构。 任何的设计都不可能hold住所有的变化,即使是计划的设计。 软件开发唯一不变的就是变化。

以设计模式作参考,可以很好地组织。

1.8. 关于几个编程语言

1.8.1. 不同语言

高级语言及其分类

  1. 过程式语言
  2. 函数式语言
  3. 逻辑程序设计语言
  4. 面向对象的语言
  5. 结构化查询语言
  6. 其他面向特定应用领域的语言

1.8.2. python

我认为,Python是一门更加高级的语言。很多设计模式已经隐藏其中。

Python的动态性是优点也是缺点。

鸭子类型,看起来是,那就是.

面向对象理论中的“类”和“对象”这两个概念在Python中都是通过Python内的对象来实现的。

python是由C实现的。

python是一种简单、灵活的动态语言,python简化了面向对象语法的复杂性,但却没有降低面向对象的特性。

使用python同样可以实现各种设计模式,而且实现过程比较简单。

python的动态性有时候也是一种灾难,尤其是构建大型复杂的系统的时候

1.8.3. C

C一般人叫做面向过程的语言。但其实C也是可以面向对象的思想。

kobject与面向对象编程语言(像C++或Java)中的对象概念的相似性决不是巧合。 kobject抽象实际上提供了在内核使用面向对象技术的可能性,而无需C++的所有额外机制(以及二进制代码大小的膨胀和额外开销)。

1.8.4. Go

没有继承的概念。没有类的概念。

Go以一种不同寻常的方式来诠释面向对象程序设计。

它没有类继承,甚至没有类。

较复杂的对象行为是通过较简单的对象组合(而非继承)完成的。

方法可以关联到任何用户定义的类型,而不一定是结构体。

具体类型和抽象类型(即接口)之间的关系是隐式的,所以一个具体类型可能会实现该类型设计者没有意识到其存在的接口。

1.8.5. Java

语言本身就存在着过度设计。更何况使用其设计的程序呢

1.8.6. C++

即使是C++的设计者也不能hold住所有的特性。一般用C++,找一个子集功能就好了。

1.9. 架构设计

软件架构的地位举足轻重,甚至在很多时候,它是项目成败与否的关键。

1.9.1. 是什么

架构设计就是设计系统的顶层结构。

1.9.2. 为什么做架构设计

架构设计是为了隔离关注点,降低复杂度。

架构设计是为了分工合作。

架构设计就是“面向对象”思想的一个具体应用而已。

也就是说,“面向对象”的思想既可以指导程序设计,也可以指导架构设计;

而且在架构设计领域,可以说只有“面向对象”这一种指导思想,因为架构设计必须划分出模块并设计好模块的交互方式。

更进一步分析:

  • 面向对象程序设计通过封装“类”来降低复杂度,
  • 面向对象架构设计通过封装为“模块”来降低复杂度;
  • 面向对象程序设计通过“类交互”来完成分工合作,
  • 面向对象架构设计通过“模块交互”来完成分工合作。

1.9.3. 架构的标准

根据IEEE的定义,软件架构(Architecture)是以组件,组件之间的关系,组件与环境之间的关系为内容的某一系统的基本组织结构,以及指导上述内容设计与演化的原理。

而Martin Fowler则认为,架构是系统核心而又稳定的组成部分,是系统构建的基础。作为软件设计的高层部分,架构从整体到部分对软件系统进行了最高层次的划分,它是支撑更细节设计的框架。因而,我们有时候也将软件架构称为高层设计(High-level Design)或顶层设计(Top-level Design)。

一般而言,架构需要关注如下内容。

  1. 程序组织(Program Organization)
  2. 数据设计(Data Design)
  3. 安全性(Security)
  4. 性能(Performance)
  5. 可扩展性(Scalability)
  6. 可靠性(Reliability)
  7. 可用性(Usability)

1.9.4. 架构设计原则

客户需求优先原则

适当超前原则

唯一不变的是变化!

首先要满足客户需求,然后再超越客户需求。

关键在于“适当”二字。

1.9.5. 架构设计屠龙刀

"拆"与"合"

拆的常用手段
拆硬件
拆地点
拆功能

至于逻辑的拆分。

分层,可分为网络接口层,数据库接口层,配置接口层,model层。中间的是核心逻辑。

大部分程序这样拆开后,最后的核心逻辑少得可怜,根本不需要什么设计。

如果中间逻辑还是很复杂,接着拆,
横向拆,竖着拆,拆成不同的函数组的模块。终究能够将核心逻辑摘出来。

怎么拆?核心思想就是,从外层一层一层的拆,这样可以保证模块的依赖关系是单向无环的图状结构。最完美的情况下应该是一个树状结构。

大部分功能,这样干都能解决了,甚至可以将逻辑拆的很细的模块,函数可以很小,看起来也很好了。

简单实例
合的常见手段
客户端合
网络合
中间件合
子系统合

1.9.6. 优秀架构设计品质

创新

results matching ""

    No results matching ""