工厂模式和抽象工厂模式
Optional views are present in many apps of all kinds. There are 1000 reasons for needing to show or hide a view based on a condition such as app state, user profile, feature/product availability, A/B testing, etc.
在各种类型的许多应用程序中都存在可选视图。 有1000个需要显示或隐藏视图的原因 基于条件,例如应用程序状态,用户个人资料,功能/产品可用性,A / B测试等。
There are also many ways of tackling this issue, but we could split them into three groups, based on which component is responsible for this logic.
解决此问题的方法也有很多,但是我们可以根据负责此逻辑的组件将它们分为三类。
- Container/optional view’s parent: It comes up as the first option because it seems the easiest and simplest way. And it might be, indeed, in simple cases. However, it becomes messy as the logic grows, or the optional views are reused in other places. 容器/可选视图的父级:它是第一个选项,因为它似乎是最简单的方法。 实际上,在简单的情况下也可能如此。 但是,随着逻辑的增长,它变得混乱,或者可选视图在其他地方被重用。
- The optional view itself: Again, a factory method within the optional view itself might be an easy way in simple cases. However, the view should not be responsible for knowing whether it should be shown or not. It should be an assertion. 可选视图本身:同样,在简单情况下,可选视图本身内的工厂方法可能是一种简便方法。 但是,视图不应负责知道是否应显示该视图。 这应该是一个断言。
- Other class/factory: Logic comes out of the container/optional view’s parent to a new class whose responsibility is instantiating the view only when it should be shown. 其他类/工厂:逻辑从容器/可选视图的父级传到一个新类,该类负责仅在应显示该视图时实例化该视图。
Let’s explore the third group since it will serve us well in more contexts.
让我们探讨第三组,因为它可以在更多情况下为我们服务。
具体工厂方法模式 (Concrete Factory Method Pattern)
The first approach we could take is to use a concrete factory method pattern and maintain a reference to both factory and OptionalView
.
我们可以采用的第一种方法是使用具体的工厂方法模式并维护对factory和OptionalView
的引用。
import UIKit
class OptionalView: UIView {
func viewDidLoad() {}
func viewWillAppear() {}
}
protocol OptionalViewFactoryProtocol {
func makeOptionalView() -> OptionalView?
}
struct OptionalViewFactory: OptionalViewFactoryProtocol {
func makeOptionalView() -> OptionalView? {
// Conditions
return OptionalView()
}
}
class ContainerViewController: UIViewController {
let optionalViewFactory: OptionalViewFactoryProtocol
var optionalView: OptionalView?
init(optionalViewFactory: OptionalViewFactoryProtocol) {
self.optionalViewFactory = optionalViewFactory
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
if let optionalView = optionalViewFactory.makeOptionalView() {
self.optionalView = optionalView
view.addSubview(optionalView)
optionalView.viewDidLoad()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
optionalView?.viewWillAppear()
}
}
There are a few things that can be improved here:
这里有一些可以改进的地方:
We are using
OptionalView
’s implementation instead of using an abstraction. If we need to modify or replaceOptionalView
, maintaining the same behavior, we will still need to modify every container where it is used.我们正在使用
OptionalView
的实现,而不是使用抽象。 如果我们需要修改或替换OptionalView
,并保持相同的行为,我们仍然需要修改使用它的每个容器。We are maintaining two references for one
OptionalView
, and we are letting the container(s) know that we are using a factory for theOptionalView
. If at some point,OptionalView
became not optional, we might want or need to remove the factory method pattern. In that case, we will also need to modify all containers whereOptionalView
is being used.我们为一个
OptionalView
维护两个引用,并且让容器知道我们正在为OptionalView
使用工厂。 如果在某些时候OptionalView
变得不是可选的,则我们可能希望或需要删除工厂方法模式。 在这种情况下,我们还需要修改所有使用OptionalView
容器。
抽象工厂方法模式 (Abstract Factory Method Pattern)
Using an abstraction for the optional view with the factory method pattern will solve the first issue. Let’s see how:
对带有工厂方法模式的可选视图使用抽象将解决第一个问题。 让我们看看如何:
import UIKit
protocol OptionalViewProtocol {
var view: UIView { get }
func viewDidLoad()
func viewWillAppear()
}
class OptionalView: UIView, OptionalViewProtocol {
var view: UIView { self }
func viewDidLoad() {}
func viewWillAppear() {}
}
protocol OptionalViewFactoryProtocol {
func makeOptionalView() -> OptionalViewProtocol?
}
struct OptionalViewFactory: OptionalViewFactoryProtocol {
func makeOptionalView() -> OptionalViewProtocol? {
// Conditions
return OptionalView()
}
}
class ContainerViewController: UIViewController {
let optionalViewFactory: OptionalViewFactoryProtocol
var optionalView: OptionalViewProtocol?
init(optionalViewFactory: OptionalViewFactoryProtocol) {
self.optionalViewFactory = optionalViewFactory
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
if let optionalView = optionalViewFactory.makeOptionalView() {
self.optionalView = optionalView
view.addSubview(optionalView.view)
optionalView.viewDidLoad()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
optionalView?.viewWillAppear()
}
}
Now we have decoupled the implementation of OptionalView
from the Container(s), but we are still exposing that we are using a factory for instantiating the optional view.
现在,我们已经将OptionalView
的实现与Container分离开了,但是我们仍然暴露出我们正在使用工厂实例化可选视图。
代理模式 (Proxy Pattern)
Now that we are using an abstraction for OptionalView
, it is easier to encapsulate the factory using a proxy pattern so the container does not need to know. Let’s see how:
现在,我们对OptionalView
使用了抽象,使用代理模式封装工厂变得更加容易,因此容器不需要知道。 让我们看看如何:
import UIKit
protocol OptionalViewProtocol {
var view: UIView? { get }
func viewDidLoad()
func viewWillAppear()
}
class OptionalView: UIView, OptionalViewProtocol {
var view: UIView? { self }
func viewDidLoad() {}
func viewWillAppear() {}
}
class OptionalViewProxy: OptionalViewProtocol {
lazy var optionalView: OptionalView? = {
// Conditions
return OptionalView()
}()
var view: UIView? { optionalView }
func viewDidLoad() { optionalView?.viewDidLoad() }
func viewWillAppear() { optionalView?.viewWillAppear() }
}
class ContainerViewController: UIViewController {
let optionalView: OptionalViewProtocol
init(optionalView: OptionalViewProtocol) {
self.optionalView = optionalView
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
if let optionalView = optionalView.view {
view.addSubview(optionalView)
}
optionalView.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
optionalView.viewWillAppear()
}
}
// With Proxy
let proxy = OptionalViewProxy()
let viewControllerWithProxy = ContainerViewController(optionalView: proxy)
// Without Proxy
let view = OptionalView()
let viewControllerWithoutProxy = ContainerViewController(optionalView: view)
Here we are only referencing the abstraction of OptionalView
, but we are actually injecting a proxy. This proxy could itself act as a factory method or it could delegate the task in another class.
在这里,我们仅引用OptionalView
的抽象,但实际上是在注入代理。 该代理本身可以充当工厂方法,也可以将任务委托给另一个类。
Containers do not know anything about the factory and we could remove the proxy without having to modify any containers at all.
容器对工厂一无所知,我们可以删除代理,而不必修改任何容器。
Thank you for reading. Let me know what you think.
感谢您的阅读。 让我知道你的想法。
工厂模式和抽象工厂模式