系统架构师的工作是复杂设计总体解决方案以及领域对象的逻辑和物理布局,这是一项在复杂环境中高风险、高影响力的活动。
1、软件架构的定义:软件架构(Software Architecture)也称之为软件体系结构,它是一组有关如下要素的重要决策:软件系统的组织,构成系统的结构化元素,接口和它们相互协作的行为的选择,结构化元素和行为元素组合成粒度更大的子系统方式的选择,以及指导这一组织(元素及其接口、协作和组合方式)的架构风格的选择。换句话说,软件架构实际上是对系统整体结构设计的刻划,系统架构师是做全局的、整体的把握工作。架构的组成与决策是架构设计的两个基本概念。架构=>蓝图+规则+解决方案
软件架构是一个认识事物的过程:原型、发现、改进、再发现、再改进,这是软件开发的必由螺旋。
2、架构师成长路线图:系统架构师已经不仅仅是技术精湛的技术专家,他需要与业务团队紧密合作,并且精通市场、业务与管理。从上升趋势来说,可以有三个层面的路线图:第一个层面,要关注系统思考。在这个层面,重要的不仅仅是掌握设计的知识点,而是更重视分析能力、创新思维能力的提升,需要更广阔的思路,这方面的空间相当非常大。这是第一层面的能力基础。第二个层面,要关注总结和指导,思维空间要转向群体。如何把已有的经验总结出来,并让这种智力资产真正发挥作用?成为架构师上升第二层面的能力基础。第三个层面,要提升自身的全面修养。我们必须引发自己思维方式的变革,要培养组织力、领导力、创新力以及拥有激情,这是架构师上升第三层面的能力基础。
要看到自身的弱点,思路要宽,多思考
架构师并不是一个普通的技术人员,他对设计站的角度更高,需要的知识和能力结构更复杂,他需要具有其他人所没有的思想、眼光和感知世界的方法,必须突破已有的思维模式和行为模式,突破长期束缚自己的思维瓶颈,才可能达到自己从未达到过的高度。
架构师要养成每项工作都记录并分析的好习惯,以形成更扎实的工作风格。在每个项目完成都需要进行总结。
3、架构师要保持自己的竞争力:架构师必须关注今天的IT技术、商业模式变革以及由此引发的软件产业变革的重大趋势,勤于思考并迎接新的挑战。一个人最核心的竞争优势是学习能力。架构师作为技术层面资深的一群,为了保持竞争力需要注意以下几个问题:(1)、保持激情:关键是信念。激情源自于信念,有了信念才会主动挑战自我,迎接挑战才会有激情,有了激情工作才会更有意思。(2)、创新思考:在工作中多尝试一些新方法,是维持自我能力的重要手段。(3)、逆向思维:逆向思维指的是使用与正常思路相反的思维方式去分析同一个问题,使思路多样化。逆向思维能够帮助人们冲破传统思维的束缚,克服惯性思维方式。从反方向考虑问题往往会取得出人意料的结果。
4、架构师要关注软件的新趋势:目前传统软件危机暴露出的问题还未真正解决,新的挑战却已摆在眼前。在人们不断思考面临的挑战以及对策中,形成了一些新的趋势,包括:(1)、软件质量以服务质量形式展现,对质量的投资可获得更高的投资回报。(2)、软件过程扩展到用户,希望更多的用户深入参与到软件全生命周期。(3)、功能至上远远不够,用户体验得到空前重视。(4)、系统集成模式面临变革,软件、服务、终端、IT基础设施将形成更紧密的价值体系。(5)、研发要更多关注非功能性需求,如安全性质量、性能、可靠性、可扩充性、可伸缩性、可用性等,从而不断提高软件的价值。
知识就是力量==>信息就是力量
架构并不完全是概要设计。概要设计还是停留在图纸上,而架构必须证明这个技术路线可行,并且能够证明大多数质量风险已经得到了解决。
5、所谓设计就是解决问题的过程:软件设计是一种思维活动,设计的魅力在于破解难题,通过直面问题的挑战,以及对相应解决方案的仔细推敲,才可能设计出真正有灵性的产品。(1)、设计不具普遍性:软件设计很少具有普通性,不同的目标需要不同的设计来支持。(2)、做出权衡:所谓软件设计,本质上就是在质量、成本、时间以及其它各种因素之间做出权衡。(3)、记录设计的理由(设计文档)。
多关注各种方面的架构设计
6、质量属性决定了架构风格:一种架构的风格,很大程度上与设计者如何满足质量要求的对策有关。需求的功能和非功能两方面都可能有质量要求。具体归纳如下:(1)、与功能性有关的质量属性主要包括:A、正确性:是指软件按照需求正确执行任务的能力。B、健壮性:指的是在异常情况下,软件能够正常运行的能力。正确性与健壮性的区别在于,前者是在功能需求之内描述问题,后者是在功能需求之外描述问题。健壮性一般有两层含义:首先是容错能力,其次是恢复能力。容错指的是发生异常情况不出错误的能力,而恢复指的是软件发生错误以后能恢复到没有发生错误钱的状态的能力。C、可靠性:是一个与时间相关的属性,指的是在一定的环境下,在一定的时间段,系统不出现故障的概率。通常用平均无故障时间来衡量。(2)、与非功能性有关的质量属性主要包括:A、性能:是指软件的“时间-空间”效率,而不仅仅是指软件运行速度。换句话说是速度要快而占用资源要少。性能=速度/资源。B、易用性:指的是用户使用软件的容易程度。C、清晰性:意味着工作成果易读、易理解。D、安全性:它的目的是系统应该具备防止非法入侵的能力,这既属于技术问题也属于管理问题。E、可扩展性:这反映软件适应“变化”的能力,包括需求、设计的变化、算法的改进和变化。F、可移植性:指的是软件不经修改(或者稍加修改)就可以在不同软硬件环境中使用的能力。
7、抵制前期进行庞大设计的诱惑:(1)、架构应该具备易演化特征;(2)、项目开发周期不要超过6个月;(3)、分而治之:抓住真正的需求、分而治之(把大项目分成小项目)、设置优先级、尽快交付;(4)、增量式开发与交付:如果前期需求比较清楚,可以把一个大项目分成若干相对独立能够持续交付的部分,这样就可以把大问题分成若干小问题;(5)、迭代式开发与交付:如果前期需求不是太清楚,项目带有强烈的创新成分,可以使用具有强迭代的逐步求精的模型。
8、重构:在不影响整体外部行为的前提下,不断地对软件进行细微的设计改进,这种渐进式的实践叫做重构。通过重构不仅能够降低维护成本,而且也为我们提供了改进代码质量的通用标准,并使我们能迅速添加新功能。从本质上说,重构根本上就是一个态度问题,而不全是技术问题。
在集中精力完成了代码逻辑以后,就需要集中精力做第二件事情,那就是重构。在对代码进行重构时,我们不会增加新功能,甚至也不会去修复bug。相反,我们会通过将代码变得更易于理解来提升代码的可读性。
重构要坚持不懈:(1)重构可以加快进度;(2)、重构应该是小步骤地进行;(3)、技术债务积累越多,重构的难度就越大。
9、对结构进行优化的基本原则:在完成了功能逻辑之后,除了代码重构以外,很多情况下还需要重新审视一下软件结构,对结构进行重构。良好的结构设计需要遵循一些原则,而原则本身就是经验的总结。依据这些原则,我们就可以在设计中有良好的设计指向。如需求不变则不需结构。
结构的4条设计原则:(1)单一职责原则(SRP):也被称之为内聚性原则;SRP原则的描述为:就一个类而言,应该仅有一个引起它变化的原因;(2)、开放–封闭原则(OCP):OCP的关键是依赖于抽象。OCP原则的目的,是要求我们设计的软件实体(类、组件、函数等等)应该是可以扩展的,但是不可修改的。A、对于扩展是开放的:这意味着组件的行为是可以扩展的,当应用的需求改变时,我们可以对组件进行扩展,使其具有满足那些改变的新行为。换句话说我们可以改变组件的功能。B、对于更改是封闭的:对组件行为进行扩展时,不必改动组件的源代码,无论是动态链接库、DLL或者是Java的jar文件都无需改动。(3)、依赖倒置原则(DIP):使用传统的结构化设计所创建出来的依赖关系结构,策略是依赖于细节的,这是糟糕的,因为这样会使策略受到细节改变的影响。面向对象的程序设计倒置了依赖关系结构,使得细节和策略都依赖于抽象,并且常常是客户拥有服务接口。事实上,这种依赖关系的倒置正是好的面向对象设计的标志所在。DIP的原则是:A、高层组件不应该依赖于低层组件。二者都应该依赖于抽象;B、抽象不应该依赖于细节,细节应该依赖于抽象。(4)、接口隔离原则(ISP):这个原则用来处理“胖(fat)”接口所具有的缺点。类的“胖”(不内聚)接口可以分解成多组方法。每一组方法都服务于一组不同的客户程序。这样,一些客户程序可以使用一组成员函数,而其它客户程序可以使用其它组的成员函数。实际中当然也存在有一些对象,它们确实不需要内聚的接口,但是ISP建议客户程序不应该看到它们作为单一的类存在。相反,客户程序看到的应该是多个具有内聚接口的抽象基类。
10、关注变化、关注特征:拥抱着变化而设计。让变化成为一个重要的设计要素,需求总是会发生变化。面向对象是个思维方式。基于接口进行设计。
软件复用(SoftwareReuse):是将已有软件的各种有关知识用于建立新的软件,以缩减软件开发和维护的花费。软件复用是提高软件生产力和质量的一种重要技术。早期的软件复用主要是代码级复用,被复用的知识专指程序,后来扩大到包括领域知识、开发经验、设计决定、体系结构、需求、设计、代码和文档等一切有关方面。
软件重用,是指在两次或多次不同的软件开发过程中重复使用相同或相似软件元素的过程。软件元素包括程序代码、测试用例、设计文档、设计过程、需要分析文档甚至领域知识。通常,可重用的元素也称作软构件,可重用的软构件越大,重用的粒度越大。
11、面向服务的架构(Service-OrientedArchitecture, SOA):面向服务的架构成功的要点是服务识别。服务识别的基本过程:(1)、了解项目的性质;(2)、业务牵头,一定不是技术牵头;(3)、明确产品的战略目标;(4)、研究企业业务流程;(5)、重用行业制品;(6)、建立契约基线;(7)、完善服务属性等级矩阵(ARMS);(8)、使用业务敏捷场景仿真(BASS)进行测试;(9)、拥抱变更。
12、架构与框架的区别:框架是一个软件,但架构不是软件,而是关于软件如何设计的重要决策。但是在引入软件框架以后,软件架构决策往往会体现在框架设计之中。不论是架构技术还是框架技术,都是为了解决软件日益复杂所带来的困难,而采取的“分而治之”的结果。架构的思维是先大局后局部,这是一种问题在抽象层面地解决方案,首先考虑大局而忽略细节。框架的思维是先通用后专用,这是一种半成品,还需要通过后期的定制才能成为具体的软件。
框架和架构的关系可以总结为两个方面:(1)、为了尽早验证架构设计,或者出于支持产品线开发的目的,可以把通用机制甚至整个架构以框架方式实现;(2)、企业可能存在大量可重用框架,这些框架可能已经实现了架构所需的重要机制,或者对某个子系统提供了可扩展的半成品,最终软件架构可以借助这些框架来构造。
框架设计最重要的部分,其实并不在于采用何种技术方案来实现,而是对已经存在的业务过程进行深入思考,寻找它们的共性,探究其中的规律,建立恰当的模式,然后选择恰当的技术实现方案。
带团队关键因素:决心、手段(方法)、激情、信心,技术不是关键因素。
13、把经验归纳总结成理论:总结是两方面的,(1)总结过程:归纳出良好设计必须遵循的步骤,以及每一步骤的目标、解决什么问题、应该有什么样的思考点和思考方向。(2)总结模式(设计模式):模式是一种实践经验的总结,通过总结每个思考点的各种解决方案,并且和过程的节点装配在一起,形成能够指导他人的模式语言。
目前大约有110多种设计模式,模式不是教条,模式仅仅是经验的总结
模式是一种经过命名的对于解决方案的格式化经验总结,它能有效地帮助我们归纳和使用经验。
14、在软件设计中,宏观上看自上而下大致上可以分成三个大的过程域,它包括:顶层架构设计过程域;领域对象设计过程域;资源应用设计过程域,图1。
顶层架构设计过程域的目标是:建立一个系统的概念性架构,为系统描绘一个初始的轮廓,并且对涉及全局的问题进行决策。
领域对象设计过程域的目标是:根据细化的领域对象模型,在细节层面对每一个领域对象进行刻画,并且对于这些细节层面的技术问题进行决策。
资源应用设计过程域的目标是:在宏观和微观两个层面,对于涉及内存、对象、数据等方面的方案和问题进行决策。
为了进一步的细化,在每个过程域内部,又可以包括一系列的过程,(用于解决一个特定的主题和问题,被称之为过程)。这样一来,宏观设计过程就可以分成11组内聚的过程(问题域)。要注意到尽管过程的描述是清晰分离的,但整个软件的设计并不仅仅是自上而下逐步求精的过程,而是相互影响、互相支持、在设计过程中不断优化的不断反馈过程,图2。
设计模式汇总:
Domain Model:定义了一个应用领域结构和工作流的精确模型,其中还包括它们的变化。
Layers:解决系统合理分层的问题。
Model-View-Controller:解决对用户界面变化的支持问题。支持某一特定用户界面的变化。
Presentation-Abstraction-Control:解决相同业务具有多种表现形式问题。
Microkernel:解决业务具有多种不同业务方法的问题。
Refelection:解决需要动态改变软件系统结构和行为的问题。
Pipes and Filters:解决算法的结构化并可以重新构建的问题。
Shared Repository:适用于网络管理和控制系统领域
Blackboard:解决运行中智能化改进处理方法的问题。
Domain Object:表现为已经将自我完备的连贯功能和基础性责任封装成定义良好的实体,通过一个或多个”显示接口”提供功能,并隐藏内部结构和实现。
Messaging:由一系列相互连接的MessageChannel和Message Router管理着跨网络的不同服务间的消息交换。
Message Channel:解决如何把彼此协作的客户端和服务连接起来的问题。
Message Router:解决如何根据条件接受”信道”消息的问题。
Message Translator:解决如何转换消息格式的问题。
Message Endpoint:解决把数据转换为消息中间件能够理解的形式的问题。
Publisher-Subscriber:为了在应用中更好的把彼此关注的事件通知给其它领域对象。
Broker:通过一个代理管理器管理领域对象间远程互操作的各个关键方面。
Client Proxy:解决客户端应用与网络基础设施相互屏蔽的问题。
Requestor:解决应用代码被基础设施的代码污染而影响可移植性的问题。
Invoker:解决服务代码被基础设施的代码污染而影响可移植性的问题。
Client Request Handler:解决客户端应用与通信相互影响的问题,它封装了客户端在统一的接口背后进行的进程间通信的细节。
Server Request Handler:解决服务端应用与通信相互影响的问题,封装了服务器端在统一的接口背后进行的进程间通信的细节。
Reactor:解决在应用中避免使用多线程的问题。
Proactor:解决在多线程的背景下出现性能问题的缺陷。
Acceptor-Connector:把事件初始化与具体处理方法分离,从而提高可维护性。
Asynchronous Completion Token:解决异步到达的事件仍然能按一定顺序处理的问题。
Explicit Interface:解决如何正确设计接口的问题。
Extension Interface:随着时间的推移,组件的接口是会膨胀的,一个胖的接口将更脆弱。解决防止”胖”接口并分离接口。
Introspective Interface:解决公开内部信息接口的问题。
Dynamic Invocation Interface:解决同一个接口允许客户端调用多种方法的问题。
Proxy:解决在同一个接口下通过代理屏蔽某些实现的问题。
Business Delegate:由本地业务代表来完成所有网络任务,分离了应用和网络处理的业务,减少了开发难度、提高了可理解性和可维护性。
Facade:解决屏蔽子系统的变化辐射到高层应用的问题。
Combined Method:解决多种相互关联的方法不合理的分布的问题。
Iterator:解决分布式元素能够方便迭代的问题。
Enumeration Method:解决减少外部迭代方式多次对聚合中的元素进行独立访问开销的问题。
Batch Method:解决多次访问加大网络开销的问题。
Encapsulated Implementation:解决对象划分的基本原则和方法问题。
Composite:建立一种结构灵活的树状结构对象组织形式,形成“整体/部分”层级结构。
Half-Object plus Protocol:通过在分布式系统中合理布局对象,以减少不合理的网络流量和服务器压力。
Replicated Component Group:解决分布式系统容错的问题,复制的组件实现位于不同的网络节点,并组成一个组件组。
Half-Sync/Half-Async:对并发系统中的异步和同步服务处理解耦合以简化编程,但又不会过度地影响性能。
Leader/Followers:解决大批量小处理的环境下减少并发线程应用的问题。
Active Object:为了减少服务器并发线程应用。
Monitor Object:解决并发业务相互协调的问题。
Guarded Suspension:在并发性程序中,当某个线程对一个资源进行访问的时候,首先需要判断这个资源的警戒条件是否成立。
Future:并发调用的服务可能需要向客户端返回结果。
Thread-Safe Interface:避免自死锁和加锁开销。
Strategized Locking:在创建或声明时,为组件配置适当类型的锁实例。使用该锁实例来保护组件中的所有临界区。
Scoped Locking:解决复杂繁琐代码中的疏忽发生漏释放造成死锁的问题。
Thread-Specific Storage:解决频繁使用对象造成反复加锁解锁造成的性能问题。
Copied Value:解决共享的值对象必须锁定带来的性能问题。
Immutable Value:解决共享的值对象必须锁定带来的性能问题。
Observer:定义一个特定的更新接口,通过该接口,Observer获得Subject状态变更的通知。
Double Dispatch:根据运行时多个对象的类型确定方法调用的过程。
Mediator:封装集合中所有对象的聚合协作行为,从而将这些对象解耦合。
Command:为这些对象定义一个通用接口,来执行它们所代表的请求。
Memento:解决在不破坏封装性的前提下正确存储和读取分布式对象状态的问题。
Context Object:解决在松耦合系统中共享与程序执行上下文相关的通用信息的问题。
Data Transfer Object:解决细粒度调用多次访问远程对象单个属性所带来的巨大开销问题。
Message:解决网络协议只支持比特流这种最简单的数据传输形式,并不能识别服务调用和数据类型的问题。
Bridge:解决在下层稳定的业务中嵌入上次变化部分的问题。
Object Adapter:解决接口变化导致的不兼容问题。
Chain of Responsibility:解决对象结构和请求分发逻辑上的变化影响到客户端的问题。
Interceptor:解决构建一个可插拔的框架变化模型的问题。
Visitor:解决将服务的实现分散在定义对象结构的各个类中难以进行集中处理的问题。
Decorator:解决在稳定的核心功能外围添加扩展的问题。
Template Method:解决在下层稳定的业务中嵌入上次变化部分的问题。
Strategy:解决在一个或多个方法中根据不同的情况执行不同行为的问题。
Wrapper Facade:主要解决应用代码使用底层API所提供的服务但代码难以理解的问题,需要对底层API进行面向对象的封装,通过提供一个简洁的、健壮的、可移植的、内聚的面向对象的接口,来达到封装函数和数据的目的。
Declarative Component Configuration:建立需要安装各类插件的宿主基础设施,使其能够正确管理运行时环境,可靠运用系统资源和服务的问题。
Container:解决领域对象直接处理平台环境造成它与平台紧密耦合并增加实现的复杂性的问题。
Component Configurator:解决在组件生命周期后期和升级时重新配置组件的问题。
Object Manager:解决客户端依赖对象管理增加应用内部的耦合度和复杂度的问题。
Virtual Proxy:解决从一个巨大数据库中把所有的对象全部加载进来消耗大量资源的问题。
Resource Pool:解决获取和释放资源(网络连接、线程或者内容)引入一定的性能开销问题。
Resource Cache:解决几个有限的资源用户频繁创建和释放资源带来不必要的性能开销问题。
Automated Garbage Collection:解决不能及时将不再使用的内存收回可能耗尽内存的问题。
Counting Handles:解决确保在堆上创建的共享对象能够可靠地、安全地、及时地回收的问题。
Abstract Factory:解决一批对象用统一的方法进行创建和销毁的问题。
Builder:解决对需要多步完成对象的创建时,简化创建过程的复杂性和多样性问题。
Factory Method:解决直接创建对象可能导致代码的混乱并影响调用端代码的独立性问题。
Disposal Method:解决销毁对象时可能需要多个步骤而引人过度的耦合问题。
Database Access Layer:它通过在两种之间引人一个映射层将面向对象应用设计同关系型数据库分离开。
Data Mapper:解决数据模型和持久化的表结构之间完全的解耦合的问题。
Row Data Gateway:解决更细致的数据模型和持久化的表结构之间完全解耦的问题。
Table Data Gateway:解决更细致的数据模型和持久化的表结构之间完全解耦的问题。
Active Record:解决降低应用中面向对象数据模型与数据库中表结构之间的耦合的问题。
往期回顾: