在 Cocoa Touch Framework 中使用 Realm 时的模式

Pattern when using Realm in a Cocoa Touch Framework

这是一个代码 pattern/design 问题,在 Cocoa Touch Framework 中使用惊人的 Realm 数据库时。具体来说,一个将作为 cocoapod 分发的框架。

假设我正在构建的框架中有一些领域对象

public class Dog: Object {
    @objc public private(set) dynamic var name: String?
    public let age = RealmOptional<Int>()
    public private(set) var owner: Person?
}

public class Person: Object {
    @objc public private(set) dynamic var name: String?
    public private(set) var dogs = LinkingObjects(fromType: Dog.self, property: "owner")
}

现在,我希望我的框架的使用者能够与这些对象进行交互。我不想用 MVVM 模式抽象这些,因为我希望我的框架的用户能够利用一些很棒的 Realm 东西,比如查询、排序,最重要的是,Realm 更改通知。

所以,第一个问题。我应该让我的框架的用户直接初始化对象吗?他们将在 Realm 初始值设定项中拥有这些选项,如果他们选择使用它们,他们将对它们负责。但是,我喜欢使用静态方法来使用工厂模式。像这样:

extension Dog {
    public static func retreiveManagedDog() throws -> Dog {
        let dog = Dog()
        do{
            let realm = try Realm()
            realm.beginWrite()

            realm.add(dog)
            try realm.commitWrite()
        }catch{
            throw error
        }
        return dog
    }
}

对于这个用例来说,这是一个好的设计模式吗?

其次,下一个问题是更新对象。由于所有 Realm 对象都必须在写入事务中更新,我不希望我的框架的用户必须编写一堆样板代码才能更改名称。所以,我这样写函数:

//MARK: Extension that has functions to update properties
extension Dog {
    public func updateName(_ name: String?) throws {
        do{
            let realm = try Realm()
            realm.beginWrite()
            self.name = name
            try realm.commitWrite()
        }catch{
            throw error
        }
    }
}

请注意,我的对象定义 private(set) 正是出于这个原因。这将有助于迫使我的框架的用户使用我的 setter 方法。

总的来说,我是不是疯了才尝试以这种方式包装 Realm?其他持久存在的伟大框架通常包含所有 SQL Lite/Core 数据逻辑。我还想提出总体上改进此模式的建议。

幸运的是,我认为您的问题没有任何硬性规定的正确答案。这真的取决于你想做什么。

首先,最好不要篡改默认的 Realm 或默认的 Realm 配置,因为任何直接使用 Realm 的应用程序都可能会操纵其中任何一个。您还需要确保为您的模型类型命名空间并将它们从默认架构中排除。 (同样,您可能希望仅使用内部定义的模型模式打开框架的领域。)

至于您的 API,它的外观将取决于您希望在框架上下文中使用 Realm 的目的。

如果 Realm 是一个实现细节,那么控制用户如何通过 APIs 与基于 Realm 的对象交互是有意义的,就像您上面描述的那样。通过这种方式,您可以准确控制用户可以对模型对象执行的操作,并确保不会以导致状态无效的方式修改它们(如果这是一个问题)。

例如,您可能希望通过您提供的 API 为用户处理在 Realm 对象上注册和注销观察者块,该对象指定了自己的先决条件和要求。如果一个对象应该被观察的时间段应该与框架中的某个其他对象或系统一致或受其控制,这可能很有用:而不是告诉用户如何存储他们的通知令牌以及何时是适当的时间处理它们,您可以在幕后处理所有这些细节,并确保用户不会在您的框架上下文中滥用 Realm。

您甚至可以将此发挥到极致,通过使您的 Realm 类型符合某些协议并让您的 API 使用和出售该协议类型,使您的用户看不到 Realm 的使用。这样你的用户就无法分辨出你的对象是 Realm 对象,也不会试图将它们粘贴到自己的 Realms 中、复制它们等。

但是,如果您想让用户知道您在 API 中为他们提供了 Realm 对象,甚至可能在他们自己使用 Realm 的同时使用它们(例如,将它们放在他们的自己的领域),那么明确地将它们作为领域对象出售是有意义的,也许像你为了方便起见而严格描述的那些帮助器APIs。在这种情况下,您需要考虑框架对 Realm 的使用如何避免干扰消费者对 Realm 的使用。