在软件开发领域经常会接触到架构这个词汇,在我最初的印象中,架构是一个很高级的词汇。它似乎代表了复杂的工程结构、高层次的抽象设计、最新的开发语言特性等等。对于当时只专注于写业务逻辑的我来说,不免心生对架构的敬畏。工作中对架构的讨论很少,出现则是一些高级晦涩的描述,但是从来没有人清楚地解释过架构做了哪些事。所以,架构到底是什么?架构和业务之间是什么关系?
当我们看一些关于架构的书籍或者资料,不免会接触到一些对架构的定义或者描述。比如:约束、规则、边界、实体关系、模型定义等等。但是懂得这些概念并不能帮助我们设计出来更好的架构,当我们套用设计原则进行架构设计时,不免会觉得空洞乏味,总觉得少了点什么。虽然我们为架构设计做了很多事,但是似乎什么也没做。因为只针对架构设计本身来说,很难说清楚它所产生的价值。所以,好的架构设计的出发点是什么?好的架构应该是什么样的呢?
去年我有一个任务:将我们当前工程的代码进行重新的拆分和组合,以厘清模块间的关系,控制工程中模块依赖的复杂度。这看起来是一个很简单的工作,找到一个不同于当前的且更合理的目录划分方案,就可以尝试落地实施。但是这又是一个很困难的工作,因为我们首先要回答有哪些模块、模块间是什么依赖关系的问题。其实,回到任务的本身,我们并不是只想对代码文件进行重新的组织和划分,我们的目标是业务模块解耦合,定义并明确业务模块间的依赖规则。面对这样的目标,我们需要首先从业务视角更清晰地定义和划分模块,然后从工程结构视角确定模块间的关系。所以,代码目录调整实际上是一个对业务场景、工程结构理解和设计的问题。代码目录的结构代表了我们的工程结构,也是业务场景划分的抽象描述,更是模块定义以及模块依赖关系的展现。
在设计代码目录划分方案的过程中,看了一些工程结构设计的资料,读了一些关于架构设计的书。对于架构有了一些理解。本文是对这段学习和任务完成过程的思考和沉淀。我希望能够回答上面提到的几个问题:
- 架构到底是什么?架构和业务之间的关系
- 好的架构的设计出发点是什么?好的架构应该是什么样的
首先架构是一个汉语词汇。它的定义是:人们对一个结构内的元素及元素间关系的一种主观映射的产物。从这个定义可以看出,传统的架构在描述一个系统中有什么元素,以及元素之间关系。在建筑领域,架构也用于描述建筑物的结构。 作为一个计算机领域的词汇,架构的定义是:有关软件整体结构与组件的抽象描述,用于指导大型软件系统各个方面的设计。实际上也在定义有什么以及关系的问题。
无论是在建筑领域还是计算机领域,我们通常会用工程描述这类工作的项目。比如我所在的部门是工程技术中心,我是一个工程类的程序员等。我们可以称之为工程的工作项目包括:建筑工程、军事工程、水利工程、生物工程、软件工程等。而我们在完成项目的过程中,进行架构设计实际上就是推进实施工程化的一部分。那么进行工程架构设计会考虑哪些因素,它对实施工程化的作用是什么呢?
假设,读者你现在是一位建筑工程师,负责建造一栋房屋。
虽然我们没有真正的盖过房子,但是在进行房屋的整体结构设计时,你一定会关心这些:
- 房屋用途。首先要明确这栋房子是干什么用的
- 房屋层数。和用途紧密相关,不同用途的房子层数也是不一样的
- 房屋外观。定义这栋房屋应该长什么样
- 房屋的布局。定义这栋房屋应该怎么更好地被使用
等等。我们称上面这几个属性是房屋的基础能力。作为一个靠谱的建筑工程师,你一定还会着重地设计这些:
- 水电走向。这很重要。保证房屋的安全性和使用的便捷
- 承重和抗压。房屋的使用寿命很大程度上依赖于此
等等。我们称上面的这几个属性是安全性和性能。
另外一方面,你大概不会关心房屋的装修风格、地板颜色、衣柜品牌等等因素。我们称这些为应用细节。
总结来说,进行房屋的工程架构设计时更多地关系底层设计,而不在乎过多的技术细节。
所以,我们可以给架构的作用下一个定义:在明确用途的基础上定义使用的规则和约束,提供了基础的支撑能力,并保障安全性、性能和使用周期。
软件架构设计的原则和要求到目前为止,我们已经明确了在做架构设计时必须遵循的前提和原则:明确用途。此外也对架构设计提出要求:提供基础能力、保障安全性、性能等。
同样的,引申到计算机领域。当我们进行软件架构设计时也必须遵循的原则有:
1、架构设计一定要从业务场景出发
这实际上就是明确用途的大前提。架构设计一定是要从业务出发、面向业务变化的。只有在我们明确了我们的业务场景和业务目标后,在此基础上进行的架构设计才是能真正产生业务价值的。一个脱离了业务场景而设计的架构,无论多么新颖和高级,也绝不是一个好的架构。
2、架构设计一定要落到业务场景中去验证
我们不能只从基础能力、安全性或者性能方面去评判一个架构的好坏。架构对业务开发的支持能力,面向业务变化时的灵活度以及持续演进能力等都是评判的因素。 此外,我们要求软件架构必须是灵活的,能够满足未来业务持续发展的要求。
业务场景是不断变化的,架构也要具有跟随业务形态不断演进的能力。架构设计的核心是保证面向业务变化时有足够灵活的响应力,这要求架构设计能够识别到业务的核心领域。所以,无论是面向当前还是面向未来,架构设计都需要真正地识别和理解业务问题。
架构设计的原则本章节介绍几个软件架构设计时可以遵循的原则,实际上在进行功能模块设计也可以参考这些设计原则。
SRP 单一职责原则- 一个函数只负责完成一个功能
- 任何一个模块只对某一类行为者负责
- 一个类或者函数应该有且仅有一个被改变的理由
在实际的编码中,我们还是可以看到很多违反单一职责的例子的,比如超长的函数体。一个函数内做了很多事,实际上就是负责了太多的功能,很多的变更都要修改这个函数,这导致很难控制变更影响的范围。
我们可以将大函数拆分成小函数,小函数体负责的功能更加单一,相应的也会更加灵活。所以我们建议大家多写一些小的函数体。但是不要在函数拆分的过程中进行过度的封装和抽象。
OCP 开闭原则- 易于扩展,抗拒修改
模块要易于扩展,控制修改。这是我们在初学编程语言时就会被教育到的设计原则。开闭原则帮助我们设计更加灵活的模块,同时还能控制模块变更的影响范围。
LSP 里氏替换原则- 所有引用父类的地方都可以替换成子类,而行为不发生改变
使用里氏替换原则可以保证父类的复用性。它主要是用来判断抽象和继承关系设计是否合理,即某个类是否应该具有某个属性,以及一个类到底是不是另外一个类的子类。
举一个典型的例子,乘马是乘马,乘白马也是乘马,乘黑马也是乘马。那么白马和黑马就是马的子类,是符合LSP的。
下面是两个典型的违反LSP原则的例子。也是网上也特别常见的例子。
第一个是正方形不是矩形。
class Rectangle {
public:
int32_t getWidth() const {return width;}
int32_t getHeight() const {return height;}
virtual void setWidth(int32_t w) {
width = w;
}
virtual void setHeight(int32_t h) {
height = h;
}
private:
int32_t width = 0;
int32_t height = 0;
};
class Square : public Rectangle {
public:
void setWidth(int32_t w) override {
Rectangle::setWidth(w);
Rectangle::setHeight(w);
}
void setHeight(int32_t h) override {
// …
}
};
void reSize(Rectangle rect) {
while (rect.getHeight()
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?