// 容器可以将简单组件和其他容器作为其子项目。链关系将在这里建立。该类将从 // 其父类处继承 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()
// 这里是具体命令。 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
// 应用程序(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()
// 远程服务接口。 interfaceThirdPartyTVLib is method listVideos() method getVideoInfo(id) method downloadVideo(id)
// 服务连接器的具体实现。该类的方法可以向腾讯视频请求信息。请求速度取决于 // 用户和腾讯视频的互联网连接情况。如果同时发送大量请求,即使所请求的信息 // 一模一样,程序的速度依然会减慢。 classThirdPartyTVClassimplementsThirdPartyTVLib is method listVideos() is // 向腾讯视频发送一个 API 请求。
method getVideoInfo(id) is // 获取某个视频的元数据。
method downloadVideo(id) is // 从腾讯视频下载一个视频文件。
// 为了节省网络带宽,我们可以将请求结果缓存下来并保存一段时间。但你可能无 // 法直接将这些代码放入服务类中。比如该类可能是第三方程序库的一部分或其签 // 名是`final(最终)`。因此我们会在一个实现了服务类接口的新代理类中放入 // 缓存代码。当代理类接收到真实请求后,才会将其委派给服务对象。 classCachedTVClassimplementsThirdPartyTVLib is private field service: ThirdPartyTVLib private field listCache, videoCache field needReset
constructor CachedTVClass(service: ThirdPartyTVLib) is this.service = service
// 享元类包含一个树的部分状态。这些成员变量保存的数值对于特定树而言是唯一 // 的。例如,你在这里找不到树的坐标。但这里有很多树木之间所共有的纹理和颜 // 色。由于这些数据的体积通常非常大,所以如果让每棵树都其进行保存的话将耗 // 费大量内存。因此,我们可将纹理、颜色和其他重复数据导出到一个单独的对象 // 中,然后让众多的单个树对象去引用它。 classTreeType is field name field color field texture constructor TreeType(name, color, texture) { ... } method draw(canvas, x, y) is // 1. 创建特定类型、颜色和纹理的位图。 // 2. 在画布坐标 (X,Y) 处绘制位图。
// 享元工厂决定是否复用已有享元或者创建一个新的对象。 classTreeFactory is static field treeTypes: collection of tree types static method getTreeType(name, color, texture)is type= treeTypes.find(name, color, texture) if (type == null) type = newTreeType(name, color, texture) treeTypes.add(type) return type
// 情景对象包含树状态的外在部分。程序中可以创建数十亿个此类对象,因为它们 // 体积很小:仅有两个整型坐标和一个引用成员变量。 classTree is field x,y field type: TreeType constructor Tree(x, y, type) { ... } method draw(canvas) is type.draw(canvas, this.x, this.y)
// 树(Tree)和森林(Forest)类是享元的客户端。如果不打算继续对树类进行开 // 发,你可以将它们合并。 classForest is field trees: collection of Trees
method plantTree(x, y, name, color, texture)is type= TreeFactory.getTreeType(name, color, texture) tree = newTree(x, y, type) trees.add(tree)
method draw(canvas) is foreach(tree in trees)do tree.draw(canvas)