![中介者](/images/patterns/cards/mediator-mini.png?id=a7e43ee8e17e4474737b1fcb3201d7ba)
Swift 中介者模式讲解和代码示例
中介者是一种行为设计模式, 让程序组件通过特殊的中介者对象进行间接沟通, 达到减少组件之间依赖关系的目的。
中介者能使得程序更易于修改和扩展, 而且能更方便地对独立的组件进行复用, 因为它们不再依赖于很多其他的类。
复杂度:
流行度:
使用示例: 中介者模式在 Swift 代码中最常用于帮助程序 GUI 组件之间的通信。 在 MVC 模式中, 控制器是中介者的同义词。
以下示例可在
Swift Playgrounds
上使用。
感谢
Alejandro Mohamad 创建了Playground版本。
概念示例
本例说明了中介者设计模式的结构并重点回答了下面的问题:
- 它由哪些类组成?
- 这些类扮演了哪些角色?
- 模式中的各个元素会以何种方式相互关联?
了解该模式的结构后, 你可以更容易地理解下面基于真实世界的 Swift 应用案例。
Example.swift: 概念示例
import XCTest
/// 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.
protocol Mediator: AnyObject {
func notify(sender: BaseComponent, event: String)
}
/// Concrete Mediators implement cooperative behavior by coordinating several
/// components.
class ConcreteMediator: Mediator {
private var component1: Component1
private var component2: Component2
init(_ component1: Component1, _ component2: Component2) {
self.component1 = component1
self.component2 = component2
component1.update(mediator: self)
component2.update(mediator: self)
}
func notify(sender: BaseComponent, event: String) {
if event == "A" {
print("Mediator reacts on A and triggers following operations:")
self.component2.doC()
}
else if (event == "D") {
print("Mediator reacts on D and triggers following operations:")
self.component1.doB()
self.component2.doC()
}
}
}
/// The Base Component provides the basic functionality of storing a mediator's
/// instance inside component objects.
class BaseComponent {
fileprivate weak var mediator: Mediator?
init(mediator: Mediator? = nil) {
self.mediator = mediator
}
func update(mediator: Mediator) {
self.mediator = mediator
}
}
/// Concrete Components implement various functionality. They don't depend on
/// other components. They also don't depend on any concrete mediator classes.
class Component1: BaseComponent {
func doA() {
print("Component 1 does A.")
mediator?.notify(sender: self, event: "A")
}
func doB() {
print("Component 1 does B.\n")
mediator?.notify(sender: self, event: "B")
}
}
class Component2: BaseComponent {
func doC() {
print("Component 2 does C.")
mediator?.notify(sender: self, event: "C")
}
func doD() {
print("Component 2 does D.")
mediator?.notify(sender: self, event: "D")
}
}
/// Let's see how it all works together.
class MediatorConceptual: XCTestCase {
func testMediatorConceptual() {
let component1 = Component1()
let component2 = Component2()
let mediator = ConcreteMediator(component1, component2)
print("Client triggers operation A.")
component1.doA()
print("\nClient triggers operation D.")
component2.doD()
print(mediator)
}
}
Output.txt: 执行结果
Client triggers operation A.
Component 1 does A.
Mediator reacts on A and triggers following operations:
Component 2 does C.
Client triggers operation D.
Component 2 does D.
Mediator reacts on D and triggers following operations:
Component 1 does B.
Component 2 does C.
真实世界示例
Example.swift: 真实世界示例
import XCTest
class MediatorRealWorld: XCTestCase {
func test() {
let newsArray = [News(id: 1, title: "News1", likesCount: 1),
News(id: 2, title: "News2", likesCount: 2)]
let numberOfGivenLikes = newsArray.reduce(0, { $0 + $1.likesCount })
let mediator = ScreenMediator()
let feedVC = NewsFeedViewController(mediator, newsArray)
let newsDetailVC = NewsDetailViewController(mediator, newsArray.first!)
let profileVC = ProfileViewController(mediator, numberOfGivenLikes)
mediator.update([feedVC, newsDetailVC, profileVC])
feedVC.userLikedAllNews()
feedVC.userDislikedAllNews()
}
}
class NewsFeedViewController: ScreenUpdatable {
private var newsArray: [News]
private weak var mediator: ScreenUpdatable?
init(_ mediator: ScreenUpdatable?, _ newsArray: [News]) {
self.newsArray = newsArray
self.mediator = mediator
}
func likeAdded(to news: News) {
print("News Feed: Received a liked news model with id \(news.id)")
for var item in newsArray {
if item == news {
item.likesCount += 1
}
}
}
func likeRemoved(from news: News) {
print("News Feed: Received a disliked news model with id \(news.id)")
for var item in newsArray {
if item == news {
item.likesCount -= 1
}
}
}
func userLikedAllNews() {
print("\n\nNews Feed: User LIKED all news models")
print("News Feed: I am telling to mediator about it...\n")
newsArray.forEach({ mediator?.likeAdded(to: $0) })
}
func userDislikedAllNews() {
print("\n\nNews Feed: User DISLIKED all news models")
print("News Feed: I am telling to mediator about it...\n")
newsArray.forEach({ mediator?.likeRemoved(from: $0) })
}
}
class NewsDetailViewController: ScreenUpdatable {
private var news: News
private weak var mediator: ScreenUpdatable?
init(_ mediator: ScreenUpdatable?, _ news: News) {
self.news = news
self.mediator = mediator
}
func likeAdded(to news: News) {
print("News Detail: Received a liked news model with id \(news.id)")
if self.news == news {
self.news.likesCount += 1
}
}
func likeRemoved(from news: News) {
print("News Detail: Received a disliked news model with id \(news.id)")
if self.news == news {
self.news.likesCount -= 1
}
}
}
class ProfileViewController: ScreenUpdatable {
private var numberOfGivenLikes: Int
private weak var mediator: ScreenUpdatable?
init(_ mediator: ScreenUpdatable?, _ numberOfGivenLikes: Int) {
self.numberOfGivenLikes = numberOfGivenLikes
self.mediator = mediator
}
func likeAdded(to news: News) {
print("Profile: Received a liked news model with id \(news.id)")
numberOfGivenLikes += 1
}
func likeRemoved(from news: News) {
print("Profile: Received a disliked news model with id \(news.id)")
numberOfGivenLikes -= 1
}
}
protocol ScreenUpdatable: class {
func likeAdded(to news: News)
func likeRemoved(from news: News)
}
class ScreenMediator: ScreenUpdatable {
private var screens: [ScreenUpdatable]?
func update(_ screens: [ScreenUpdatable]) {
self.screens = screens
}
func likeAdded(to news: News) {
print("Screen Mediator: Received a liked news model with id \(news.id)")
screens?.forEach({ $0.likeAdded(to: news) })
}
func likeRemoved(from news: News) {
print("ScreenMediator: Received a disliked news model with id \(news.id)")
screens?.forEach({ $0.likeRemoved(from: news) })
}
}
struct News: Equatable {
let id: Int
let title: String
var likesCount: Int
/// Other properties
static func == (left: News, right: News) -> Bool {
return left.id == right.id
}
}
Output.txt: 执行结果
News Feed: User LIKED all news models
News Feed: I am telling to mediator about it...
Screen Mediator: Received a liked news model with id 1
News Feed: Received a liked news model with id 1
News Detail: Received a liked news model with id 1
Profile: Received a liked news model with id 1
Screen Mediator: Received a liked news model with id 2
News Feed: Received a liked news model with id 2
News Detail: Received a liked news model with id 2
Profile: Received a liked news model with id 2
News Feed: User DISLIKED all news models
News Feed: I am telling to mediator about it...
ScreenMediator: Received a disliked news model with id 1
News Feed: Received a disliked news model with id 1
News Detail: Received a disliked news model with id 1
Profile: Received a disliked news model with id 1
ScreenMediator: Received a disliked news model with id 2
News Feed: Received a disliked news model with id 2
News Detail: Received a disliked news model with id 2
Profile: Received a disliked news model with id 2