Paul Jiang's Blog

  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

面向模式的软件架构-Layers模式

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 架构

Layers有助于将应用程序划分为多组子任务,其中每组子任务都位于特定抽象层。

背景

需要分解的大型系统

问题

假设你要设计一个系统,其主要特征是需要同时解决高层问题和低层问题,且高层操作依赖于低层操作。在这个系统中,有些部分负责处理低层问题,如硬件陷阱、传感器输入等。在另一端可能有用户可见的功能(如游戏的界面)或高层策略(如电话计费)。在典型的通信过程中,请求从高层向低层传输,而响应、到来的数据及事件通知沿相反的方向传输。


在这种情况下,需要平衡下述作用力。

  1. 以后修改源代码不会波及整个系统。
  2. 接口稳定,甚至遵守标准组织制定的标准。
  3. 系统的各个部分都可更换。
  4. 未来可能需要打造其他系统,这些系统与当前设计的系统面临着相同的低层问题。
  5. 应将类似的职责编组,让系统更容易理解和维护。
  6. 根本没有所有的标准组件粒度。
  7. 复杂的组件需要进一步分解。
  8. 跨越组件边界可能影响性能,例如,必须跨越多个组件边界传输大量数据时。
  9. 系统将由一个程序员团队打造,因此分工必须明确。

结构

在结构方面,Layers模式的主要特征是,第J+1层只使用第J层的服务,层之间没有其他直接依赖关系。这种结构类似于栈,每层都将下面的各层保护起来,禁止上面的层直接访问它们。
|客户端|
|-|
|第N层|
|第N-1层|
|…|
|第1层|

实现

  1. 定义将任务划分到不同层的抽象准则。该准则通常是离平台的概念距离。
    在真正的软件开发中,大家通常结合使用多种抽象准则。例如,根据离硬件的距离划分较低的层,按概念复杂度划分较高的层。
    例如,
    ||
    |–|
    |用户可以看到的元素|
    |具体的应用程序模块|
    |通用服务层|
    |操作系统接口层|
    |操作系统|
    |硬件|

  2. 根据抽象准则确定抽象层级数。层数太多可能带来不必要的开销,层数太少又可能导致结构不清晰。

  3. 给每层命名并分派任务。最高层的任务就是整个系统要完成的任务,由客户指定。其他各层的任务是辅佐上一层。
  4. 规范服务。最重要的实现原则是相邻界限分明,即任何组件都不跨越多层。
  5. 完善层次划分。反复执行第1~4步。如果不考虑抽象准则导致的层及其服务,通常无法准确地定义抽象准则。另外,先定义组件和服务,再根据它们之间的使用关系拼凑出分层结构的做法通常是错误的。由于这样的结构没有反映固有的分层原则,维护系统时很可能破坏原有的架构。例如,新组件可能使用多层提供的服务,违背严格分层的策略。
    解决之道是反复执行前4步,直到分层结构自然而稳定。
  6. 规范每层的接口。如果对第J+1层来说,第J层应该算是“黑盒”,就应设计一个统一接口,该接口提供第J层的所有服务,还可能将其封装到一个Facade对象中。
  7. 确定各层的结构。传统上,重点是确保层间关系合理,而各层内部却随心所欲,其结果是混乱不堪。对于复杂的层,应将其划分为多个组件,进行这种划分时粒度更细的模式可提供帮助。
  8. 规范相邻层之间的通信。在层间通信方面,最常用的机制是推模型:第J层调用第J-1层的服务时,随服务调用一起传递所需的信息。与这种模型相反的是拉模型,指的是下层根据自己的判断从上层那取回信息。然而,拉模型可能增加当前层对上一层的依赖关系,要避免拉模型导致的下层对上层的依赖,可使用回调。
  9. 将相邻层解耦。通常上一层知道下一层,但下一层不知道其用户的身份。这意味着只存在单向耦合:修改第J层时,不用考虑第J+1层,条件是被修改的服务的接口和语义保持不变。
    为支持自下而上的通信,可使用回调,并保留自上而下的单向耦合。使用回调时,上层向下层注册回调函数。在下层向上层发送的事件固定时,这种方式特别有效。
    还可在一定程度上降低上层对下层的依赖,根据接口编写上层的代码,让上层不依赖于下层的具体实现。这里由主程序负责在层之间建立“连接”,但这种工作通常由连接管理组件统一管理。
    对于需要同时向上和向下传递消息的通信栈,显示地将下层关联到上层通常更佳。
  10. 制定错误处理策略。对分层架构而言,处理错误的代价可能非常高,无论从处理时间还是编程工作量的角度来说都如此。错误发生后,要么在当前层进行处理,要么转给上一层。如果采用第二种做法,当前层必须将错误转换为上一层能够明白的错误描述。一般说来,应尽可能在当前层处理错误,以免高层包含大量处理众多不同错误的代码。

效果

优点

  1. 各层可重用
  2. 支持标准化。一个著名的标准化接口是POSIX编程接口。
  3. 限制了依赖关系的范围。
  4. 可更换性。
    缺点
  5. 行为变化可能引发雪崩效应。一层的行为发生变化可能引发严重的问题。如果为完成局部调整,必须对很多层做大量修改,那么分层就成了缺点。
  6. 效率低下。
  7. 不必要的工作。
  8. 难以确定正确的层次粒度。

面向模式的软件架构-Blackboard模式

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 架构

Blackboard架构模式对还未找到确定解决策略的问题很有帮助。在Blackboard模式中,多个专业子系统通过集思广益,获得可能的部分解或近似解。

背景

未找到或找不到确定解决之道的不成熟领域。

问题

Blackboard模式解决没有可行而确定的解决方案将原始数据转换为高级数据结构(如图表或英语词组)。存在这种问题的领域包括视觉识别、图像识别、语音识别和监视等。这种问题具有如下特点:可分解成多个子问题,但每个子问题都属于不同的专业领域。
对于这种问题,影响其解决方案的作用力如下。

  • 不可能在合理的时间内遍历整个解空间。
  • 鉴于领域不成熟,可能需要对同一个子任务尝试不同的算法。
  • 子问题的算法各不相同。
  • 输入、中间结果和最终结果的表示方式各不相同,而不同算法是根据不同范式实现的。
  • 一个算法通常使用另一个算法的结果。
  • 涉及不可靠的数据和近似解。
  • 算法的执行顺序不确定时还可能要求支持并行性。

解决方案

Blackboard架构背后的理念是,一系列独立的程序携手合作,致力于处理同一个数据结构。
在解决问题的过程中,系统通过合并、修正或否决部分解来完成工作。每个部分解都针对一个子问题,是这个子问题的最终解在特定阶段的表现形式。所有的可能解构成解空间,并被组织成多个抽象层级,其中最低层为输入的内部表示,最高层包含系统要解决的整个问题的可能解。
之所以使用名称“黑板”(blackboard),是因为它让人想起专家们站在黑板前协作解决问题的情形。专家们通常自行决定截下来该由谁来到黑板前,而在这里介绍的模式中,如果有多个程序都能提供帮助,将由调停者(moderator)组件决定这些程序的执行顺序。

###结构
对系统进行划分,使其包含一个黑板组件、一系列知识源以及一个控制组件。
黑板为中央数据存储区,解空间中的元素及控制数据都存储在这里。黑板提供了一个接口,让所有知识源都能够对其进行读写。
每个知识源都是一个独立的子系统,解决整个问题的特定方面。
控制组件运行一个监视黑板内容变化的循环,并决定接下来采取什么措施。

实现

1
2
3
4
5
6
7
sequenceDiagram
控制->>分段:execCondition
控制->>音节生成:execCondition
控制->>单词生成:execCondition
分段->>黑板:检查
音节生成->>黑板:检查
单词生成->>黑板:检查
  1. 定义问题。
  2. 定义问题的解空间。我们将解分为中间解和顶级解,还将其分为部分解和完整解。顶级解位于最高抽象层级,其他层级的解为中间解。完整解是整个问题的答案,而部分解是部分问题的答案。
  3. 将求解过程分为如下步骤:
  • 定义如何将解转换为上一层级的解。
  • 描绘如何作出同一抽象层级的推测。
  • 详细说明如何从其他层级寻找证据,以证实做出的推测。
  • 指出可利用什么样的知识将部分解空间排除在外。
  1. 根据子任务将知识划分为专业知识源。
  2. 定义黑板的词表。
  3. 规范系统的控制机制。
  4. 实现知识源。

软件构架实践-质量属性

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 架构

质量属性场景

质量属性场景是一种面向特定的质量属性的需求。它由以下6部分组成:

  • 刺激源:某个生成该刺激的实体(人、计算机系统或任何其他激励器)。
  • 刺激:该刺激是当刺激到达系统时需要考虑的条件。
  • 环境:该刺激在某些条件内发生。当刺激发生时,系统可能处于过载,或者正在运行,也可能是其他情况。
  • 制品:某个制品被刺激。这可能是整个系统,也可能是系统的一部分。
  • 响应:该响应是在刺激到达后所采取的行动。
  • 响应度量:当响应发生时,应该能够以某种方式对其进行度量,以对需求进行测试。

我们将一般的质量属性场景与具体的质量属性场景区分开来,前者是指那些独立于系统,很可能适合任何系统的场景,后者是指适合正在考虑的某个特定系统的场景。

可用性的一般场景生成

场景的部分 可能的值
源 系统内部、系统外部
刺激 错误:疏忽、崩溃、时间、响应
制品 系统的处理器、通信通道、持久存储器、进程
环境 正常操作、降级模式
响应 系统应该检测时间,并进行如下一个或多个活动:将其记录下来,通知适当的各方,根据已定义的规则禁止导致错误或故障的事件源,在一段预先指定的时间间隔内不可用。
响应度量 系统必须可用的时间间隔;可用时间;系统可以在降级模式下运行的时间间隔;修复时间;

可用性的一般场景

可用性的场景样例

系统质量属性

  1. 可用性

可用性所关注的方面包括:如何检测系统故障,系统故障发生的频度,出现故障时会发生什么情况,允许系统有多长时间非正常运行,什么时候可以安全的出现故障,如何防止故障的发生以及发生故障时要求进行哪种通知。

  1. 可修改性

可修改性是有关变更的成本问题,它提出了两个关注点。

  • 可以修改什么?可以修改系统的任何方面,最常见的就是系统计算的功能、系统存在的平台、系统运行的环境、系统所展示的质量属性以及其容量。
  • 任何进行变更以及由谁进行变更?可以在编译期间、构建期间、配置设置迁建或者执行期间改变实现。变更还可以由开发人员、最终用户或系统管理员进行。
  1. 性能

性能与时间有关。可以用等待时间、处理期限、系统吞吐量、响应抖动、由于系统太忙因而无法做出响应所导致的未处理事件的数量,以及因系统太忙所丢失的数据。

  1. 安全性

可以把安全性刻画为一个提供认可、机密性、完整性、保证、可用性和审核的系统。

  • 认可:就是交易不能被交易的任何一方拒绝的属性。这意味着如果你通过Internet订购了某个项目的话,就不能否认。
  • 机密性:就是未经授权不能访问数据或服务的属性。
  • 完整性:根据计划来提交数据或服务的属性。自从老师给你分配登记后,等级就没有改变过。
  • 保证:交易的各方是所声称的人的属性。当客户发送一个信用卡卡号到Internet商家时,该商家正是客户所认为的商家。
  • 可用性:系统可用于合法用户的属性。拒绝服务供给不会阻碍你订购。
  • 审核:系统在其内部跟踪活动的属性,跟踪的级别足以对活动进行重构。
  1. 可测试性

可测试性的响应度量是测试在发现缺陷方面的效率,以及要想达到某个期望的覆盖范围,需要用多长时间进行测试。

  1. 易用性

易用性关注的是对用户来说完成某个期望任务的容易程度和系统所提供的用户支持的种类。

  • 学习系统的特性:如果用户不熟悉某个特定的系统或该系统的某一特定方面,那么系统可以如何使学习任务变得更容易。
  • 有效的使用系统:系统如何提高用户的操作效率。
  • 将错误的影响降到最低:系统怎样使用户所犯的错误造成的影响最小。
  • 使系统适应用户的需要:系统如何使用户的任务变得更轻松。
  • 提高自信和满意度:系统如何使用户却行采取了正确的行动。

其他系统质量属性

在其他资料中,还可以发现大量的其他属性。比如可扩充性,可移植性等等。在引用这些质量属性的时候,通过对一般场景的6个部分进行描述即可。

商业质量属性

  1. 上市时间
  2. 成本和收益
  3. 所希望的系统生命期的长短
  4. 目标市场
  5. 推出计划
  6. 与老系统的集成

构架质量属性

  1. 概念完整性:在各个层次上统一系统设计的根本指导思想。
  2. 正确性和完整性:构架能够满足系统的各种需求及运行时的资源要求的必要条件。
  3. 可构建性:保证能够由指定的开发小组在规定的时间里及时开发系统,并允许在开发过程中做某些更改的架构属性。

软件构架实践-构架商业周期

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 架构

软件构架是技术、商业和社会等诸多因素作用的结果,而软件构架的存在反过来又会影响技术、商业和社会环境。

构架所受的影响

  • 构架受系统受众的影响

很多人和组织都对构建软件系统感兴趣。我们把这样的人或组织称为涉众:客户、最终用户、开发人员、项目经理、维护人员以及那些对系统进行市场营销活动的人等等。这些涉众所关注的问题各不相同,但都要求系统能够在他们所关注的方面提供保证或优化。

  • 构架受开发组织的影响

组织中是否有足够多的有空闲时间且精通C/S通信的程序员?人员的技能也是一个影响因素,开发进度和预算也会对构架产生影响。

  • 构架受设计师的素质和经验的影响

设计构架时所做的各种选择与设计师本人所受的教育或培训背景、对其他成功构架的了解以及对某些性能极佳或极差的系统的了解有关。

  • 构架受技术环境的影响

这里所说的技术环境包括行业中的通常做法或在设计师所属专业团体中占主导地位的软件工程技巧。

  • 影响构架的其他因素

构架对诸影响因素的反作用

企业目标、产品需求、设计师的经验、构架和最终系统之间构成带有反馈回路的、可由开发组织实施管理的周期。开发组织对这个周期管理的好,就能够不断成长壮大,拓展其经营范围,充分利用以前在构架和系统构建方面的投资。

  • 构架影响开发组织的结构
  • 构架影响开发组织的目标
  • 构架影响客户对下一个系统的要求
  • 构架系统的过程丰富了整个开发团体的敬仰
  • 一些典型的系统会影响并实际改变软件工程的发展,也就是系统开发人员学习和实践的技术环境。

软件过程

  • 为系统构建一个商业案例
  • 理解系统需求
  • 创建或选择构架
  • 将构架编成文档,并与有关各方进行交流
  • 对此构架进行分析和评价
  • 根据此构架实现系统
  • 保证系统实现符合构架的要求

什么样的架构才算好

关于过程的建议

  • 构架的设计应该由一位设计师来完成,或者由一个在某位设计师领导下的小组来完成。
  • 设计师应全面掌握系统的功能需求,并且应有一份所设计构架应满足的划分了优先级的质量属性列表。
  • 构架的文档应该完备,至少应有一个静态视图和一个动态视图,应该采用所有人员认可的文档形式,以保证所有涉众都能很容易的理解这些文档。
  • 应该把架构设计方案交由各涉众传阅,应该让各涉众积极参与设计方案的评审。
  • 应该对构架认真进行分析,得出可应用的量化度量指标。也应该对质量属性进行正式评估,以避免出现发现问题时为时已晚的情况。
  • 构架的设计应有助于增量式实现。为此,可先创建一个粗略的、具备雏形但功能最简单的系统,通过把这个骨架系统逐步细化、扩大来得到所期望的系统。
  • 允许构架带来一定的资源争用,但应该清楚地给出这些资源争用的解决方案,告之于有关各方,并保证这些解决方案切实可行。例如,若网络占用是要考虑的问题,设计师就要为每个开发小组制定出将网络占用减少到最低限度的指导原则。

关于产品(结构)的建议

  • 构架应采用定义良好的模块,各模块的功能责任划分应基于信息隐藏和相互独立的原则。
  • 应该使用特定于每个属性的众所周知的构架战术来实现质量属性。
  • 构架绝对不可以依赖于某个特定版本的商业产品或工具。
  • 应将产生数据的模块和使用数据的模块分离开。
  • 对于并行处理系统,构架应该采用定义良好的进程或任务,它们未必反映模块分解结构。
  • 每个任务或进程的编写都要考虑到与特定处理器的关系,并保证能够方便的改变这种关系。
  • 构架应该采用少量的、简单的交互模式。在整个运行过程中,系统的功能应保持一致,这可使系统易于理解,有助于缩短开发时间、提高可靠性、增强可修改性。

设计模式-适配器

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 架构

目的

将一个类的接口转换成客户希望的另外一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

类适配器

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
Class Shape
{
Public:
Shape();
Virtual void BoundingBox(Point& bottomLeft, Point& topRight)const;
Virtual Manipulator* CreateManipulator() const;
};

ClassTextView
{
Public:
TextView();
Void GetOrigin(Coord& x, Coord& y) const;
Void GetExtend(Coord& width, Coord& height) const;
Virtual bool IsEmpty() const;
};


ClassTextShape : public Shape, private TextView
{
Public:
TextShape();
Virtual void BoundingBox(Point& bottomLeft, Point& topRight)const;
Virtual bool IsEmpty() const;
Virtual Manipulator* CreateManipulator() const;
};

//对TextView接口进行转换使之匹配Shape
VoidTextShape::BoundingBox(Point& bottomLeft, Point& topRight) const
{
Coord bottom, left, width, height;

GetOrigin(bottom, left);
GetExtend(width, height);

bottomLeft = Point(bottom, left);
topRight = Point(bottom + height, left + width);
};

对象适配器

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
//对象适配器采用对象组合的方法将具有不同接口的类组合在一起。在该方法中,适配器TextShape维护一个指向TextView的指针
Class TextShape : public Shape
{
Public:
TextShape (TextView*);
Virtual void BoundingBox(Point& bottomLeft, Point& topRight)const;
Virtual bool IsEmpty() const;
Virtual Manipulator* CreateManipulator() const;
Private:
TextView* _text;
};


TextShape ::TextShape (TextView*)
{
_text = t;//初始化
};

VoidTextShape::BoundingBox(Point& bottomLeft, Point& topRight) const
{
Coord bottom, left, width, height;

_text->GetOrigin(bottom, left);
_text->GetExtend(width, height);

bottomLeft = Point(bottom, left);

topRight = Point(bottom + height, left + width);
};

设计模式-装饰

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 架构

目的

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。

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
Class VisualComponent
{
Public:
VisualComponent();

Virtual void Draw();
Virtual void Resize();
};


Class Decorator : public VisualComponent
{
Public:
Decorator(VisualComponent*);

Virtual void Draw();
Virtual void Resize();
};


Void Decorator::Draw()
{
Return _component->Draw();
};

Void Decorator::Resize()
{
Return _component->Resize();
};

Class BorderDecorator : public Decorator
{
Public:
BorderDecorator (VisualComponent*, int borderWidth);
Virtual void Draw();
Private:
void DrawBorder(int);
Private:
Int _width;
};


Void BorderDecorator::Draw()
{
Decorator::Draw();
DrawBorder(_width);
}

设计模式-组合

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 架构

目的

将对象组合成树形结构以表示“部分-整体”的层次结构。使得用户对单个对象和组合对象的使用具有一致性。

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
Class Equipment
{
Public:
Virtual ~Equipment();

Const char* Name() { return _name};

Virtual Watt Power();
Virtual Currency NetPrice();
Virtual Currency DiscountPrice();

Virtual void Add(Equipment*);
Virtual void Remove(Equipment*);
Virtual Iterator<Equipment*>* CreateIterator();
Protected:
Equipment(const char*);
Private:
Const char* _name;
};


Class FloppyDisk : public Equipment
{
Public:
FloppyDisk(const char*);
Virtual ~FloppyDisk ();

Virtual Watt Power();
Virtual Currency NetPrice();
Virtual Currency DiscountPrice();
};


Class CompositeEquipment : public Equipment
{
Public:
Virtual ~CompositeEquipment ();

Virtual Watt Power();
Virtual Currency NetPrice();
Virtual Currency DiscountPrice();

Virtual void Add(Equipment*);
Virtual void Remove(Equipment*);
Virtual Iterator<Equipment*>* CreateIterator();
Protected:
CompositeEquipment (const char*);
Private:
List<Equipment*> _equipment;
};


Currency CompositeEquipment::NetPrice()
{
Iterator<Equipment*>* I = CreateIterator();
Currency total = 0;
For(i->First(); !i->IsDone(); i->Next())
{
Total += i->CurrentItem()->NetPrice();
}
Delete I;
Return total;
};


Class Chassis : public CompositeEquipment
{
Public:
Chassis (const char*);
Virtual ~Chassis ();

Virtual Watt Power();
Virtual Currency NetPrice();
Virtual Currency DiscountPrice();
};


Cabinet* cabinet = new Cabinet();
Chassis* chassis = new Chassis();
Cabinet->Add(chassis);

Bus* bus = new Bus("MCA Bus");
Bus->Add(new Card("16Mbs Token Ring"));

Chassis->Add(bus);
Chassis->Add(new FloppyDisk("3.5in Floppy"));

Cout << "The net price is " << chassis->NetPrice() << endl;

设计模式-生成器

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 架构

目的

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

抽象工厂与生成器相似,主要的区别是生成器模式着重于一步步构造一个复杂对象。而抽象工厂着重于多个系列的产品对象。Builder在最后的一步返回,而对于Abstract Factory来说,产品是立即返回的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Class MazeBuilder
{
Public:
Virtual void BuildMaze();
Virtual void BuildRoom(int room);
Virtual void BuildDoor(int roomFrom, int roomTo);
Virtual Maze* GetMaze();
Protected:
MazeBuilder();
};


Maze*MazeGame::CreateMaze(MazeBuilder& build)
{
Builder.BuildMaze();
Builder.BuildRoom(1);
Builder.BuildRoom(2);
Builder.BuildDoor(1,2);
Return builder.GetMaze();
};

设计模式-桥接

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 架构

目的

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

动机

当一个抽象可能有多个实现时,通常用继承来协调它们。抽象类定义对该抽象的接口,而具体的子类则用不同方式加以实现。但是此方法有时不够灵活。继承机制将抽象部分与它的实现部分固定在一起,使得难以对抽象部分和实现部分独立地进行修改、扩充和重用。

代码示例

`
Class Window
{
Public:
Window(View contents);
Virtual void DrawContents();
Virual void Open();
Virual void Close();
Virual void Iconify();
Virual void Deiconify();
Virual void SetOrigin(const Point& at);
Virual void SetExtent(const Point& extent);
Virual void Raise();
Virual void Lower();
Virual void DrawLine(const Point&, const Point&);
Virual void DrawRect(const Point&, const Point&);
Virual void DrawPolygon(const Point[], int n);
Virual void DrawText(const char
, const Point&);

Protected:
WindowImp GetWindowImp();
View
GetView();
Private:
WindowImp _imp;
View
_contents;
};

//Window维护一个对WindowImp的引用, WindowImp抽象类定义了一个对底层窗口系统的接口。
Class WindowImp
{
Public:
Virtual void ImpTop() = 0;
Virtual void ImpBottom() = 0;
Virtual void ImpSetExtent(const Point&) = 0;
Virtual void ImpSetOrigin(const Point&) = 0;
Virtual void DeviceRect(Coord, Coord, Coord, Coord) = 0;
Virtual void DeviceText(const char, Coord, Coord) = 0;
Virtual void DeviceBitmap(const char
, Coord, Coord) = 0;
Protected:
WindowImp();
};

VoidWindow::DrawRect(const Point& p1, const Point& p2)
{
WindowImp* imp = GetWindowImp();
Imp->DeviceRect(p1.x(), p1.Y(), p2.X(), p2.Y());
};

设计模式-抽象工厂

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 架构

目的

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

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
Sample:
Interface IComputerFactory{
createMouse();
createOS();
};

Class Mouse{};

Class OS{};
//微软鼠标
Class MicrosoftMouse extends Mouse{};

//苹果鼠标
Class AppleMouse extends Mouse{};

//微软系统
Class MicrosoftOS extends OS{};

//苹果系统
Class AppleOS extends OS{};

Class MicrosoftFactory implements IComputerFactory{

Mouse createMouse(){
Return new MicrosoftMouse();//创建相应的鼠标品牌
};

OS createOS();
};



ClassAppleFactory implementsI IComputerFactory{
Mouse createMouse(){
Return new AppleMouse();//创建相应的鼠标品牌
};

OS createOS();
};

Classclient{

//具体工厂的抽象接口作为参数传入
createComputer(ComputerFactory factory){
//动态绑定到具体工厂,具体工厂生产相应的鼠标品牌
Mouse mouse = factory.createMouse();
OS os = factory.createOS();
}
};
1…8910…22

Paul Jiang

212 日志
26 分类
26 标签
Links
  • 褚霸
  • 章亦春
  • Martin Fowler
© 2020 Paul Jiang
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Gemini v6.5.0