持续创作,加速生长!这是我参加「日新方案 6 月更文应战」的第11天,点击查看活动详情

  • 本文首要介绍iOS设计形式中的组合形式,组合形式是一种部分-全体的层次结构,能够看成一种笼统集合

1. 什么事组合形式?

你有两类目标:产品盒子。一个盒子中能够包括多个产品或许几个较小的盒子。这些小盒子中相同能够包括一些产品或更小的盒子,以此类推。
假定你期望在这些类的基础上开发一个定购系统。订单中能够包括无包装的简略产品,也能够包括装满产品的盒子……以及其他盒子。此时你会怎么核算每张订单的总价格呢?
你能够尝试直接核算:打开一切盒子,找到每件产品,然后核算总价。这在实在国际中或许可行,但在程序中,你并不能简略地运用循环语句来完成该作业。你必须事前知道一切产品盒子的类别,一切盒子的嵌套层数以及其他繁杂的细节信息。因而,直接核算极不方便,乃至完全不可行。
组合形式主张运用一个通用接口来与产品盒子进行交互,并且在该接口中声明一个核算总价的办法。
那么办法该怎么设计呢?关于一个产品,该办法直接回来其价格;关于一个盒子,该办法遍历盒子中的一切项目,问询每个项目的价格,然后回来该盒子的总价格。如果其间某个项目是小一号的盒子,那么当前盒子也会遍历其间的一切项目,以此类推,直到核算出一切内部组成部分的价格。你乃至能够在盒子的终究价格中增加额定费用,作为该盒子的包装费用。
该方法的最大优点在于你无需了解构成树状结构的目标的详细类。你也无需了解目标是简略的产品仍是复杂的盒子。你只需调用通用接口以相同的方法对其进行处理即可。当你调用该办法后,目标会将恳求沿着树结构传递下去。
我么的会员系统相同是一种树状机构,咱们会员会有多个下线会员,一起也会有上线会员,也便是父节点和子节点。一起咱们界说一些行为比方卖货提成给父节点,经过迭代器核算恣意一个会员以及其下线会员的提成收入。
在面向目标软件设计中咱们借用类似的思维。组合结构能够非常复杂,并且其内部不应该暴漏给客户端。咱们需要经过一致的接口把整个复杂结构作为一个全体来运用,所以客户端不必知道某个节点事什么就能够运用它。

组合形式: 将目标组合成树形结构表明“部分-全体”的层次结构。组合使得用户对单个目标和组合的运用具有一致性。

2. 什么时候运用组合形式

在以下景象,自然会想到运用这一形式:

  • 想获得目标笼统树形表明(部分-全体层次结构);
  • 想让客户端一致处理组合结构中的一切目标。

3. Cocoa Touch框架中运用组合形式

在Cocoa Touch框架中,UIView被组织成一个组合结构。每个UIView的实例能够包括UIView的其他实例,形成一致的树状结构,让客户端对单个UIView目标UIView的组合一致对待。
视图组合结构参加绘图事件处理。当恳求超视图为显现进行渲染时,消息会先在超视图被处理,然后传给其子视图,消息会传播到广泛整个树的其他子视图。因为它们都是相同类型UIView,能够被一致处理,并且UIView层次结构的一个分支也能够相同作为一个视图来处理。
一致的视图也作为一个响应者链,用于事件处理和消息处理。

4. 代码展示

import XCTest
/// The base Component class declares common operations for both simple and
/// complex objects of a composition.
protocol Component {
    /// The base Component may optionally declare methods for setting and
    /// accessing a parent of the component in a tree structure. It can also
    /// provide some default implementation for these methods.
    var parent: Component? { get set }
    /// In some cases, it would be beneficial to define the child-management
    /// operations right in the base Component class. This way, you won't need
    /// to expose any concrete component classes to the client code, even during
    /// the object tree assembly. The downside is that these methods will be
    /// empty for the leaf-level components.
    func add(component: Component)
    func remove(component: Component)
    /// You can provide a method that lets the client code figure out whether a
    /// component can bear children.
    func isComposite() -> Bool
    /// The base Component may implement some default behavior or leave it to
    /// concrete classes.
    func operation() -> String
}
extension Component {
    func add(component: Component) {}
    func remove(component: Component) {}
    func isComposite() -> Bool {
        return false
    }
}
/// Leaf 类代表结束目标的组成。叶子不能有任何子类。
/// 一般,实际作业的是 Leaf 目标,而 Composite 目标只托付给它们的子组件。
class Leaf: Component {
    var parent: Component?
    func operation() -> String {
        return "Leaf"
    }
}
/// Composite 类表明可能有
/// 子级的复杂组件。一般,复合目标将实际作业托付给它们的
/// 子目标,然后“汇总”结果。
class Composite: Component {
    var parent: Component?
    /// This fields contains the conponent subtree.
    private var children = [Component]()
    /// A composite object can add or remove other components (both simple or
    /// complex) to or from its child list.
    func add(component: Component) {
        var item = component
        item.parent = self
        children.append(item)
    }
    func remove(component: Component) {
        // ...
    }
    func isComposite() -> Bool {
        return true
    }
    /// Composite 以特定方法履行其首要逻辑。它
    /// 递归遍历其一切子节点,收集和汇总
    /// 它们的结果。因为复合体的子代将这些调用传递给它们的
    /// 子代等等,因而会遍历整个目标树。
    func operation() -> String {
        let result = children.map({ $0.operation() })
        return "Branch(" + result.joined(separator: " ") + ")"
    }
}
class Client {
    /// The client code works with all of the components via the base interface.
    static func someClientCode(component: Component) {
        print("Result: " + component.operation())
    }
    /// Thanks to the fact that the child-management operations are also
    /// declared in the base Component class, the client code can work with both
    /// simple or complex components.
    static func moreComplexClientCode(leftComponent: Component, rightComponent: Component) {
        if leftComponent.isComposite() {
            leftComponent.add(component: rightComponent)
        }
        print("Result: " + leftComponent.operation())
    }
}
/// Let's see how it all comes together.
class CompositeConceptual: XCTestCase {
    func testCompositeConceptual() {
        /// This way the client code can support the simple leaf components...
        print("Client: I've got a simple component:")
        Client.someClientCode(component: Leaf())
        /// ...as well as the complex composites.
        let tree = Composite()
        let branch1 = Composite()
        branch1.add(component: Leaf())
        branch1.add(component: Leaf())
        let branch2 = Composite()
        branch2.add(component: Leaf())
        branch2.add(component: Leaf())
        tree.add(component: branch1)
        tree.add(component: branch2)
        print("\nClient: Now I've got a composite tree:")
        Client.someClientCode(component: tree)
        print("\nClient: I don't need to check the components classes even when managing the tree:")
        Client.moreComplexClientCode(leftComponent: tree, rightComponent: Leaf())
    }
}

履行结果

Client: I've got a simple component:
Result: Leaf
Client: Now I've got a composite tree:
Result: Branch(Branch(Leaf Leaf) Branch(Leaf Leaf))
Client: I don't need to check the components classes even when managing the tree:
Result: Branch(Branch(Leaf Leaf) Branch(Leaf Leaf) Leaf)

5. 总结

组合形式的首要目的是让树状结构中每个节点具有相同的笼统接口。这样整个结构可作为一个一致的笼统结构运用,而不暴漏其内部表明。对每个节点(叶节点或组合体)的任何操作,能够经过协议或笼统类中界说的相同接口来进行。组合形式总是跟着迭代器形式一起运用,以遍历组合目标中的每一个项目。