swift 文章收集

Swift:静态工厂方法

2020-01-19  本文已影响0人  韦弦Zhy

大多数对象在我们的APP中使用之前,都需要某种形式的设置。无论是我们要根据APP的品牌设置样式的视图(View),还是要配置的视图控制器(View Controller),亦或是在测试中创建存根的值时,我们经常发现需要将设置代码放在某个地方。

放置此类设置代码的一个非常常见的地方是子类。只需将您需要设置的对象子类化,覆盖其初始化程序并在那里进行设置——完成!尽管这肯定是一种可行的方法,但是本周,让我们看一下编写不需要任何子类形式的设置代码的另一种方法——使用静态工厂方法(static factory methods

swift: 静态工厂方法

视图 Views

视图是我们在编写UI代码时必须设置的最常见对象之一。iOS上的UIKit和Mac上的AppKit都为我们提供了创建具有原生外观的UI所需的所有基本核心构建块,但是我们经常需要自定义这些外观以适合我们的设计并为其定义布局。

同样,这是许多开发人员选择子类化并创建内置视图类的自定义变体的地方,就像这里的UILabel一样,我们将使用它来渲染标题:

class TitleLabel: UILabel {
    override init(frame: CGRect) {
        super.init(frame: frame)

        font = .boldSystemFont(ofSize: 24)
        textColor = .darkGray
        adjustsFontSizeToFitWidth = true
        minimumScaleFactor = 0.75
    }
}
相反,让我们尝试使用静态工厂方法来实现相同的目的。我们要做的是在 UILabel 上添加一个扩展,使我们能够从上面创建与 TitleLabel完全相同设置的新实例,如下所示:
extension UILabel {
    static func makeForTitle() -> UILabel {
        let label = UILabel()
        label.font = .boldSystemFont(ofSize: 24)
        label.textColor = .darkGray
        label.adjustsFontSizeToFitWidth = true
        label.minimumScaleFactor = 0.75
        return label
    }
}
//我们只会在单个视图控制器中使用它,因此我们将范围设为私有(暂时),
//以免将此功能添加到我们的应用程序全局使用UIButton中。
private extension UIButton {
    static func makeForBuying() -> UIButton {
        let button = UIButton()
        ...
        return button
    }
}
class ProductViewController {
    private lazy var titleLabel = UILabel.makeForTitle()
    private lazy var buyButton = UIButton.makeForBuying()
}
extension UILabel {
    static var title: UILabel {
        let label = UILabel()
        ...
        return label
    }
}
class ProductViewController {
    private lazy var titleLabel = UILabel.title
    private lazy var buyButton = UIButton.buy
}

视图控制器 View controllers

让我们继续查看控制器,这是使用子类非常常见的另一种对象。虽然我们可能无法完全摆脱视图控制器(或与此相关的视图)的子类化,但是某些类型的视图控制器可以从工厂方法中受益。

尤其是在使用子视图控制器时,我们通常最终会得到一组视图控制器,它们只能在其中呈现特定状态,而不是在其中包含大量逻辑。对于那些视图控制器,将其设置移动到静态工厂API可能是一个很好的解决方案。

在这里,我们使用这种方法来实现一个计算属性,该属性返回一个加载视图控制器,用于显示加载旋转框:

extension UIViewController {
    static var loading: UIViewController {
        let viewController = UIViewController()

        let indicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
        indicator.translatesAutoresizingMaskIntoConstraints = false
        indicator.startAnimating()
        viewController.view.addSubview(indicator)

        NSLayoutConstraint.activate([
            indicator.centerXAnchor.constraint(
                equalTo: viewController.view.centerXAnchor
            ),
            indicator.centerYAnchor.constraint(
                equalTo: viewController.view.centerYAnchor
            )
        ])

        return viewController
    }
}
class ProductListViewController: UIViewController {
    func loadProducts() {
        let loadingVC = add(.loading)

        productLoader.loadProducts { [weak self] result in
            loadingVC.remove()
            self?.handle(result)
        }
    }
}

对添加便捷API的唯一修改是使其返回添加的子视图控制器,从而可以在使用点语法的同时获取对其的引用。当不使用该新功能时,也可以添加@discardableResult来删除所有警告。

测试存根 Test stubs

不仅需要在主应用程序代码中执行很多设置,而且在编写测试时还经常需要这样做。尤其是在测试依赖于特定模型配置的代码时,很容易以充满样板的测试结束,这使它们更难以阅读和调试。

假设我们的应用程序中有一个User模型,其中包含给定用户具有什么样的权限,并且我们的许多测试都是基于当前用户的权限来验证我们的逻辑。不必在所有测试中都使用样板数据手动创建用户,而是创建一个静态工厂方法,该方法基于一组权限返回一个用户存根,如下所示:

extension User {
    static func makeStub(permissions: Set<User.Permission>) -> User {
        return User(
            name: "TestUser",
            age: 30,
            signUpDate: Date(),
            permissions: permissions
        )
    }
}
class FolderManagerTests: XCTestCase {
    func testDeletingFolder() throws {
        // 现在,我们可以快速创建具有所需权限的用户
        let user = User.makeStub(permissions: [.deleteFolders])
        let manager = FolderManager(user: user)
        let folderName = "Test"

        try manager.addFolder(named: folderName)
        XCTAssertNotNil(manager.folder(named: folderName))

        try manager.deleteFolder(named: folderName)
        XCTAssertNil(manager.folder(named: folderName))
    }
}
extension User {
    static func makeStub(age: Int = 30,
                         permissions: Set<User.Permission> = []) -> User {
        return User(
            name: "TestUser",
            age: age,
            signUpDate: Date(),
            permissions: permissions
        )
    }
}

结论 Conclusion

文章来自 John SundellStatic factory methods in Swift简单翻译了一下,希望对大家有用

附:

extension UILabel {
    class func makeForTitle() -> UILabel {
        let label = UILabel()
        label.font = .boldSystemFont(ofSize: 24)
        label.textColor = .darkGray
        label.adjustsFontSizeToFitWidth = true
        label.minimumScaleFactor = 0.75
        return label
    }
}

OC: 创建一个UILabelCategory

@interface UILabel (Factory)
+ (UILabel *)makeForTitle;
@end

@implementation UILabel (Factory)
+ (UILabel *)makeForTitle {
    UILabel *label = [[UILabel alloc] init];
    label.font = [UIFont boldSystemFontOfSize:24];
    label.textColor = [UIColor darkGrayColor];
    label.adjustsFontSizeToFitWidth = YES;
    label.minimumScaleFactor = 0.75;
    return label;
}
@end
typedef NSString * WXOptionKey NS_STRING_ENUM;

FOUNDATION_EXPORT WXOptionKey const WXOptionKeyOne;
FOUNDATION_EXPORT WXOptionKey const WXOptionKeyTwo;

OC: .m 文件

WXOptionKey const WXOptionKeyOne = @"One";
WXOptionKey const WXOptionKeyTwo = @"Two";

Swift,对应的是静态属性

struct WXOptionKey {
  static var one = "One"
  static var two = "Two"
}

使用时都可以使用点语法

赏我一个赞吧~~~

上一篇 下一篇

猜你喜欢

热点阅读