Swift中的App架构和对象组成

对象组合是面向对象编程的核心概念。 对象可以包含其他对象,有时会创建复杂的层次结构。

在本文中,我想展示如何设计这种架构。 在一个实际的示例中,您将看到处理复杂的层次结构很简单,而对象和层次结构可以重用。

面向对象的设计过程包括规划对象如何连接和交互以创建零件或完整系统。 我们都习惯了Model-View-Controller体系结构,该体系结构定义了包含视图和模型的控制器对象。 这些组成部分共同构成了应用程序的基础。

形成树结构的递归类型使事情变得有趣。

递归组合很简单,可以用来表示任何潜在的复杂层次结构。

组合模式是“设计模式:可重用的面向对象软件的元素”一书中描述的结构模式。 它描述了如何构建由两种对象的类组成的类层次结构:原始对象和复合对象。

将对象组合到树结构中以表示部分整个层次结构。 复合可以使客户统一对待单个对象和对象组成。

设计模式:可重用的面向对象软件的元素

复合模式定义了下一个参与者:

  • 组件是一个抽象接口。 它声明函数并由客户端使用。
  • Leaf实现了由Component接口声明的功能。
  • 复合工具组成。 它包含子组件数组。 Composite通过委派(转发)对其子级的调用来实现Component接口声明的功能。
  • 客户端是通过Component接口使用合成的代码。

我们可以像这样实现Composite模式:

现实生活中的实现可能会略有不同。 常见的变化是Component,Leaf和Composite是同一对象。

使用树结构进行组合是一种非常强大的模式,它对应用程序体系结构具有很多好处:

  • 将复杂的任务分解为小部分。 组件解决小任务。 组成一棵树以解决更大的任务。
  • 与复杂结构进行交互的方式与与单个实例进行交互的方式相同。 组件树具有与单个组件相同的接口。
  • 单一责任原则。 组件负责单个功能。 它可以是特定于域的,也可以是特定于组成的(例如按特定顺序安排孩子)。
  • 可重用性。 组件是可重用的,可以在更大的层次结构中使用组件树来解决更大的任务。
  • 可测试性。 由于组件和组件树是独立的,因此可以进行单元测试。
  • 应用各种设计模式的机会。 树结构可以很好地与各种创建,结构和行为设计模式配合使用。
  • 简单。 最后但并非最不重要的。 在特殊情况下,复合模式只需要一个类即可实现。 树和树上的操作对于开发人员是众所周知的。

难以置信? 让我们看一些例子。

UIView

UIView的层次结构可能是树结构最突出的示例。

视图是应用程序用户界面的基本构建块。 可以将视图嵌套在其他视图中以创建视图层次结构。 例如,我们使用小的UIView节点构造复杂的树。

我们可以按功能对视图进行分组:

  • 基础UIView类。 它定义了所有视图共有的行为,呈现内容以及处理与该内容的任何交互。
  • 显示内容。 UILabelUIImageViewUIButton等用于显示特定于域的内容。 该视图通常是叶子( UILabelUIImageView)并且可以是复合视图( UIButtonUITextField)
  • 管理子视图。 UIScrollViewUITableViewUICollectionView等用于安排其他视图。 UIStackView是一个很好的例子—此视图是UIView的非渲染子类,仅用于管理布局。

UIViewController

从iOS 5.0开始, UIViewController的表单层次结构类似于UIView的表单层次结构。

Apple定义了两种类型的视图控制器:

  • 显示内容。 内容视图控制器管理应用程序内容的离散部分。 这是您的UIViewController子类。
  • 促进结构和导航。 容器视图控制器以特定方式安排子视图控制器。 UINavigationControllerUITabBarControllerUISplitViewController等,这些控制器定义应用程序的导航和结构。

我们再一次看到一棵树,叶子在其中实现了特定于域的功能。

异步执行,GCD

如“四人帮”一书中所述,复合模式的原始动机是图形应用程序和文档。 但是应用程序不仅仅可以解决异步问题?

编写异步代码时的常见挑战是控制执行顺序和完成。 假设我们有一个任务,可以使用闭包(例如,网络操作)执行异步工作并完成报告。

我们有要执行的任务列表,我们想知道所有任务何时完成。

一种解决方案是使用DispatchGroup ,如下所示:

这是应用合成的机会。 让我们为需要单个完成处理程序的一组任务创建一个类:

现在,我们需要做的就是提供要运行的任务列表:

从这里,我们可以使用小的构建块来构建异步任务的复杂层次结构,以解决一个大问题。 如果需要,可以创建有序的复合任务类。 这些类是简单且可重用的。

WWDC 2015上有一个关于将合成与NSOperation结合使用的精彩会议-Advanced NSOperations。

持久获取数据

组合的另一个很好的例子是获取和持久化数据。 假设我们要获取一个模型,并且有两个来源:本地和远程。 我们想优先考虑本地商店。 如果模型不存在,请从网络加载模型。 这是我们如何使用组合来组织它们:

CompositeStore遍历存储列表,直到获取结果模型。 DispatchSemaphore用于等待每个提取操作完成后再继续。 由于在实例化CompositeStore时定义了订单,因此它将首先尝试从本地商店加载模型,如果失败则从网络加载模型。

UI数据源

组合最有趣的应用之一是UITableViewUICollectionViewMKMapView数据源协议。

实现这种组合具有挑战性,但是如果您基于其中一种视图拥有复杂的UI,则肯​​定会有所收获。

具有“集合视图” WWDC的“高级用户界面”会话中涵盖了该主题。

如您所见,组合和递归结构具有广泛的适用性。 此处未涵盖的内容包括格式设置,输入验证,观察,数据处理,状态机等。

复合模式非常简单,在某些情况下使用单个类实现。 要记住的一件事是遵循角色。 组件定义接口,Leaf实现小的功能,然后Composite将所有内容组合在一起。

对于具有树结构的更高级的体系结构,计算机科学非常有帮助。 花一些时间学习树上的基本算法。 特别是树遍历:广度优先搜索和深度优先搜索。 从长远来看,这将获得回报。

最后,归结为找出复杂的问题并分解为较小的任务。 这是一项艰巨的任务,但这是善意与善意之间的区别。