第一项任务:确定系统及其形式与功能。
第二项任务:确定系统中的实体、实体的形式与功能,以及系统边界和系统所处的环境。
第三项任务:确定实体之间的关系。
第四项任务:基于实体的功能以及实体之间的功能互动,来确定系统的涌现属性。
微软把架构师分为 4 种:
架构师要做的就是简化系统的复杂度,消除业务歧义,致力于输出健壮兼容的系统架构。
资源评估对项目经理来说很重要,资源是项目进度的支撑;
资源评估对架构师来说也很重要,资源是架构实现的支撑;
系统的功能来自客户的需求,这二者如果出现歧义,架构师需要去消除。
架构就是对系统中的实体以及实体之间的关系所进行的抽象描述
分层架构:MVC,六边形架构,洋葱架构
事件驱动架构
微核架构
微服务架构
云原生架构
我们设计对象(类)的时候,如果某个类拥有完成某个职责所需要的所有信息,那么这个职责就应该分配给这个类来实现。这时,这个类就是相对于这个职责的信息专家。
eg: 常见的网上商店的购物车(ShopCar),需要让每种商品(SKU)只在购物车内出现一次,购买相同商品,只需要更新商品的数量即可。
实际应用中,符合下列任一条件的时候,都应该由类 A 来创建类 B,这时 A 是 B 的创建者:
a、A 是 B 的聚合
b、A 是 B 的容器
c、A 持有初始化 B 的信息(数据)
d、A 记录 B 的实例
e、A 频繁使用 B
如果一个类创建了另外一个类,那么这两个类之间就有了耦合,也可以说产生了依赖关系。依赖或耦合本身是没有错误的,但是他们带来的问题就是在以后的维护中产生连锁反应,而必要的耦合是逃不掉的,我们能做的就是正确的创建耦合关系,不要随便建立类之间的依赖关系,那么该如何去做呢?就是要遵守创建者模式规定的基本原则,凡是不符合以上条件的,都不能随便用 A 创建 B。
eg:因为订单(Order)是商品(SKU)的容器,所以应该由订单来创建商品。如下图:
低耦合模式的意思就是要我们尽可能地减少类之间的连接。
其作用非常重要:
a、低耦合降低了因一个类的变化而影响其他类的范围。
b、低耦合使用类更容易理解,因为类会变得简单,更内聚。
下面这些情况会造成类 A、B 之间的耦合:
a、A 是 B 的属性
b、A 调用 B 的实例的方法
c、A 的方法中引用的 B,例如 B 是 A 方法的返回值或参数。
d、A 是 B 的子类,或者 A 实现 B
关于低耦合,还有下面一些基本原则:
a、Don’t Talk to Strangers 原则
意思就是说,不需要通信的两个对象之间,不要进行无谓的连接,连接了就有可能产生问题,不连接就一了百了了。
b、如果 A 已经和 B 有连接,如果分配 A 的职责给 B 不合适的话(违反信息专家模式),那么就把 B 的职责分配给 A。
c、两个不同模块的内部类之间不能连接,否则比招报应!
eg:Creator 模式的例子里,实际业务中需要另一个出货人来清点订单(Order)上的商品(SKU),并计算出商品的总价,但是由于订单和商品之间的耦合已经存在了,那么把这个职责分配给订单更合适,这样可以降低耦合,以便降低系统的复杂性。如下图:
高内聚的意思是给类尽量分配内聚的职责,也可以说成是功能性内聚的职责。即功能性紧密相关的职责应该放在一个类里,并共同完成有限的功能,那么就是高内聚合。这样更有利于类的理解和重用,也便于类的维护。
高内聚也可以说是一种隔离,就像人体由很多独立的细胞组成,大厦由很多砖头、钢筋、混凝土组成,每一个部分(类)都有自己独立的职责和特性,每一个部分内部发生了问题,也不会影响其他部分,因为高内聚的对象之间是隔离开的。
eg:一个订单数据存取类(OrderDAO),订单即可以保存为 Excel 模式,也可以保存到数据库中;那么,不同的职责最好由不同的类来实现,这样才是高内聚的设计,如下图:
用来接受和处理系统事件的职责,一般应该分配给一个能够代表整个系统的类,这样的类通常被命名为“XX处理器”、“XX协调器”或“XX会话”。
关于控制器类,有如下原则:
a、系统事件的接收与处理通常由一个高级类来代替。
b、一个子系统会有很多控制类,分别处理不同的事务。
这里的多态跟 OO 三大基本特征之一的“多态”是一个意思。
eg:我们想设计一个绘画程序,要支持可以画不同类型的图形,我们定义一个抽象类 Shape,矩形(Rectangle)、圆形(Round)分别继承这个抽象类,并重写(override)Shape 类里的Draw() 方法,这样我们就可以使用同样的接口(Shape抽象类)绘制出不同的图形,如下图:
这里的纯虚构跟我们常说的纯虚构函数意思相近。高内聚低耦合,是系统设计的终极目标,但是内聚和耦合永远都是矛盾对立的。高内聚以为这拆分出更多数量的类,但是对象之间需要协作来完成任务,这又造成了高耦合,反过来依然。该如何解决这个矛盾呢?这个时候就需要纯虚构模式,由一个纯虚构的类来协调内聚和耦合,可以在一定程度上解决上述问题。
eg:上面多态模式的例子,如果我们的绘图程序需要支持不同的系统,那么因为不同系统的API结构不同,绘图功能也需要不同的实现方式,那么该如何设计更合适呢?如下图:
这里我们可以看到,因为增加了纯虚构类AbstractShape,不论是哪个系统都可以通过AbstractShape 类来绘制图形,我们即没有降低原来的内聚性,也没有增加过多的耦合,可谓鱼肉和熊掌兼得。
“间接”顾名思义,就是这个事不能直接来办,需要绕个弯才行。绕个弯的好处就是,本来直接会连接在一起的对象彼此隔离开了,一个变动不会影响另一个。就像我在前面的低耦合模式里说的一样,“两个不同模块的内部类之间不能直接连接”,但是我们可以通过中间类来间接连接两个不同的模块,这样对于这两个模块来说,他们之间仍然是没有耦合/依赖关系的。
预先找出不稳定的变化点,使用统一的接口封装起来,如果未来发生变化的时候,可以通过接口扩展新的功能,而不需要去修改原来旧的实现。也可以把这个模式理解为 OCP(开闭原则),就是说一个软件实体应当对拓展开发,对修改关闭。在设计一个模块的时候,要保证这个模块可以在不需要被修改的前提下可以得到拓展。这样做的好处就是通过拓展给系统提供了新的职责,以满足新的需求,同时又没有改变系统原来的功能。
There should never be more than one reason for a class to change.
一个类更改的原因不应该超过一个。
eg:参考下图中的设计,图形计算程序只使用了正方形的Area()方法,永远不会使用Draw()方法,而它却跟Draw方法关联了起来。这违反了单一原则,如果未来因为图形绘制程序导致Draw()方法产生了变化,那么就会影响到本来毫不关系的图形计算程序。
那么我们该怎么做呢?如下图,将不同的职责分配给不同的类,使单个类的职责尽量单一,就隔离了变化,这样他们也不会互相影响了。
Software entities like classes, modules and functions should be open for extension but closed for modification.
一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭。
eg:如下图,有一个客户端程序通过数据访问接口操作数据,对于这套系统来说,一开始计划使用的是SQL Server或Oracle数据库,但是后来考虑到成本,改用免费的mysql;那么对于客户端程序来说,后来数据的扩展对它没有任何影响,它在不知不觉间就用上了免费好用的MySQL数据库,这全要感谢OCP原则。
Functions that use use pointers or references to base classes must be able to use objects of derived classes without knowing it.
所有引用基类的地方必须能透明地使用其子类的对象。
Talk only to your immediate friends and not to strangers.
只与你的直接朋友交谈,不跟“陌生人”说话。
Clients should not be forced to depend upon interfaces that they don`t use.
The dependency of one class to another one should depend on the smallest possible.
客户端不应该依赖它不需要的接口。
类间的依赖关系应该建立在最小的接口上。
eg:参考下图的设计,在这个设计里,取款、存款、转帐都使用一个通用界面接口,也就是说,每一个类都被强迫依赖了另两个类的接口方法,那么每个类有可能因为另外两个类的方法(跟自己无关)而被影响。拿取款来说,它根本不关心“存款操作”和“转帐操作”,可是它却要受到这两个方法的变化的影响。
为每个类都单独设计专门的操作接口,使得它们只依赖于它们关系的方法,这样就不会互相影响。
High level modules should not depend upon low level modules. Both should depend upon abstractions.
Abstractions should not depend upon details. Details should depend upon abstractions.
上层模块不应该依赖底层模块,它们都应该依赖于抽象。
抽象不应该依赖于细节,细节应该依赖于抽象。
eg:参考下图的设计,一个开关跟灯直接连接在一起了,也就是说开关依赖于灯的打开和关闭方法,那么如果我想用这个开关也可以打开其他东西呢,比如电视、音响。显然这个设计是无法满足这个要了,因为我们依赖了细节而不是抽象,这个开关已经等价于“灯的开关”。
如下设计我们不仅可以打开灯,还可以打开电视和音响,甚至未来任何实现了“开关接口”的任何东西。
GoF 23种设计模式一般分为三大类。
创建型模式,就是创建对象的模式,抽象了实例化的过程。
它帮助一个系统独立于如何创建、组合和表示它的那些对象。
关注的是对象的创建,创建型模式将创建对象的过程进行了抽象,也可以理解为将创建对象的过程进行了封装,作为客户程序仅仅需要去使用对象,而不再关心创建对象过程中的逻辑
结构型模式是为解决怎样组装现有的类,设计他们的交互方式,从而达到实现一定的功能的目的。
结构型模式包容了对很多问题的解决。例如:扩展性(外观、组成、代理、装饰)封装性(适配器,桥接)。
行为型模式涉及到算法和对象间职责的分配。
行为模式描述了对象和类的模式,以及它们之间的通信模式。
行为型模式刻划了在程序运行时难以跟踪的复杂的控制流可分为行为类模式和行为对象模式
1、
2、林振华–架构设计的本质
3、软件架构–《系统架构:复杂系统的产品设计与开发》笔记
4、UML概述
5、GRASP模式概述
6、GRASP----(职责分配原则)
7、GRASP 设计原则
8、六大设计原则(SOLID)
9、软件架构–《设计模式–GoF》理解
10、软件架构(software architecture)
11、Software Architecture软件架构是啥
12、架构师之路 — 软件架构 — Overview
13、软考分类精讲-软件架构设计(一)
14、软件岗位–CTO、技术VP、技术总监、首席架构师
15、如何成为更好的软件架构师