架构设计和包图

包组织原则

  • 将在功能上有紧密联系的、垂直或水平的切片打包
  • 将一族接口打包
  • 将一组不稳定的类打包
  • 提取独立的类型
  • 利用工厂(factory)来降低实体包之间的依赖
  • 不要在包中出现回路
提示:功能性相关的类。
一般说来,决定类之间是否有功能性联系,可以看是否满足以下条件:
  1. 在一个类里发生变化会影响到另一个类;
  2. 移除一个类将会影响另一个类;
  3. 两个类之间有复杂的内部交互或是互相传递大量的信息;
  4. 如果一个边界类(boundary class)中的一个函数是用来呈现某个实体类的,那么这个边界类将和这个实体类有功能性关联;
  5. 两个类被同一个因素影响或产生交互;
  6. 两个类之间有关系;
  7. 一个类创建另一个类的实体;
判断两个类不应该放在同一个包里,可以观察是否满足以下条件之一:
  1. 两个类与不同的因素有关系;
  2. 一个可选类和一个强制类不应该放在同一个包里;
包元素的可视性。一个包里,只有pulic属性的类可以和外部进行交互。



包设计原则


包的设计原则可分为两大类:
一类是间隔尺度,规定了包的内聚度,包括1)发布和重用等价原则,REP(The Release-Reuse Equivalency Principle);2)全部重用原则,CRP( The Common Reuse Principle);3)公共闭合原则,CCP(The Common Closure Principle)。
另一类是稳定性,规定了包之间的耦合度,包括1)非循环依赖原则,ADP(The Acyclic Dependencies Principle);2)稳定依赖原则,SDP(The Stable Dependencies Principle);3)稳定抽象原则,SAP(The Stable Abstractions Principle)。

发布重用原则(REP)

REP规定, The granule of reuse is the granule of release,重用粒度等于发布粒度。它包含了以下几个意思:
  • 包通常是一个发布版本中的基本单位;
  • 为了提供重用所需要的保证,开发人员必须将他们的软件分成一些可重用的包,然后跟踪这些包,再发布新的版本;
  • 我们重用的任何东西都必须被发布并且跟踪;
  • 一个包中的元素或类要么都可重用,要么都不可重用。这是因为如果一个包里包含了某个可以重用的软件,它就不应该再包括不是用于重用的软件。

重用发布等价原则为我们指明了包的设计方针:

一个包中的元素(类)要么都可重用,要么都不可重用。


全部重用原则(CRP)

CRP规定, The classes in a package are reused together. If you reuse one of the classes in a package, you reuse them all.也就是说, 包的所有类被一起重用。如果你重用了其中的一个类,就重用全部。换一种通俗的说法: Classes that aren’t reused together should not be grouped together, 没有被一起重用的类不应该被组合在一起
CRP帮助我们决定哪些类应该放在同一个包里:那些将会一起重用的类应该放在同一个包里。
CRP更告诉我们哪些类不应该放在同一个包里:那些不是紧密联系的类不应该放在同一个包里。可以独立使用或在不同情形下使用的类型应该放在不同的包里。

依赖一个包就是依赖这个包所包含的一切。当一个包发生了改变,并发布新的版本,使用这个包的所有用户都必须在新的包环境下验证他们的工作,即使被他们使用的部分没有发生任何改变。 因为如果包中包含有未被使用的类,即使用户不关心该类是否改变,但用户还是不得不升级该包并对原来的功能加以重新测试。


公共闭合原则(CCP)

CCP规定, The classes in a package should be closed together against the same kinds of changes. a change that affects a package affects all the classes in that package, 一个包中所有的类应该对同一种类型的变化关闭。一个变化影响一个包,便影响了包中所有的类,而且不会影响其他包。
一个更简短的说法是: Classes that change together, belong together. 一起修改的类,应该组合在一起(同一个包里)。
CCP跟开闭原则(OCP: Open Closed Principle) 有着很深的渊源关系,CCP的“关闭”(closure)就是OCP所提倡的:classes should be closed for modification but open for extension. 类应该对修改关闭,对扩展开放。但我们知道,100%的“关闭”是不现实的,我们在设计系统时,只能尽量地保持对大多数可预见的修改关闭。
CCP延伸了OCP的“关闭”概念,当因为某个原因需要修改时,把需要修改的范围限制在一个最小范围内的包里。


非循环依赖原则(ADP)

ADP规定,The dependency structure between packages must be a directed acyclic graph (DAG). That is, there must be no cycles in the dependency structure.包之间的依赖结构必须是一个直接的无环图形(DAG)。也就是说,在依赖结构中不允许出现环(循环依赖)。

如果出现了包循环依赖问题,如包A依赖包B,包B依赖包C,而包C又依赖包A,那么我们修改了B并需要发布B的一个新的版本,因为B依赖C,所以发布时应该包含C,但C同时又依赖A,所以又应该把A也包含进发布版本里。也就是说,依赖结构中,出现在环内的所有包都不得不一起发布。它们形成了一个高耦合体,当项目的规模大到一定程度,包的数目变多时,包与包之间的关系便变得错综复杂,各种测试也将变得非常困难,常常会因为某个不相关的包中的错误而使得测试无法继续。而发布也变得复杂,需要把所有的包一起发布,无疑增加了发布后的验证难度。

为了打破这种循环依赖,有两种解决方法:

1)找出两个包里参与这个循环的因素,并把它们组合成一个新的包。

例如,若存在以下依赖关系。


包C要依赖包A,必定A中包含有A,C共通使用的类,把这些共同类抽出来放在一个新的包D里。这样就把C依赖A变成了C依赖D以及A依赖D,从而打破了循环依赖关系。如图:


这样,包的依赖关系就从A->B->C->A变成了:

A->B->C->D

A->D

2)利用接口。ISP(接口分隔原则)可以剔除没用到的接口。DIP(依赖倒置原则)在类的调用之间引入抽象层。

例如,若存在左下图的循环依赖。


那么A必然使用了B中的某个类,这样我们可以吧这个类分离成一个接口,并让B实现这个接口并依赖于它,同时让包A也依赖于这个接口,就可以打破这种循环。

又例如,三个循环依赖的类Myapplication、MyTasks和MyDialoges,如左下图。如果用方法一,我们可以将Myapplication和MyDialoges共同使用的类分离成一个新的包,如右下图所示;如果用方法二,MyDialoges中的类X使用了Myapplication中的类Y,我们可以为类Y设计一个接口IX,并把它放在包MyDialogues里,再让Myapplication中的Y实现并该包依赖于它。



稳定依赖原则(SDP)

SDP规定, The dependencies between packages in a design should be in the direction of the stability of the packages. A package should only depend upon packages that are more stable that it is. 一个设计中的包之间的依赖应该朝着稳定的方向进行。一个包只应该依赖那些比自己更稳定的包。
换成另一个说法是: Depend in the direction of stability. 朝着稳定的方向进行依赖。

也就是说,包应该依赖比自己更稳定的包。因为如果依赖一个不稳定的包,那么当这个不稳定的包发生变化时,本身稳定的包也不得不发生变化,变得不稳定了。

所谓稳定,在现实生活中是指一个物体具有稳固不变的属性使它很难发生变化。应用到软件概念上,我们认为一个软件是稳定的,是因为这个软件很难发生改变,或更确切地说,是不需要发生改变。一个设计良好,能应对各种变化不需要修改的软件当然是稳定的了,但事实上,往往一个软件常常需要对应某个事先没有预测到的用户需求而不得不发生改变,当这种改变发生时,能把修改控制在最小的范围之内,并能稳定的工作(包括软件本身以及依赖它的其它软件实体等),我们也会认为该软件是相对稳定的。

那么,怎么判断一个包是否稳定呢?我们可以通过下面的方法来判断一个包的稳定系数:

  • Ca:Afferent Coupling。向心耦合。依赖该包(包含的类)的外部包(类)的数目(i.e. incoming dependencies)。
  • Ce: Efferent Coupling。离心耦合。被该包依赖的外部包的数目(i.e. outgoing dependencies)。
I: Instability。不稳定性。I=Ce/(Ce+Ca)。它的值处于[0,1]之间。I值越小越稳定。

如图1,X的Ce=0,所以不稳定性I=0,它是稳定的。相反,如图2,Y的Ce=3,Ca=0,所以它的不稳定性I=1,它是不稳定的。

SDP要求一个包的不稳定性I要大于它所依赖的包的不稳定性。“Depend upon packages whose I metric is lower than yours.”

换句话说,沿着依赖的方向,包的不稳定性应该逐渐降低,稳定性应该逐渐升高。


例如,左下图中一个稳定的包依赖了一个不稳定的包,就违反了SDP原则。

       

那么我们怎样打破这种不稳定关系呢?我们可以使用 DIP(依赖倒置原则)在类的调用之间引入抽象层。如右上图中,我们从不稳定的包中分离出一个接口IU,并让不稳定包中的类C实现这个接口,再让U依赖这个接口。(因为接口是抽象的,而抽象的往往都是稳定的。)

稳定抽象原则(SAP)

SAP原则规定, Packages that are maximally stable should be maximally abstract. Instable packages should be concrete. The abstraction of a package should be in proportion to its stability. 最稳定的包应该是最抽象的包。不稳定的包应该是具体的包。包的抽象程度跟它的稳定性成正比。

一个包的抽象程度越高,它的稳定性就越高。反之,它的稳定性就越低。一个稳定的包必须是抽象的,反之,不稳定的包必须是具体的。

稳定的包的构成

抽象类或接口通过子类继承扩展行为,这表示抽象类或接口比它们的子类更具有稳定性。总之,为了构成稳定的包,应该提高包内的抽象类或接口的比率;它们的子类可以放在另一个不稳定的包内,该包依赖上述稳定的包,从而遵循了稳定依赖原则(SDP)。

理想的体系结构应该是:

不稳定的(容易改变的)包处于上层

- 它们是具体的包实现

稳定的(不容易改变的)包处于下层

- 不容易改变,但容易扩展

- 接口比实现(具体的运行代码)在内在特性上更具有稳定性


因此,我们可以1)将具有功能性关联的接口放在同一个包里,并与其实现分离开来;2)利用工厂来降低具体的包之间的依赖,一种提高包的稳定性的方法是减少它对其他包中具体的包类的依赖。

我们通常使用工厂(factory)来降低包之间的耦合度。




参考资料:包的设计原则 http://www.uml.org.cn/mxdx/200912233.asp



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
(1)UML样例源文件(8个样例文件) uml/uml.mdl //01.UML快速入门 uml/usecase.mdl //02.用例图 uml/class.mdl //03.静态图——类图、对象图、包图 uml/component.mdl //04.实现图——组件图与部署图 uml/statechart.mdl //05.行为图——状态图 uml/activity.mdl //06.行为图——活动图 uml/sequence.mdl //07.交互图——序列图 uml/collaboration.mdl //08.交互图——协作图 (2)UML上机演练源文件(16个实例文件) uml/usecase_product.mdl //02.用例图——企业产品生产销售管理系统 uml/class_login.mdl //03.类图和包图——企业信息管理系统用户登录和注册模块 uml/component_emis.mdl //04.组件图与部署图——企业信息管理系统 uml/statechart_bug.mdl //05.状态图——Bug管理系统 uml/statechart_atm.mdl //05.状态图——ATM机存取款 uml/statechart_thread.mdl //05.状态图——Java线程类Thread uml/activity_bug.mdl //06.活动图——Bug管理系统 uml/activity_atm.mdl //06.活动图——ATM机存取款 uml/activity_thread.mdl //06.活动图——Java线程类Thread uml/sequence_bug.mdl //07.序列图——Bug管理系统 uml/sequence_atm.mdl //07.序列图——ATM机存取款 uml/sequence_tel.mdl //07.序列图——打电话 uml/collaboration_bug.mdl //08.协作图——Bug管理系统 uml/collaboration_atm.mdl //08.协作图——ATM机存取款 uml/collaboration_tel.mdl //08.协作图——打电话 uml/bug.mdl //09.综合实例——Bug管理系统 (3)设计模式样例(24个讲解样例程序) pattern/src/principle/liskovsubstitution//10.3.2里氏代换原则 pattern/src/creation/factorymethod //11.1工厂方法模式 pattern/src/creation/abstractfactory //11.2抽象工厂模式 pattern/src/creation/singleton //11.3单例模式 pattern/src/creation/builder //11.4建造者模式 pattern/src/creation/prototype //11.5原型模式 pattern/src/structure/adapter //12.1适配器模式 pattern/src/structure/decorator //12.2装饰器模式 pattern/src/structure/proxy //12.3代理模式 pattern/src/structure/facade //12.4外观模式 pattern/src/structure/bridge //12.5桥接模式 pattern/src/structure/composite //12.6组合模式 pattern/src/structure/flyweight //12.7享元模式 pattern/src/behavior/strategy //13.1策略模式 pattern/src/behavior/templatemethod //13.2模板方法模式 pattern/src/behavior/observer //13.3观察者模式 pattern/src/behavior/iterator //13.4迭代子模式 pattern/src/behavior/chainofresponsibility//13.5责任链模式 pattern/src/behavior/command //13.6命令模式 pattern/src/behavior/memento //13.7备忘录模式 pattern/src/behavior/state //13.8状态模式 pattern/src/behavior/visitor //13.9访问者模式 pattern/src/behavior/mediator //13.10中介者模式 pattern/src/behavior/interpreter //13.11解释器模式 (4)SOA(12个程序) soa/JAXWSHelloWorldServer.zip //15.JAX-WS服务端——HelloWorld实例 soa/JAXWSHelloWorldClient.zip //15.JAX-WS客户端——HelloWorld实例 soa/AxisHelloWorldServer.zip //16.Axis服务端——HelloWorld实例 soa/AxisHelloWorldClient.zip //16.Axis客户端——HelloWorld实例 soa/SCAJavazip //19.SCA实例——计算器实例 soa/SCAWSServer.zip //19.SCA服务端实例——HelloWorld实例 soa/SCAWSClient.zip //19.SCA客户端实例——HelloWorld实例 soa/OSGiHelloWorld.zip.zip //21.OSGi实例——HelloWorld实例 soa/demo.sql //24.SOA服务架构实战——企业信息管理系统数据库脚本 soa/ssh2.zip //24.SOA服务架构实战——企业信息管理系统SSH2代码 soa/EmisWeb.zip //24.SOA服务架构实战——企业信息管理系统SCA服务端 soa/EmisService.zip //24.SOA服务架构实战——企业信息管理系统Web端
根据提供的引用内容,可以得出Spring Boot架构设计图如下: ``` 工具:util 整合:integration 基础:infrastructure 领域:domain 应用:application 门面:facade 客户端:client 控制:controller 启动:boot ``` 其中,工具util提供一些常用的工具类;整合integration用于整合各种框架和组件;基础infrastructure提供基础设施,如数据库访问、缓存等;领域domain含业务逻辑和领域模型;应用application负责协调各个领域的业务逻辑;门面facade提供对外的接口;客户端client是与外部系统进行交互的模块;控制controller接收和处理用户请求;启动boot是整个应用的入口。 请注意,这只是一个简化的架构设计图,实际的架构可能会更加复杂和细分。 #### 引用[.reference_title] - *1* [SpringBoot+SpringCluod框架下的系统架构图](https://blog.csdn.net/weixin_41290516/article/details/109642898)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [六个思考维度:DDD + SpringBoot工程九结构图解与实战](https://blog.csdn.net/woshixuye/article/details/128861325)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋斗的小壁虎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值