软件系统设计——设计模式
Contents
桥接模式(Bridge Pattern)
Bridge is a structural design pattern that lets you split a large class or a set of closely related classes into two separate hierarchies—abstraction and implementation—which can be developed independently of each other.

理解桥接模式,需要理解如何分离抽象化和实现化,使得二者脱耦,可以独立变化
Adding new shape types and colors to the hierarchy will grow it exponentially. For example, to add a triangle shape you’d need to introduce two subclasses, one for each color. And after that, adding a new color would require creating three subclasses, one for each shape type. The further we go, the worse it becomes.
The Bridge pattern attempts to solve this problem by switching from inheritance to the object composition. What this means is that you extract one of the dimensions into a separate class hierarchy, so that the original classes will reference an object of the new hierarchy, instead of having all of its state and behaviors within one class

In a worst-case scenario, this app might look like a giant spaghetti bowl, where hundreds of conditionals connect different types of GUI with various APIs all over the code.

装饰模式(decorator)
Imagine that you’re working on a notification library which lets other programs notify their users about important events.
The initial version of the library was based on the Notifier
class that had only a few fields, a constructor and a single send
method. The method could accept a message argument from a client and send the message to a list of emails that were passed to the notifier via its constructor. A third-party app which acted as a client was supposed to create and configure the notifier object once, and then use it each time something important happened.

At some point, you realize that users of the library expect more than just email notifications. Many of them would like to receive an SMS about critical issues. Others would like to be notified on Facebook and, of course, the corporate users would love to get Slack notifications.

How hard can that be? You extended the Notifier
class and put the additional notification methods into new subclasses. Now the client was supposed to instantiate the desired notification class and use it for all further notifications.
But then someone reasonably asked you, “Why can’t you use several notification types at once? If your house is on fire, you’d probably want to be informed through every channel.”

One of the ways to overcome these caveats is by using Aggregation or Composition
instead of Inheritance. Both of the alternatives work almost the same way: one object has a reference to another and delegates it some work, whereas with inheritance, the object itself is able to do that work, inheriting the behavior from its superclass.
With this new approach you can easily substitute the linked “helper” object with another, changing the behavior of the container at runtime. An object can use the behavior of various classes, having references to multiple objects and delegating them all kinds of work. Aggregation/composition is the key principle behind many design patterns, including Decorator. On that note, let’s return to the pattern discussion.

观察者模式(Observer)
Imagine that you have two types of objects: a Customer
and a Store
. The customer is very interested in a particular brand of product (say, it’s a new model of the iPhone) which should become available in the store very soon.

The customer could visit the store every day and check product availability. But while the product is still en route, most of these trips would be pointless.

That’s why it’s crucial that all subscribers implement the same interface and that the publisher communicates with them only via that interface. This interface should declare the notification method along with a set of parameters that the publisher can use to pass some contextual data along with the notification.

观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式是一种对象行为型模式。
这一模式中的关键对象是观察目标和观察者,一个目标可以有任意数目的与之相依赖的观察者,一旦目标的状态发生改变,所有的观察者都将得到通知。
观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
并且在观察目标和观察者之间建立了一个抽象的耦合,符合开闭原则的要求
缺点(fallbacks):如果一个观察目标对象有很多直接和间接的观察者的话,将所
有的观察者都通知到会花费很多时间。
观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

Pseudocode(伪代码)

// The base publisher class includes subscription management
// code and notification methods.
class EventManager is
private field listeners: hash map of event types and listeners
method subscribe(eventType, listener) is
listeners.add(eventType, listener)
method unsubscribe(eventType, listener) is
listeners.remove(eventType, listener)
method notify(eventType, data) is
foreach (listener in listeners.of(eventType)) do
listener.update(data)
// The concrete publisher contains real business logic that's
// interesting for some subscribers. We could derive this class
// from the base publisher, but that isn't always possible in
// real life because the concrete publisher might already be a
// subclass. In this case, you can patch the subscription logic
// in with composition, as we did here.
class Editor is
public field events: EventManager
private field file: File
constructor Editor() is
events = new EventManager()
// Methods of business logic can notify subscribers about
// changes.
method openFile(path) is
this.file = new File(path)
events.notify("open", file.name)
method saveFile() is
file.write()
events.notify("save", file.name)
// ...
// Here's the subscriber interface. If your programming language
// supports functional types, you can replace the whole
// subscriber hierarchy with a set of functions.
interface EventListener is
method update(filename)
// Concrete subscribers react to updates issued by the publisher
// they are attached to.
class LoggingListener implements EventListener is
private field log: File
private field message: string
constructor LoggingListener(log_filename, message) is
this.log = new File(log_filename)
this.message = message
method update(filename) is
log.write(replace('%s',filename,message))
class EmailAlertsListener implements EventListener is
private field email: string
private field message: string
constructor EmailAlertsListener(email, message) is
this.email = email
this.message = message
method update(filename) is
system.email(email, replace('%s',filename,message))
// An application can configure publishers and subscribers at
// runtime.
class Application is
method config() is
editor = new Editor()
logger = new LoggingListener(
"/path/to/log.txt",
"Someone has opened the file: %s")
editor.events.subscribe("open", logger)
emailAlerts = new EmailAlertsListener(
"admin@example.com",
"Someone has changed the file: %s")
editor.events.subscribe("save", emailAlerts)
change manager
当目标和观察者间的依赖关系特别复杂时, 可能需要一个维护这些关系的对象。我们称这样的对象为更改管理器(Change Manager)

观察者模式扩展
MVC模式
- MVC模式是一种架构模式,它包含三个角色:模型(Model),视图(View)和控制器(Controller)。观察者模式可以用来实现MVC模式,观察者模式中的观察目标就
是MVC模式中的模型(Model),而观察者就是MVC中的视图(View),控制器(Controller)充当两者之间的中介者(Mediator)。当模型层的数据发生改变时,视图层将自动改变其显示内容。
中介者模式(Mediator)
Mediator is a behavioral design pattern that lets you reduce chaotic dependencies between objects. The pattern restricts direct communications between the objects and forces them to collaborate only via a mediator object.

example: A Profile Dialog
Say you have a dialog for creating and editing customer profiles. It consists of various form controls such as text fields, checkboxes, buttons, etc.

Some of the form elements may interact with others. For instance, selecting the “I have a dog” checkbox may reveal a hidden text field for entering the dog’s name. Another example is the submit button that has to validate values of all fields before saving the data.

模式动机
- 在面向对象的软件设计与开发过程中,根据“单一职责原则”,我们应该尽量将对象细化,使其只负责或呈现单一的职责。
- 对于一个模块,可能由很多对象构成,而且这些对象之间可能存在相互的引用,为了减少对象两两之间复杂的引用关系,使之成为一个松耦合的系统,我们需要使用中介者模式,这就是中介者模式的模式动机。
The Mediator pattern suggests that you should cease all direct communication between the components which you want to make independent of each other. Instead, these components must collaborate indirectly, by calling a special mediator object that redirects the calls to appropriate components. As a result, the components depend only on a single mediator class instead of being coupled to dozens of their colleagues.
In our example with the profile editing form, the dialog class itself may act as the mediator. Most likely, the dialog class is already aware of all of its sub-elements, so you won’t even need to introduce new dependencies into this class.

最显著的更改发生在实际的表单元素上。让我们考虑一下提交按钮。以前,每次用户单击该按钮时,它都必须验证所有单个表单元素的值。现在,它的唯一工作是通知对话框有关单击的信息。收到此通知后,对话框本身会执行验证或将任务传递给各个元素。因此,按钮不依赖于十几个表单元素,而只依赖于 dialog 类。
You can go further and make the dependency even looser by extracting the common interface for all types of dialogs. The interface would declare the notification method which all form elements can use to notify the dialog about events happening to those elements. Thus, our submit button should now be able to work with any dialog that implements that interface.


Real-World Analogy
真实世界的类比


组件是包含一些业务逻辑的各种类。每个组件都有一个对中介器的引用,该中介器使用中介器接口的类型声明。该组件不知道 mediator 的实际类,因此您可以通过将组件链接到不同的 mediator 来在其他程序中重用该组件。
Pseudocode
伪代码
// The mediator interface declares a method used by components
// to notify the mediator about various events. The mediator may
// react to these events and pass the execution to other
// components.
interface Mediator is
method notify(sender: Component, event: string)
// The concrete mediator class. The intertwined web of
// connections between individual components has been untangled
// and moved into the mediator.
class AuthenticationDialog implements Mediator is
private field title: string
private field loginOrRegisterChkBx: Checkbox
private field loginUsername, loginPassword: Textbox
private field registrationUsername, registrationPassword,
registrationEmail: Textbox
private field okBtn, cancelBtn: Button
constructor AuthenticationDialog() is
// Create all component objects by passing the current
// mediator into their constructors to establish links.
// When something happens with a component, it notifies the
// mediator. Upon receiving a notification, the mediator may
// do something on its own or pass the request to another
// component.
method notify(sender, event) is
if (sender == loginOrRegisterChkBx and event == "check")
if (loginOrRegisterChkBx.checked)
title = "Log in"
// 1. Show login form components.
// 2. Hide registration form components.
else
title = "Register"
// 1. Show registration form components.
// 2. Hide login form components
if (sender == okBtn && event == "click")
if (loginOrRegister.checked)
// Try to find a user using login credentials.
if (!found)
// Show an error message above the login
// field.
else
// 1. Create a user account using data from the
// registration fields.
// 2. Log that user in.
// ...
// Components communicate with a mediator using the mediator
// interface. Thanks to that, you can use the same components in
// other contexts by linking them with different mediator
// objects.
class Component is
field dialog: Mediator
constructor Component(dialog) is
this.dialog = dialog
method click() is
dialog.notify(this, "click")
method keypress() is
dialog.notify(this, "keypress")
// Concrete components don't talk to each other. They have only
// one communication channel, which is sending notifications to
// the mediator.
class Button extends Component is
// ...
class Textbox extends Component is
// ...
class Checkbox extends Component is
method check() is
dialog.notify(this, "check")
// ...
中介者承担两方面的职责:
- 中转作用(结构性):通过中介者提供的中转作用,各个同事对象就不再需要显式引用其他同事,当需要和其他同事进行通信时,通过中介者即可。该中转作用属于中介者在结构上的支持。
- 协调作用(行为性):中介者可以更进一步的对同事之间的关系进行封装,同事可以一致地和中介者进行交互,而不需要指明中介者需要具体怎么做,中介者根据封装在自身内部的协调逻辑,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装。该协调作用属于中介者在行为上的支持。

中介者模式的优点
- 简化了对象之间的交互。
- 将各同事解耦。
- 减少子类生成。
- 可以简化各同事类的设计和实现。
中介者模式的缺点
- 在具体中介者类中包含了同事之间的交互细节,可能会
导致具体中介者类非常复杂,使得系统难以维护。
在中介者模式中,通过创造出一个中介者对象,将系统中有关的对象所引用的其他对象数目减少到最少,使得一个对象与其同事之间的相互作用被这个对象与中介者对象之间的相互作用所取代。因此,中介者模式就是迪米特法则的一个典型应用
模板方法模式(Template Method)
Template Method is a behavioral design pattern that defines the skeleton of an algorithm in the superclass but lets subclasses override specific steps of the algorithm without changing its structure.

example
Imagine that you’re creating a data mining application that analyzes corporate documents. Users feed the app documents in various formats (PDF, DOC, CSV), and it tries to extract meaningful data from these docs in a uniform format.

At some point, you noticed that all three classes have a lot of similar code. While the code for dealing with various data formats was entirely different in all classes, the code for data processing and analysis is almost identical. Wouldn’t it be great to get rid of the code duplication, leaving the algorithm structure intact?
The Template Method pattern suggests that you break down an algorithm into a series of steps, turn these steps into methods, and put a series of calls to these methods inside a single template method. The steps may either be abstract
, or have some default implementation. To use the algorithm, the client is supposed to provide its own subclass, implement all abstract steps, and override some of the optional ones if needed (but not the template method itself).

正如你所看到的那样, 我们有两种类型的步骤:
- 抽象步骤必须由各个子类来实现
- 可选步骤已有一些默认实现, 但仍可在需要时进行重写
还有另一种名为钩子的步骤。 钩子是内容为空的可选步骤。 即使不重写钩子, 模板方法也能工作。 钩子通常放置在算法重要步骤的前后, 为子类提供额外的算法扩展点。

游戏中所有的种族都有几乎同类的单位和建筑。 因此你可以在不同的种族上复用相同的 AI 结构, 同时还需要具备重写一些细节的能力。 通过这种方式, 你可以重写半兽人的 AI 使其更富攻击性, 也可以让人类侧重防守, 还可以禁止怪物建造建筑。 在游戏中新增种族需要创建新的 AI 子类, 还需要重写 AI 基类中所声明的默认方法。
class GameAI is
// 模板方法定义了某个算法的框架。
method turn() is
collectResources()
buildStructures()
buildUnits()
attack()
// 某些步骤可在基类中直接实现。
method collectResources() is
foreach (s in this.builtStructures) do
s.collect()
// 某些可定义为抽象类型。
abstract method buildStructures()
abstract method buildUnits()
// 一个类可包含多个模板方法。
method attack() is
enemy = closestEnemy()
if (enemy == null)
sendScouts(map.center)
else
sendWarriors(enemy.position)
abstract method sendScouts(position)
abstract method sendWarriors(position)
// 具体类必须实现基类中的所有抽象操作,但是它们不能重写模板方法自身。
class OrcsAI extends GameAI is
method buildStructures() is
if (there are some resources) then
// 建造农场,接着是谷仓,然后是要塞。
method buildUnits() is
if (there are plenty of resources) then
if (there are no scouts)
// 建造苦工,将其加入侦查编组。
else
// 建造兽族步兵,将其加入战士编组。
// ……
method sendScouts(position) is
if (scouts.length > 0) then
// 将侦查编组送到指定位置。
method sendWarriors(position) is
if (warriors.length > 5) then
// 将战斗编组送到指定位置。
// 子类可以重写部分默认的操作。
class MonstersAI extends GameAI is
method collectResources() is
// 怪物不会采集资源。
method buildStructures() is
// 怪物不会建造建筑。
method buildUnits() is
// 怪物不会建造单位。
工厂模式
简单工厂模式
简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
模式结构
- 简单工厂模式包含如下角色:
- Factory:工厂角色
- Product:抽象产品角色
- ConcreteProduct:具体产品角色
模式实例与解析
- 实例:权限管理
- 在某OA系统中,系统根据对比用户在登录时输入的账
号和密码以及在数据库中存储的账号和密码是否一致来
进行身份验证,如果验证通过,则取出存储在数据库中
的用户权限等级(以整数形式存储),根据不同的权限
等级创建不同等级的用户对象,不同等级的用户对象拥
有不同的操作权限。现使用简单工厂模式来设计该权限
管理模块。
