系统设计

系统设计

架构设计

软件体系结构风格是描述某一特定应用领域中系统组织方式的惯用模式。体系结构风格定义一个系统家族,即一个体系结构定义一个词汇表和一组约束

  • 词汇表中包含一些构件和连接件类型,
  • 而这组约束指出系统是如何将这些构件和连接件组合起来的。

体系结构风格反映了领域中众多系统所共有的结构语义特性,并指导如何将各个模块和子系统有效地组织成一个完整的系统。

经典架构风格

数据流风格

批处理 每个处理步骤是一个独立的程序,每一步必须在前一步结束后才能开始,
数据必须是完整的,以整体的方式传递。
它的基本构件是独立的应用程序,连接件是某种类型的媒介
批处理前后构件不一定有关联,但必须前一个行完才能执行下一个
经典数据处理、
程序开发、
Windows 下的 BAT 程序
管道-过滤器 每个构件都有一组输入和输出,构件读输入的数据流,经过内部处理,然后产生输出数据流
这个过程通常通过对输入流的变换及增量计算来完成,所以在输入被完全消费之前,输出便产生了
基本构件是过滤器,连接件是数据流传输管道
UNIX shell 编写的程序。
传统的编译器
(包括词法分析、语法分析、语义分析和代码生成) 一个阶段的输出是另一个阶段的输入。

两者的区别:

批处理每一个步骤都是独立的,每一步必须在前一步结束后才能开始,数据必须是完整的,以整体的方式传递。

管道过滤器 每个构件都有一组输入和输出,前一个输出作为后一个输入, 数据管道过滤器中流动的这个过程就是对输入流的变换及增量计算 ,在输入被完全消费之前,输出便产生了

调用/返回风格

调用-返回风格在系统中采用了调用与返回机制。利用调用-返回实际上是一种分而治之的策略,主要思想是将一个复杂的大系统分解为若干个子系统,降低复杂度,增加可修改性。

主程序/子程序 主程序/子程序风格一般采用单线程控制,把问题划分为若干处理步骤,构件即为主程序和子程序
子程序通常可合成为模块。过程调用作为交互机制,即充当连接件
面向对象 面向对象系统风格建立在数据抽象和面向对象的基础上,数据的表示方法和它们的相应操作封装在一个抽象数据类型或对象中。这种风格的构件是对象
层次型 每一层为上层服务,并作为下层的接口。仅相邻层间具有层接口,
允许每层用不同的方法实现,为软件重用提供了强大的支持。
连接件由通过层间交互协议来定义

以数据为中心

仓库 存储和维护数据的中心场所。
中央数据结构(说明当前数据状态) 和一组独立构件(对中央数据进行操作)组成,
连接件即为仓库与独立构件之间的交互
集成IDE
黑板体 是一种问题求解模型,是组织推理步骤、控制状态数据和问题求解之领域知识的概念框架。
黑板系统中,它的这个中心不叫做数据库了,而是黑板,它周围的这些构件儿都叫做知识源
适合解决没有确定性算法的这一类问题
组成:各种黑板、知识源、控制模块
信号处理领域,如语音识别和模式识别

独立构件风格

独立构件体系结构风格强调系统中的每个构件都是相对独立的个体,它们之间不直接通信,以降低耦合度,提升灵活度。

进程通信 消息传递的方式可以是点到点、异步或同步方式及远程过程调用(RPC)等。
构件是独立的过程,连接件是消息传递。
事件系统体 构件不直接调用一个过程,而是触发或广播一个或多个事件。
系统中的其他构件中的过程在一个或多个事件中注册,当一个事件被触发,系统自动调用在这个事件中注册的所有过程。

虚拟机风格

虚拟机体系结构风格基本思想是人为构建一个运行环境,可以解析与运行自定义的一些语言,增加架构的灵活性。

解释器 通常被用来建立一种虚拟机以弥合程序语义与硬件语义之间的差异
业务灵活组合那就是选择解释器风格。
解释器缺点是执行效率较低。
专家系统
用户自行定义游戏对象
规则系统体 包括知识库、规则解释器、规则/数据选择器及工作内存(程序运行存储区), 电商促销、AI围棋

C2 风格

C2风格通过连接件连接构件或某个构件组,构件与构件之间无连接,

image-20240423012545958

面向服务架构

SOA是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种这样的系统中的服务可以一种统一和通用的方式进行交互。

SOA 的设计原则

  • 无状态。以避免服务请求者依赖于服务提供者的状态。
  • 单一实例。避免功能冗余。
  • 明确定义的接口。使用者依赖服务规约调用服务,所以服务定义必须长时间稳定,一旦公布,不能随意更改;服务的定义应尽可能明确,减少使用者的不适当使用;不要让使用者看到服务内部的私有数据。
  • 自包含和模块化。服务封装了那些在业务上稳定、重复出现的活动和组件,实现服务的功能实体是完全独立自主的,独立进行部署、版本控制、自我管理和恢复。
  • 粗粒度。服务数量不应该太大,依靠消息交互而不是远程过程调用(RPC),通常消息量比较大,但是服务之间的交互频度较低。
  • 服务之间的松耦合性。服务使用者看到的是服务的接口,其位置、实现技术和当前状态等对使用者是不可见的,服务私有数据对服务使用者是不可见的。
  • 重用能力。服务应该是可以重用的。
  • 互操作性、兼容和策略声明

SOA 主要协议和规范:

SOAP 简单对象访问协议,Simple Object Access Protocol 。 SOAP是在分散或分布式的环境中交换信息的简单的协议

WSDL :Web服务描述语言,Web Services Description Language , 是一个用来描述Web服务说明如何与Web服务通信的XML语言。可描述Web服务的三个基本属性。

  • 服务做些什么——服务所提供的操作(方法)。
  • 如何访问服务——和服务交互的数据格式以及必要协议。
  • 服务位于何处——协议相关的地址,如 URL。

UDDI :统一描述、发现和集成,Universal Description Discovery and Integration 。 UDDI 计划是一个广泛的、开放的行业计划,它使得商业实体能够彼此发现;定义它们怎样在Internet上互相作用,并在一个全球的注册体系架构中共享信息。包含了服务描述与发现的标准规范

WSDL用来描述服务,UDDI用来注册和查找服务,而SOAP作为传输层,用来在消费这和服务者之间传送消息

一个消费者可以在UDDI注册表查找服务,取得服务的WSDL描述,然后通过SOAP来调用该服务。企业也可以在UDDI上面注册服务。

image-20230919102448227

服务注册表模式

服务注册表模式,支持如下SOA治理功能:

  1. 服务注册:应用开发者,也叫服务提供者,向注册表公布他们的功能。他们公布服务合同,包括服务身份、位置、方法、绑定、配置、方案和策略等描述性属性。
  2. 服务位置:也就是服务应用开发者,帮助他们查询注册服务,寻找符合自身要求的服务。注册表让服务的消费者检索服务合同。对谁可以访问注册表,以及什么服务属性通过注册表暴露的控制
  3. 服务绑定:服务的消费者利用检索到的服务合同来开发代码,开发的代码将与注册的服务绑定、调用注册的服务以及与它们实现互动。

image-20230910220855824

企业服务总线ESB

其思想是提供一种标准的软件底层架构,各种程序组件能够以服务单元的方式“插入”到该平台上运行,并且组件之间能够以标准的消息通信方式来进行交互。

它的定义通常如下:企业服务总线是由中间件技术实现的支持面向服务架构的基础软件平台,支持异构环境中的服务以基于消息和事件驱动模式的交互,并且具有适当的服务质量和可管理性。

包括:客户端(服务请求者)、基础架构服务(中间件)、核心集成服务(提供服务)。
ESB 的核心功能如下:

  1. 提供位置透明性的消息路由和寻址服务
  2. 提供服务注册和命名的管理功能。
  3. 支持多种消息传递范型(如请求/响应、发布/订阅等)。
  4. 支持多种可以广泛使用的传输协议
  5. 支持多种数据格式及其相互转换
  6. 提供日志和监控功能
image-20240428170224190

一个典型的在 ESB环境中组件之间的交互过程是:( 这种交互过程不再是点对点的直接交互模式,而是由事件驱动的消息交互模式。 )

  • 首先由服务请求者触发一次交互过程,产生一个服务请求消息,并将该消息按照ESB 的要求标准化,
  • 然后标准化的消息被发送给服务总线。 ESB 根据请求消息中的服务名或者接口名进行目的组件查找,将消息转发至目的组件,并最终将处理结果逆向返回给服务请求者。

通过这种方式, ESB 最大限度上解耦了组件之间的依赖关系,降低了软件系统互连的复杂性。连接在总线上的组件无需了解其他组件和应用系统的位置及交互协议,只需要向服务总线发出请求,消息即可获得所需服务。服务总线事实上实现了组件和应用系统的位置透明和协议透明。技术人员可以通过开发符合 ESB标准的组件(适配器)将外部应用连接至服务总线,实现与其他系统的互操作。同时, ESB 以中间件的方式,提供服务容错、负载均衡、 QoS 保障和可管理功能。

微服务

优点

  • 复杂应用解耦:构将单一模块应用分解为多个微服务,每个服务专注于单一功能。同时保持总体功能不变。避免了复杂度的不断积累
  • 独立 每个微服务可进行独立开发与部署,某个微服务发生变更时无需编译、部署整个系统应用
  • 技术选型灵活 每个开发团队可根据自身应用的业务需求发展状况选择合适的体系架构与技术
  • 容错 在微服务架构下,由于各个微服务相互独立,故障会被隔离在单个服务中,不会造成全局应用系统瘫痪
  • 松耦合,易扩展 微服务架构中每个服务之间都是松耦合的,可以根据实际需求实现独立扩展,体现微服务架构的灵活性

微服务架构面临的问题与挑战

  • 微服务架构的分区数据库体系,不同服务拥有不同数据库,不得不放弃传统数据库的强一致性,转而追求最终一致性、
  • 服务发现与服务调用链跟踪变得困难
  • 性能问题:由于微服务注重独立性,互相通信时只能通过标准接口,可能产生延迟或调用出错。

MDA

MDA的3种核心模型:

  • 平台独立模型(PIM) 【平台无关模型】:具有高抽象层次、独立于任何实现技术的模型。
  • 平台相关模型(PSM)【平台相关模型】:为某种特定实现技术量身定做,让你用这种技术中可用的实现构造来描述系统的模型。PIM会被变换成一个或多个PSM。
  • 代码Code:用源代码对系统的描述(规约)。每个PSM都将被变换成代码。

架构评估

业界已开发出多种软件架构评估的方法,按基于的技术手段来看,可以分为三类:基于调查问卷或检查表的方式、基于场景的方式和基于度量的方式。

  • 基于调查问卷或检查表的方式:该方式的关键是要设计好问卷或检查表,它充分利用系统相关人员的经验和知识,获得对架构的评估。其缺点是在很大程度上依赖于评估人员的主观推断

  • 基于度量的方式:制定一些定量值来度量架构,如代码行数等。要制定质量属性和度量结果之间的映射

  • 基于场景的方式 : 包括架构权衡分析法(Architecture Tradeoff Analysis Method,ATAM)和软件架构分析方法(Software Architecture Analysis Method,SAAM),以及CBAM(Cost Benefit Analysis Method)成本收益分析方法。通过分析软件架构对场景(也就是对系统的使用或修改活动)的支持程度,从而判断该架构对这一场景所代表的质量需求的满足程度。

场景

  • 刺激源:谁造成的刺激
  • 刺激:一个响应系统的情况
  • 制品:系统被刺激的部分
  • 环境:刺激发生时,系统所处的状态
  • 响应:刺激所产生的结果
  • 响应度量指标:如何评估响应
image-20240423103433910

SAAM、ATAM

SAAM ATAM ( 架构权衡分析方法 ) CBAM ( 成本收益分析方法 )
描述 最初它用于比较不同软件体系的架构,以分析系统架构的可修改性,后来实践证明它也可用于其他质量属性 是 在SAAM 的基础上发展起来的,主要针对性能、实用性、安全性和可修改性,在系统开发之前,对这些质量属性进行评价和折中 CBAM 用来对架构设计决策的成本和收益进行建模,它的基本思想是架构策略影响系统的质量属性,反过来这些质量属性又会为系统的项睦干系人带来一些收益(称为“效 用 ”)
特定目标 SAAM 的目标是对描述应用程序属性的文档,验证基本的架构假设和原则 ATAM 的目标是在考虑多个相互影响的质量属性的情况下,从原则上提供一种理解软件架构的能力的方法 在 ATAM 评估结果的基础上对架构的经济性进行评估
活动 形成场景、架构描述、 对场景的分类和确定优 、单个场景评估、评估场景交互作用、 总体评估 场景和需求收集、架构视图和场景实现、属性模型构造和分析、折中 整理场景、对场景进行细化、确定场景优先级、分配效用、 确定期望的质量属性响应级别的效用、 计算各架构策略的总收益、 根据受成本限制影响的投资收益率选择架构策略
评估技术 场景技术 场景技术 场景技术
质量属性 可修改性是 SAAM 分析的主要质量属性 分析多个相互竞争的质量属性 经济性进
image-20230911171859154

处理流程设计

工作流管理系统

工作流管理系统(Work Flow Management System,WFMS)通过软件定义、创建工作流并管理其执行

WFMS 的基本功能

  • 对工作流进行建模。即定义工作流,包括具体的活动和规则等,所创建的模型是同时可以被人和计算机所“理解”的,工作流对应现实世界的业务处理过程,不能改变真实业务的处理逻辑。
  • 工作流执行。遵循工作流模型来创建和执行实际的工作流,即通过WFMS 可以执行多个工作项。
  • 业务过程的管理和分析。监控和管理执行中的业务(工作流),例如,进度完成情况和数据所处状态、工作分配与均衡情况等。

工作流参考模型(Work flow Reference Model , WRM ) 包含 6 个基本模块,分别是

  • 工作流执行服务: 【核心模块】,功能包括【创建和管理流程定义】,创建、管理和执行流程实例。
  • 工作流引擎:是为流程实例【提供运行环境】,并解释执行流程实例的软件模块。
  • 流程定义工具:是【管理流程定义的工具】,它可以通过图形方式把复杂的流程定义显示出来并加以操作,流程定义工具与工作流执行服务交互,一般该模块为设计人员提供图形化的用户界面。
  • 客户端应用:是通过请求的方式与工作流执行服务交互的应用。
  • 调用应用:是【被工作流执行服务调用的应用】,调用应用与工作流执行服务交互。
  • 管理监控工具:主要指组织机构和参与者等数据的维护管理和流程执行情况的监控,管理监控工具与工作流执行服务交互。

image-20240423150002697

流程设计工具

程序流程图

程序流程图(Program Flow Diagram,PFD ) 用一些图框表示各种操作,它独立于任何一种程序设计语言,比较直观、清晰,易于学习掌握。流程图中只能包括图13-2所示的 5 种基本控制结构,任何复杂的程序流程图都应由这5 种基本控制结构组合或嵌套而成。

img

N-S图

用方框代替传统的程序流程图(PFD)。

在 N-S 图中也包括5 种控制结构,分别是顺序型选择型WHILE 循环型(当型循环)、UNTIL 循环型(直到型循环)和多分支选择型

img

问题分析图

问题分析图(Problem Analysis Diagram,PAD ) 是继PFD 和 N -S 图之后,又一种描述详细设计的工具,它由日立公司于1979年提出,也是一种支持结构化程序设计的图形工具。PAD 也包含 5 种基本控制结构,并允许递归使用

img

IPO图

IPO图用来描述每个模块的输入、输出数据加工

img

结构化设计

结构化设计(Structured Design , 以下简称SD ) 是一种面向数据流的方法,它以SRS 代表的软件需求规格说明(Software Requirements Specification), SA 代表的系统分析(System Analysis)阶段所产生的数据流图数据字典等文档为基础,是一个自顶向下、逐步求精和模块化的过程。SD 方法的基本思想是将软件设计成由相对独立且具有单一功能的模块组成的结构,分为概要设计详细设计两个阶段

概要设计

概要设计又称为系统总体结构设计,它是系统开发过程中很关键的一步,其主要任务是将系统的功能需求分配给软件模块,确定每个模块的功能和调用关系,形成软件的模块结构图,即系统结构图。在概要设计中,将系统开发的总任务分解成许多个基本的、具体的任务。确定每个模块的功能、接口和模块之间的调用关系

概要设计的工具:系统结构图 ( 模块结构图 )

系统结构图 :反映系统的功能实现模块之间的联系与通信,包括各模块之间的层次结构,即反映了系统的总体结构

详细设计

详细设计的工具:具体的图形有业务流图、程序流程图 ( 只能描述执行过程而不能描述有关的数据 ) 、PAD图、N S 流程图

详细设计:为每个具体任务选择适当的技术手段处理方法 ,为每个模块设计实现的细节

模块的四个要素:

  • 输入和输出:模块的输入来源和输出去向都是同一个调用者,即一个模块从调用者那儿取得输入,进行加工后再把输出返回调用者

  • 处理功能:指模块把输入转换成输出所做的工作。

  • 内部数据:指仅供该模块本身引用的数据。

  • 程序代码:指用来实现模块功能的程序。

结构化设计原则

模块独立性原则 ( 高内聚、低耦合)

保持模块的大小适中

扇入/扇出系数合理 (多扇入,少扇出)

  • 扇出表示调用了其他模块,扇出越多,表示模块复杂度越高,耦合度越高。
  • 扇入表示被几个模块调用,扇入越多,表示模块的复用做的越好

深度和宽度均不宜过高

image-20240423160335834
内聚类型 描 述
功能内聚 完成一个单一功能,各个部分协同工作,缺一不可
顺序内聚 处理元素相关,而且必须顺序执行
通信内聚 所有处理元素集中在一个数据结构的区域上
过程内聚 处理元素相关,而且必须按特定的次序执行 强调的是 特定的序列,可以不是顺序
瞬时内聚(时间内聚) 所包含的任务必须在同一时间间隔内执行 变量初始化
逻辑内聚 完成逻辑上相关的一组任务 把一件事情的各个流程合并成一个大的模块。
例如:把添加购物车、提交订单、付款等几个步骤合并成“购物”模块
偶然内聚(巧合内聚) 完成一组没有关系或松散关系的任务
耦合类型 描 述
非直接耦合 两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的
数据耦合 —组模块借助参数表传递简单数据
标记耦合 —组模块通过参数表传递记录信息 ( 数据结构)
控制耦合 模块之间传递的信息中包含用于控制模块内部逻辑的信息
外部耦合 一组模块都访问同一全局简单变量, 而且不是通过参数表传递该全局变量的信息
公共耦合 多个模块都访问同一个公共数据环境
内部耦合 一个模块直接访问另一个模块的内部 ;
两个模块有一部分程序代码重叠
一个模块不通过正常入口转到另一个模块的内部
一 个模块有多个入 口

面向对象设计

设计软件类

在系统设计过程中,类可以分为三种类型:实体类边界类控制类

实体类映射需求中的每个实体,实体类保存需要存储在永久存储体中的信息

控制类是用于控制用例工作的类

  • 应用逻辑、业务逻辑、数据处理逻辑
  • “动词+名词”或 “名 词+动词” 例如:身份验证器

边界类用于封装在用例内、外流动的信息或数据流

  • 窗体、报表、打印机和扫描仪等硬件的接口
  • 菜单、购物车、报表、二维码
  • 窗口、通信协议、打印机接口、传感器和终端

面向对象设计的原则

  • 单一职责原则:设计目的单一的类

  • 开放-封闭原则:对扩展开放,对修改封闭

  • 里氏(Liskov)替换原则:子类可以替换父类

  • 依赖倒置原则:要依赖于抽象,而不是具体实现;针对接口编程,不要针对实现编程

  • 接口隔离原则:使用多个专门的接口比使用单一的总接口要好

  • 组合重用原则:要尽量使用组合,而不是继承关系达到重用目的

  • 迪米特(Demeter)原则、最少知识原则 :一个对象应当对其他对象有尽可能少的了解

设计模式

创建型设计模 式 定义 记忆关键字
Abstract Factory 抽象工厂模式 提供一个接口,可以创建一系列相关或相互依 赖的对象,而无需指定它们具体的类 抽象接口
Builder 构建器模式 将一个复杂类的表示与其构造相分离,使得相 同的构建过程能够得出不同的表示 类和构造分离
Factory Method 工厂方法模式 定义一个创建对象的接口,但由子类决定需要 实例化哪一个类。使得子类实例化过程推迟 子类决定实例 化
Prototype 原型模式 用原型实例指定创建对象的类型,并且通过拷 贝这个原型来创建新的对象 原型实例,拷 贝
Singleton 单例模式 保证一个类只有一个实例,并提供一个访问它 的全局访问点 唯一实例
结构型设计 模式 定义 记忆关键字
Adapter 适配器模式 将一个类的接口转换成用户希望得到的另一种接口。 它使原本不相容的接口得以协同工作 转换,兼容接口
Bridge 桥接模式 将类的抽象部分和它的实现部分分离开来,使它们可 以独立的变化 抽象和实现分离
Composite 组合模式 将对象组合成树型结构以表示“整体-部分”的层次 结构,使得用户对单个对象和组合对象的使用具有一 致性 整体-部分,树形 结构
Decorator 装饰模式 动态的给一个对象添加一些额外的职责。它提供了用 子类扩展功能的一个灵活的替代,比派生一个子类更 加灵活 附加职责
Fagade 外观模式 定义一个高层接口,为子系统中的一组接口提供一个 一致的外观,从而简化了该子系统的使用 对外统一接口
Flyweight 享元模式 提供支持大量细粒度对象共享的有效方法 细粒度,共享
Proxy 代理模式 为其他对象提供一种代理以控制这个对象的访问 代理控制
行为型设计 模式 定义 记忆关键字
Chain of Responsibility 职责链模式 通过给多个对象处理请求的机会,减少请求的发送者 与接收者之间的耦合。将接收对象链接起来,在链中 传递请求,直到有一个对象处理这个请求 传递请求、职责 链接
Command 命令模式 将一个请求封装为一个对象,从而可用不同的请求对 客户进行参数化,将请求排队或记录请求日志,支持 可撤销的操作 日志记录、可撤 销
Interpreter 解释器模式 给定一种语言,定义它的文法表示,并定义一个解释 器,该解释器用来根据文法表示来解释语言中的句子 解释器,虚拟机
Iterator 迭代器模式 提供一种方法来顺序访问一个聚合对象中的各个元素 而不需要暴露该对象的内部表示 顺序访问,不暴 露内部
Mediator 中介者模式 用一个中介对象来封装一系列的对象交互。它使各对 象不需要显式地相互调用,从而达到低耦合,还可以 独立的改变对象间的交互 不直接引用
行为型设计 模式 定 义 记忆关键字
Memento 备忘录模式 在不破坏封装性的前提下,捕获一个对象的内部状态, 并在该对象之外保存这个状态,从而可以在以后将该对 象恢复到原先保存的状态 保存,恢复
Observer 观察者模式 定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都得到通知并自动 更新 通知、自动更新
State 状态模式 允许一个对象在其内部状态改变时改变它的行为 状态变成类
Strategy 策略模式 定义一系列算法,把它们一个个封装起来,并且使它们 之间可互相替换,从而让算法可以独立于使用它的用户 而变化 算法替换
Template Method 模板方法模式 定义一个操作中的算法骨架,而将一些步骤延迟到子类 中,使得子类可以不改变一个算法的结构即可重新定义 算法的某些特定步骤
Visitor 访问者模式 表示一个作用于某对象结构中的各元素的操作,使得在 不改变各元素的类的前提下定义作用于这些元素的新操 作 。 数据和操作分离

工厂方法模式

为每一个产品都创建一个专属的工厂类,只能通过该工厂类创建产品。把生成产品类的时间延迟,需要通过对应的工厂类来生成对应的产品类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public interface Factory {
//声明产生产品类的方法
public Product createProduct();
}


public class FactoryA implements Factory {
//实现工厂类的方法生成产品类A
public Product createProduct()
{
return new ProductA();
}

}


public class Client {
public static void main(String[] args) {
Factory factory;
factory = new FactoryA();
factory.createProduct(); //创建 ProductA 实例

factory = new FactoryB();
factory.createProduct(); //创建 ProductB 实例

}
}
  • 可以实现“开发-封闭”原则,无论加多少产品类,我们都不用修改原来类中的代码,而是通过增加工厂类来实现。
  • 但是这还是有缺点的,如果产品类过多,我们就要生成很多的工厂类

抽象工厂模式

工厂方法模式,每一个产品类都要创建一个工厂,如果产品类过多,我们就要生成很多的工厂类。实际上一个工厂也不会只生产一种产品,例如苹果代工厂就会同时生产iPad、iPhone、iWatch 等。

可以把同一产品族相关联的产品放到同一个工厂生产。即一个工厂类生产多种产品。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class FactoryA implements Factory {
@Override
public Product createProduct()
{
return new ProductA();
}
@Override
public Gift createGift()
{
return new GiftA();
}
}


public class Client {
public static void main(String[] args) {
Factory factory;
factory = new FactoryA(); //工厂A 既生产 产品 也生产 礼品
factory.createProduct();
factory.createGift();

}
}

构造器模式

某个类创建时需要多个传参,这时候 构造函数就会接受多个参数,会造成构造函数过于复杂。

有一个 class A ,现在要创建 class A ,但是不通过 class A自身的构造函数来创建。而是要额外使用一个 class Builder 来创建 A的对象 , A 对象的属性是通过 Builder 来设置的 , 最后Builder 会返回一个A对象(修改好属性)。

用修改属性的方式来代替复杂的 construction 构造函数传参。

当构造复杂对象的时候,可以使用构造器模式构造对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Toy {
private String head;
private String body;
private ArrayList legs;
private ArrayList hands;

public String getHead() { }
public void setHead(String head) { }


/**
省略
getBody setBody
getLegs setLegs
getHands setHands
*/
static class Builder {
private Toy toy;

public Builder() {
toy = new Toy();
}

public Builder setHead(String head) {
toy.setHead(head);
return this;
}

// 省略 setBody setLegs setHands

public Toy build() {
return toy;
}
}

public static void main(String[] hh) {


Toy toy = new Toy.Builder()
.setBody("body")
.setHands("hands")
.setLegs("legs")
.setHead("head")
.build();
}

}

原型模式

原型模式要求对象实现一个可以“克隆”自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例。这样一来,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个clone方法来获取新的对象,而无须再去通过new来创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public  class Shape  {

private String id;
protected String type;


public Object clone() {

return new Shape(this.id , this.type)

}
}

public class text {
public static void main(String[] args) {
Shape shape = new Shape( "123" ,"123");
Shape pronesa1 = shape.clone();
}

}

单例模式

一个类只有一个实例 。每次创建类都是返回同一个实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class HungrySingleton {

private static final HungrySingleton instance=new HungrySingleton();
/**
* 私有构造方法
*/
private HungrySingleton(){
System.out.println("private HungrySingleton()");
}
/**
* 唯一公开获取实例的方法(静态工厂方法)
*
* @return
*/
public static HungrySingleton getInstance() {
return instance;
}

}




public class Test {
public static void main(String[] args) {
HungrySingleton.otherMethod();
System.out.println("-----------------");
System.out.println(HungrySingleton.getInstance());
System.out.println(HungrySingleton.getInstance());

}
}

适配器模式(Adapter )

将一个类的接口转换成客户希望的另一个接口。 使得原本不相容的接口可以协同工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 中式插座(源角色 Adaptee) 
public class ChineseStandard {
public String getChineseStandard() {
return "中式插座";
}
}

// 英式插座(目标角色 Target)
public interface BritishStandard {
String getBritishStandard();
}

// 插座适配器(适配器角色 Adapter)
public class StandardAdapter extends ChineseStandard implements BritishStandard {
@Override
public String getBritishStandard() {
//开启适配 。。。。。。。(省略)
return this.getChineseStandard();
}
}

public class Notebook {
public void charge(BritishStandard britishStandard) {
if ("中式插座".equals(britishStandard.getBritishStandard())) {
System.out.println("充电成功!");
} else {
System.out.println("充电失败!");
}
}
}



桥接模式(Bridge )

将抽象部分与它的实现部分分离,使它们都可以独立地变化。桥接模式将继承关系转化成关联关系

示例:家电电器有很多种,也有很多品牌,电器和品牌存在关系。

img

如图所示,如果按照这样的设计,每增加一种电器就需要对应的绑定品牌;另一种情况是,每增加一个品牌就需要在对应的电器下添加。这样的话会导致出现许多重复性的代码,而且耦合度也很高。

如果添加新的品牌或者新的电器而不会改动先有的类,该怎么设计呢?

根据桥接模式,代码可以设计成下图的流程

img

组合模式(Composite)

组合模式又叫合成(部分-整体)模式,属于结构型模式。组合模式将对象组织到树结构中,可以用来描述整体与部分的关系,可以使客户端将单纯元素与复合元素同等看待。

在树形结构中,最顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点,如下图所示:

img

根节点和树枝节点本质上属于同一种数据类型,可以作为容器使用;而叶子节点与树枝节点在语义上不属于用一种类型。但是在组合模式中,会把树枝节点和叶子节点看作属于同一种数据类型(用统一接口定义),让它们具备一致行为

这样,在组合模式中,整个树形结构中的对象都属于同一种类型,带来的好处就是用户不需要辨别是树枝节点还是叶子节点,可以直接进行操作,给用户的使用带来极大的便利。

装饰器(Decorator)

指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。

装饰模式是一种用于替代继承的技术,通过无需定义子类的方式动态地增强对象的功能

外观模式(Facade )

例子:这里有一台计算机,开机需要CPU、硬盘、内存的配合,但是用户实际上用户不需要逐个启动CPU、硬盘 、内存,用户只需要按了开机键就可以完成开机了。

在计算机应用外观模式中定义了一个开机的方法,在这个方法里面添加CPU、硬盘 、内存启动的方法。那么调用这个开机的方法,就可以实现开机了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// CPU
public class CPU {
public void freeze() {
System.out.println("CPU执行freeze操作");
}
public void jump() {
System.out.println("CPU执行jump操作");
}
public void execute() {
System.out.println("CPU正常运行");
}
}

// 硬盘类:
public class HardDrive {
public void read() {
System.out.println("读取硬盘");
}
}

//内存类:
public class Memory {
public void load() {
System.out.println("将硬盘中读取到信息加载到运行内存");
}
}

// 外观类:
public class ComputerFacade {
private CPU processor;
private Memory ram;
private HardDrive hd;

public ComputerFacade() {
this.processor = new CPU();
this.ram = new Memory();
this.hd = new HardDrive();
}

//开机
public void start() {
processor.freeze();
hd.read();
ram.load();
processor.jump();
processor.execute();
System.out.println("计算机正常启动完毕");
}
}

享元模式(Flyweight )

它主要解决的问题是创建大量相似对象时的内存开销问题。该模式通过共享具有相同状态的对象来减少内存使用量

享元模式的思想是:当需要创建一个新对象时,首先检查是否已经存在具有相同状态的对象。如果存在,则返回已经存在的对象,否则创建一个新的对象。因此,如果要创建多个具有相同状态的对象,可以重复使用相同的对象,从而减少内存开销。

代理模式 ( Proxy )

在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口

职责链模式(chain of responsibility )

在职责链模式中,多个处理单元依次处理同一个请求。一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B处理器处理完后再 传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理单元各自承担各自的处理职 责

img

命令模式(Command )

一般而言,在软件开发中,行为的请求者和行为的执行者是紧密耦合在一起的,调用关系简单易懂,但是这样不容易拓展,有些时候,我们需要记录、撤销、重做处理的时候,不易修改。

命令模式会 将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开,解耦合。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储、传递、调用、增加与管理。

举个现实中的例子,比如我们去餐厅吃饭:

点餐(写好菜单,发起请求) --> 订单系统处理,生成订单(创建命令) --> 厨师获取到订单,开始做菜(执行命令),在这个过程中我们并没有直接与厨师交谈,不知道那个厨师做,厨师也不知道是哪个顾客需要,只需要按照订单处理就可以。

解析器模式(Interpreter)

提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。

中介者模式(Mediator)

每个具体同事只知道自己的行为,而不了解其他同事类的情况,但他们都认识中介者对象,中介者知道所有具体同事类,并从具体同事接受消息,向具体同事对象发出命令。

  1. 类之间各司其职,符合迪米特法则。
  2. 降低了对象之间的耦合性,使得对象易于独立地被复用。
  3. 将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。

备忘录模式(Memento )

保存一个对象的某个状态,以便在适当的时候恢复对象。如: 撤销重做 、 游戏保存

观察者模式(Observer )

观察者模式定义了一种一对多的依赖关系,让多个观察者同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

状态模式( State )

我们经常会看到只有一个按钮的台灯,但是可以通过按钮调节亮度,大概是如下一个循环 “关 -> 弱光 -> 亮 -> 强光 -> 关”,那么每次按按钮后,要跳转到什么状态,其实和当前状态有关。我们可以用 if else 解决这个问题,也可以用状态模式解决。

用状态模式解决,就是将这四个状态封装为四个类,可以将各种状态的转换逻辑 , 分布到状态的子类中 , 减少相互依赖 ,这样未来新增一种模式,只要改变部分类即可。

策略模式(Strategy )

定义了一组同类型的算法,在不同的类中封装起来,每种算法可以根据当前场景相互替换,从而使算法的变化独立于使用它们的客户端(即算法的调用者)

模板方法模式(Template Method )

在程序开发中,经常会遇到这种情况:某个方法要实现的算法需要多个步骤,但其中有一些步骤是固定不变的,而另一些步骤则是不固定的。为了提高代码的可扩展性和可维护性,模板方法模式在这种场景下就派上了用场。

我们可以在父类中确定整个流程的循序,并实现固定不变的步骤,而把不固定的步骤留给子类实现。甚至可以通过一个钩子方法,让子类来决定流程中某个方法的执行与否

示例.模板方法模式

详解访问者(Visitor)

动物园中有多个场馆,比如豹子馆,海豚馆,大象馆等等,有些场馆是需要特殊收费的,动物园针对不同类型的游客有不同的收费方式,比如学生半价。

这个场景下,包括以下要素:动物园动物园中的各个场馆不同类型的游客不同类型的游客票价不同

动物园就相当于一个对象结构,该结构包含具体的元素-各个场馆,每个场馆(元素)都有接待游客(visitor)的方法(accept)。

这些被处理的数据元素相对稳定(动物园中的场馆一般比较稳定)而访问方式多种多样(比如学生散客,学生团体,普通游客,团体游客等不同的访问方式)的数据结构,如果用“访问者模式”来处理比较方便。

访问者模式能把处理方法从数据结构中分离出来,并可以根据需要增加新的处理方法,且不用修改原来的程序代码与数据结构,这提高了程序的扩展性和灵活性。


系统设计
http://example.com/2024/05/23/系统分析师/系统设计/
Author
John Doe
Posted on
May 23, 2024
Licensed under