// 其他对象必须能切换音频播放器当前所处的状态。 method changeState(state: State) is this.state = state
// UI 方法会将执行工作委派给当前状态。 method clickLock() is state.clickLock() method clickPlay() is state.clickPlay() method clickNext() is state.clickNext() method clickPrevious() is state.clickPrevious()
// 状态可调用上下文的一些服务方法。 method startPlayback() is // ... method stopPlayback() is // ... method nextSong() is // ... method previousSong() is // ... method fastForward(time) is // ... method rewind(time) is // ...
// 所有具体状态类都必须实现状态基类声明的方法,并提供反向引用指向与状态相 // 关的上下文对象。状态可使用反向引用将上下文转换为另一个状态。 abstractclassState is protected field player: AudioPlayer
// 上下文将自身传递给状态构造函数。这可帮助状态在需要时获取一些有用的 // 上下文数据。 constructor State(player) is this.player = player
// 容器可以将简单组件和其他容器作为其子项目。链关系将在这里建立。该类将从 // 其父类处继承 showHelp(显示帮助)的行为。 abstractclassContainerextendsComponent is protected field children: array of Component
method add(child) is children.add(child) child.container = this
// 原始组件应该能够使用帮助操作的默认实现... classButtonextendsComponent is // ...
// 但复杂组件可能会对默认实现进行重写。如果无法以新的方式来提供帮助文字, // 那组件总是还能调用基础实现的(参见 Component 类)。 classPanelextendsContainer is field modalHelpText: string
method showHelp() is if(modalHelpText != null) // 显示包含帮助文字的模态窗口。 else super.showHelp()
// ...同上... classDialogextendsContainer is field wikiPageURL: string
method showHelp() is if(wikiPageURL != null) // 打开百科帮助页面。 else super.showHelp()
// 具体中介者类可解开各组件之间相互交叉的连接关系并将其转移到中介者中。 classAuthenticationDialogimplementsMediator 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 // 创建所有组件对象并将当前中介者传递给其构造函数以建立连接。
// 当组件中有事件发生时,它会通知中介者。中介者接收到通知后可自行处理, // 也可将请求传递给另一个组件。 method notify(sender, event) is if(sender == loginOrRegisterChkBx and event == "check") if (loginOrRegisterChkBx.checked) title = "登录" // 1. 显示登录表单组件。 // 2. 隐藏注册表单组件。 else title = "注册" // 1. 显示注册表单组件。 // 2. 隐藏登录表单组件。
if (sender == okBtn && event == "click") if (loginOrRegister.checked) // 尝试找到使用登录信息的用户。 if (!found) // 在登录字段上方显示错误信息。 else // 1. 使用注册字段中的数据创建用户账号。 // 2. 完成用户登录工作。 …
// 组件会使用中介者接口与中介者进行交互。因此只需将它们与不同的中介者连接 // 起来,你就能在其他情境中使用这些组件了。 classComponent 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")
// 具体组件之间无法进行交流。它们只有一个交流渠道,那就是向中介者发送通知。 classButtonextendsComponent is // ...
classTextboxextendsComponent is // ...
classCheckboxextendsComponent is method check() is dialog.notify(this, "check") // ...
// 发布者基类包含订阅管理代码和通知方法。 classEventManager 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)
// 具体发布者包含一些订阅者感兴趣的实际业务逻辑。我们可以从发布者基类中扩 // 展出该类,但在实际情况下并不总能做到,因为具体发布者可能已经是子类了。 // 在这种情况下,你可用组合来修补订阅逻辑,就像我们在这里做的一样。 classEditor is public field events: EventManager private field file: File
constructor Editor()is events=newEventManager()
// 业务逻辑的方法可将变化通知给订阅者。 method openFile(path) is this.file = newFile(path) events.notify("open", file.name)
method saveFile() is file.write() events.notify("save", file.name)
// ...
// 这里是订阅者接口。如果你的编程语言支持函数类型,则可用一组函数来代替整 // 个订阅者的层次结构。 interfaceEventListener is method update(filename)
// 具体订阅者会对其注册的发布者所发出的更新消息做出响应。 classLoggingListenerimplementsEventListener is private field log: File private field message
constructor LoggingListener(log_filename, message) is this.log = newFile(log_filename) this.message = message
method update(filename) is log.write(replace('%s',filename,message))
classEmailAlertsListenerimplementsEventListener is private field email: string
constructor EmailAlertsListener(email, message) is this.email = email this.message = message
method update(filename) is system.email(email, replace('%s',filename,message))
// 应用程序可在运行时配置发布者和订阅者。 classApplication is method config()is editor=newEditor()
// 应用程序(Application)类可对集合和迭代器进行配置,然后将其传递给客户 // 端代码。 classApplication is field network: SocialNetwork field spammer: SocialSpammer
method config() is if working with WeChat this.network = newWeChat() if working with LinkedIn this.network = newLinkedIn() this.spammer = newSocialSpammer()
// 这里是具体命令。 classCopyCommandextendsCommand is // 复制命令不会被保存到历史记录中,因为它没有改变编辑器的状态。 method execute() is app.clipboard = editor.getSelection() returnfalse
classCutCommandextendsCommand is // 剪切命令改变了编辑器的状态,因此它必须被保存到历史记录中。只要方法 // 返回 true,它就会被保存。 method execute() is saveBackup() app.clipboard = editor.getSelection() editor.deleteSelection() returntrue
classPasteCommandextendsCommand is method execute() is saveBackup() editor.replaceSelection(app.clipboard) returntrue
// 撤销操作也是一个命令。 classUndoCommandextendsCommand is method execute() is app.undo() returnfalse
// 全局命令历史记录就是一个堆桟。 classCommandHistory is private field history: array of Command
// 后进... method push(c: Command) is // 将命令压入历史记录数组的末尾。
// ...先出 method pop():Command is // 从历史记录中取出最近的命令。
// 编辑器类包含实际的文本编辑操作。它会担任接收者的角色:最后所有命令都会 // 将执行工作委派给编辑器的方法。 classEditor is field text: string
method getSelection() is // 返回选中的文字。
method deleteSelection() is // 删除选中的文字。
method replaceSelection(text) is // 在当前位置插入剪贴板中的内容。
// 应用程序类会设置对象之间的关系。它会担任发送者的角色:当需要完成某些工 // 作时,它会创建并执行一个命令对象。 classApplication is field clipboard: string field editors: array of Editors field activeEditor: Editor field history: CommandHistory