如何用Realm编写更好的数据访问层

问题描述 投票:5回答:2

我一直在一些小项目中使用Realm,我非常喜欢它。我希望继续在更大的项目中使用它,我正在寻找更好的结构我的数据访问层。

我遇到了类似的question,并试图建立我在那里找到的信息。在那里讨论的方法是DAO模式,所以我给了它一个镜头。

这是我的模特课。

class Chat: Object {
    dynamic var id: String = ""
    dynamic var createdAt: Date = Date()
    dynamic var creatorId: String = ""
    dynamic var title: String?
    let chatMessages = List<ChatMessage>()

    override static func primaryKey() -> String? {
        return "id"
    }

    convenience init(fromJSON json: JSON) {
        self.init()
        // ...
    }
}

然后我创建了一个ChatDAOProtocol来保存所有方便助手方法。

protocol ChatDAOProtocol {
    func addMessage(_ message: ChatMessage)
    func getChatThumbnail() -> UIImage
    func getParticipants(includingMe: Bool) -> [Participant]?
    static func getChat(fromId id: String) -> Chat?
    static func getChat(fromCreatorId id: String) -> Chat?
}

最后,我创建了另一个名为ChatHelper的类,它实现了所有这些协议方法。

class ChatHelper: ChatDAOProtocol {
    func addMessage(_ message: ChatMessage) {

    }

    func getChatThumbnail() -> UIImage {
        return UIImage()
    }

    func getParticipants(includingMe: Bool) -> [Participant]? {
        return nil
    }

    static func getChat(fromId id: String) -> Chat? {
        return nil
    }

    static func getChat(fromCreatorId id: String) -> Chat? {
        return nil
    }

}

这似乎比在VC和东西上遍布所有与数据库相关的代码更好。但我还是有些疑惑。

例如,假设我需要获得聊天的所有参与者,现在我必须在ChatHelper类上调用该方法。如果我想简单地得到聊天标题,我会调用title对象本身的Chat属性。看起来不是一个非常统一的界面。我是否应该在帮助程序中包含所有属性的getter和setter。因此,永远不会直接调用Chat对象(除了可能创建实例)。

要么

我应该让Chat对象本身符合ChatDAOProtocol协议吗?所以所有便利方法以及属性都可以直接从Chat对象直接访问?

还是有比这两者更好的方法?

ios swift realm dao data-access-layer
2个回答
3
投票

这是一个非常棘手的问题,因为它实际上取决于你想要从与Realm的直接交互中抽象出多少,以及你想要与Realm的性能妥协多少。

就个人而言,我认为如果您抽象出查询和编写逻辑,但仍然直接从Realm模型对象中读取,那就没问题了。如果您移动到另一个基于对象的数据库(如核心数据),那么当您重构父类时这些对象属于其他东西(例如,RLMObjectNSManagedObject),您的业务逻辑从这些对象读取的方式不会改变。

但是你必须要小心的一件事是以一种非常低效地利用Realm的方式抽象逻辑。

我可以看到的主要示例是在你的getParticipants方法中,你将返回一个标准的Swift数组。将Realm Results对象转换为这样会导致对内存中的每个对象进行分页(而不是根据请求进行延迟加载),因此您将失去很多Realm性能优势。但由于Results对象的行为与标准数组类似,因此如果您直接返回,则无需更改业务逻辑。

另一个考虑因素:如果您正在更新一批对象上的单个属性,那么确保在单个写入事务中更新所有对象,而不是每次帮助程序内部打开写入事务的助手类都会更好方法被调用。


0
投票

我是Realm的新手,但我已经编写了这段代码,以通用方式访问我的对象。我仍然有一些问题从后台线程访问此方法,但我希望它有所帮助!

import Foundation
import RealmSwift


class GenericDAOImpl <T:Object> : GenericDAO {

  //  MARK: setup

  var realm: Realm?
  init() {
    do {
      realm = try Realm()
    } catch {
      logger.error("Realm Initialization Error: \(error)")
    }
  }

  //   MARK: protocol implementation

  func save(_ object: T) -> Bool {
    guard let `realm` = realm else {
      return false
    }

    do {
      try realm.write {
        realm.add(object)
      }
    } catch {
      return false
    }

    return true
  }

  func saveAll(_ objects: [T]) -> Int {
    var count = 0
    for obj in objects {
      if save(obj) { count += 1 }
    }
    return count
  }

  private func findAllResults() -> Results<T>? {
    return realm?.objects(T.self)
  }

  func findAll() -> [T] {
    guard let res = findAllResults() else { return [] }
    return Array(res)
  }

  func findByPrimaryKey(_ id: Any) -> T? {
    return self.realm?.object(ofType: T.self, forPrimaryKey: id)
  }

  func deleteAll() {
    guard let res = findAllResults() else { return }

    do {
      try realm?.write {
        self.realm?.delete(res)
      }
    } catch {
      logger.error("Realm Error Deleting Objects: \(error)")
      return
    }
  }

}

我忘了展示我的GenericDAO协议:

protocol GenericDAO {
  associatedtype T:Object

  func save(_ object: T) -> Bool

  func saveAll(_ objects: [T]) -> Int

  func findAll() -> [T]

  func findByPrimaryKey(_ id: Any) -> T?

  func deleteAll()

}

要创建实例:

let movieDAO = GenericDAOImpl<Movie>()

我仍在努力看看这是否是最好的方法,但无论如何它对我帮助很大。

© www.soinside.com 2019 - 2024. All rights reserved.