swift 听筒模式_Swift中的“复合”模式

swift 听筒模式

定义 (Definition)

‘Composite’ pattern is a structural design pattern that is useful for composing classes and objects into a larger system.

“复合”模式 是一种结构设计模式,可用于将类和对象组成更大的系统。

The ‘Composite’ pattern defines an interface for dealing with structures of objects uniformly. Structures are in the form of trees that contains individual objects and composition of objects.

“复合”模式定义用于统一处理对象结构的接口。 结构采用树木的形式,其中包含单个对象和对象组成。

我们什么时候应该使用这种模式? (When should we use this pattern?)

通过统一的界面组成多个对象 (To compose multiple objects through a unified interface)

This pattern should be used when we have a collection of objects that we treat uniformly. The client does not know the types of objects it deals with and it implies that all objects should conform to the same interface. It makes the client code simple because the types of objects become transparent to the client.

当我们有一组统一处理的对象时,应使用此模式。 客户端不知道它处理的对象类型,这意味着所有对象都应遵循相同的接口。 由于对象的类型对客户端透明,因此它使客户端代码变得简单。

将一组对象视为一个对象 (To treat a composition of objects as one)

This pattern should be used when we have a collection of objects with hierarchical relationships. The collection of objects forms a tree like structure with a hierarchy. The structure contains leaves (individual objects) and composites (composition of objects).

当我们具有具有层次关系的对象集合时,应使用此模式。 对象的集合形成具有层次结构的树状结构。 该结构包含叶子(单个对象)和复合材料(对象的组成)。

Image for post
image from Design Patterns — Elements of Reusable Object-Oriented Software by the Gang of Four
设计模式中的图像—四人帮的可重复使用的面向对象软件的元素

In this structure, a composite object executes operations from its children. It means leaf objects define behaviors and composite objects delegate the work to their children. At the end, the client thinks it communicates with one object, even though behind the interface we can have multiple objects.

在这种结构中,复合对象从其子对象执行操作。 这意味着叶对象定义行为,而复合对象将工作委托给子对象。 最后,客户认为它与一个对象通信,即使在接口后面我们可以有多个对象。

使对象知识与客户端脱钩 (To decouple objects knowledge from the client)

This pattern should be used when the client is aware of the types of objects it uses. By sharing an interface, objects do not need to be treated individually and it removes complexity from the application. The client does not need to check if it calls the right method on the right object: it calls the same method over an entire structure of objects. The ‘Composite’ pattern collects and manages objects for the client, which can work with any objects from the structure without any coupling.

当客户端知道其使用的对象类型时,应使用此模式。 通过共享接口,不需要单独对待对象,并且它消除了应用程序的复杂性。 客户端不需要检查是否在正确的对象上调用了正确的方法:它在整个对象结构上调用了相同的方法。 “复合”模式为客户端收集和管理对象,该对象可以与结构中的任何对象一起工作,而无需任何耦合。

我们应该如何使用这种模式? (How should we use this pattern?)

A client communicates with a collection of objects through an interface (Component) representing operations executed by leaves and composites.

客户端通过表示由叶子和合成执行的操作的接口( 组件 )与对象集合进行通信。

Image for post
image from Design Patterns — Elements of Reusable Object-Oriented Software by the Gang of Four
设计模式中的图像—四人帮的可重复使用的面向对象软件的元素

具体例子 (Concrete example)

Let’s say we have a mobile app that load data remotely or locally depending on the internet connection. Here is the UML diagram reflecting the structure above.

假设我们有一个移动应用程序,可以根据互联网连接远程或本地加载数据。 这是反映以上结构的UML图。

Image for post

We can use any of our leaf objects (LocalLoader or RemoteLoader) to load data for our app. If we make a remote request with a bad internet connection, we want to anticipate any failing requests by loading local data. We create a composite object (CompositeFallbackLoader) in order to provide a fallback when remote data fail to load.

我们可以使用任何叶子对象(LocalLoader或RemoteLoader)来加载应用程序的数据。 如果通过互联网连接发出远程请求,我们希望通过加载本地数据来预测任何失败的请求。 我们创建一个复合对象(CompositeFallbackLoader),以便在无法加载远程数据时提供备用。

实作 (Implementation)

protocol LoadData {
func load(completion: @escaping (Result<Data, Error>) -> Void)
}

In our example, we do not define the Add and Remove methods because we want to keep our composite object immutable by injecting individual objects in the constructor. We also do not use the getChild method since we do not have any use of it. Following the Interface Segregation Principle, we do not implement methods that our objects do not need.

在我们的示例中,我们没有定义Add和Remove方法,因为我们想通过将单个对象注入构造函数中来使复合对象保持不变。 我们也没有使用getChild方法,因为我们没有使用它。 遵循接口隔离原则,我们不会实现我们的对象不需要的方法。

If your use case needs those methods, we could throw exceptions as default implementations of our methods in order to force us to implement them. Leaves and composites play different roles, so it makes more sense if we do not write default implementations that do not make sense to an object. However, if the shared interface contains operations for both types of objects, we lose safety since an object might do something meaningless with some methods (for example, the Add method has no point being in a leaf). It is a design decision to separate responsibilities into interfaces. It would make the design safe because we will not call meaningless methods. However, we would lose transparency because the client needs to know the types of objects it communicates with. Depending on your needs, find the right balance between transparency and safety.

如果您的用例需要这些方法,我们可以抛出异常作为方法的默认实现,以迫使我们实现它们。 叶子和复合材料扮演着不同的角色,因此,如果我们不编写对对象没有意义的默认实现,那将更有意义。 但是,如果共享接口同时包含两种对象的操作,则由于对象可能会对某些方法执行无意义的操作(例如,Add方法在叶中没有意义),因此我们将失去安全性。 将职责划分为界面是一项设计决策。 这将使设计安全,因为我们不会调用无意义的方法。 但是,由于客户端需要知道与之通信的对象的类型,因此我们将失去透明度。 根据您的需求,在透明度和安全性之间找到适当的平衡。

叶对象 (Leaf objects)

class LoadRemoteData: LoadData {
func load(completion: @escaping (Result<Data, Error>) -> Void) {
completion(.failure(NSError(domain: "any error", code: 0)))
}
}class LoadLocalData: LoadData {
func load(completion: @escaping (Result<Data, Error>) -> Void) {
completion(.success("'my local data'".data(using: .utf8)!))
}
}

Each of the individual objects conforms to the LoadData interface. In this example, we fake a remote request failure and a successful local data retrieval to show the use of the composite object.

每个单独的对象都符合LoadData接口。 在此示例中,我们伪造了远程请求失败和成功的本地数据检索,以显示组合对象的使用。

复合对象 (Composite object)

class CompositeFallbackLoader: LoadData {
let remote: LoadData
let local: LoadData init(remote: LoadData, local: LoadData) {
self.remote = remote
self.local = local
} func load(completion: @escaping (Result<Data, Error>) -> Void) {
remote.load(completion: { [weak self] result in
switch
result {
case .success:
print("fetch \(result) remotely")
case .failure:
self?.retrieveLocalData()
}
})
} private func retrieveLocalData() {
local.load(completion: { localResult in
switch
localResult {
case let .success(data):
let myData = String(data: data, encoding: .utf8)!
print("fetch \(myData) locally")
case let .failure(error):
print(error)
}
})
}
}

The composite object conforms to the LoadData interface. It implements a fallback so when a remote request fails, data are retrieved locally.

复合对象符合LoadData接口。 它实现了后备功能,因此当远程请求失败时,将在本地检索数据。

在操场上运行代码 (Run code in a Playground)

Here is an Online Swift Playground so an Xcode Playground does not have to be created in order to test this implementation of the ‘Composite’ pattern. Then, copy the code below that corresponds with the full implementation of the ‘Composite’ pattern for our mobile app showing a failing remote request with a fallback on local data.

这是一个在线Swift游乐场,因此不必为了测试“复合”模式的实现而创建Xcode游乐场。 然后,复制下面的代码,该代码与我们的移动应用程序的“复合”模式的完整实现相对应,该模式显示失败的远程请求并回退了本地数据。

import Foundationprotocol LoadData {
func load(completion: @escaping (Result<Data, Error>) -> Void)
}class LoadRemoteData: LoadData {
func load(completion: @escaping (Result<Data, Error>) -> Void) {
completion(.failure(NSError(domain: "any error", code: 0)))
}
}class LoadLocalData: LoadData {
func load(completion: @escaping (Result<Data, Error>) -> Void) {
completion(.success("'my local data'".data(using: .utf8)!))
}
}class CompositeFallbackLoader: LoadData {
let remote: LoadData
let local: LoadData init(remote: LoadData, local: LoadData) {
self.remote = remote
self.local = local
} func load(completion: @escaping (Result<Data, Error>) -> Void) {
remote.load(completion: { [weak self] result in
switch
result {
case .success:
print("fetch \(result) remotely")
case .failure:
self?.retrieveLocalData()
}
})
} private func retrieveLocalData() {
local.load(completion: { localResult in
switch
localResult {
case let .success(data):
let myData = String(data: data, encoding: .utf8)!
print("fetch \(myData) locally")
case let .failure(error):
print(error)
}
})
}
}// Clientlet composite = CompositeFallbackLoader(
remote: LoadRemoteData(),
local: LoadLocalData()
)composite.load(completion: { result in
switch
result {
case let .success(data):
print(data)
case let .failure(error):
print(error)
}
})

Finally, paste and run the code.

最后,粘贴并运行代码。

翻译自: https://levelup.gitconnected.com/composite-pattern-in-swift-8c7b932fc157

swift 听筒模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值