【问题标题】:What Design pattern for keeping data partly in persistent storage (SQL) and Partly in RAM (memory)?将数据部分保存在持久存储 (SQL) 部分保存在 RAM(内存)中的设计模式是什么?
【发布时间】:2016-03-04 11:21:27
【问题描述】:

我正在用 C++ 和 Qt 设计一个应用程序。 我在 Object 类中有经常需要 数据,在 ObjectExtended 类中有很少需要 数据。 每个 Object 都与 ObjectExtended 相关联。

例子:

 Class Object 
 {
     string importantData; //we need that very often
     int id;
 }

 Class ObjectExtended
 {
     date dateCreated; //meta info, we need this rarely
     int assosiatedObjectId; //to know to which Object this extended data relates
 }

我有 list<Object>list<ObjectExtended> 的对象列表

我想要的是将列表保存在 RAM(内存)中并将列表保存在 ROM(本地存储,例如 SQL)中。 需要节省内存。我已经在 SQL 中保存数据以保存应用程序状态和数据。

我可能面临的问题是我的数据可能会发生变化,如果我将 ObjectObjectExtended 的数据分开,我将不得不重写大量代码。

是否有一些最适合我的问题的设计模式?

【问题讨论】:

  • 当您说“我的数据可以更改”时,您的意思是它可以在运行时更改,或者您的意思是数据架构更改(设计时)?
  • 你的意思是“ROM”,如只读存储器?或者您是否希望将 ObjectExtended 保存在持久性存储中,例如数据库或文件系统?
  • 我的意思是我可以将一些成员从 ObjectExtended 移动到 Object ,反之亦然,不是在运行时,而是在代码(更改应用程序的架构,例如,我将在 Object 中添加需要来自 ObjectExtended 的新数据的方法,并且我必须将数据从一个类移动到另一个类)。 “ROM”是指持久存储
  • 所以不是ROM,请更新问题
  • 我尝试了非常简单的getSingleValueFromSql(valueId, objectId) 与 set 函数相同。所以我们可以实现低内存使用和访问很少使用的数据。

标签: c++ sql qt design-patterns memory


【解决方案1】:

在应用程序代码中,使用Object的虚拟getter和setter,这样你就不需要改变Object的实现了。

class Object {
public:
    virtual string getA() { return ""; }
    virtual void setA(const string&) {}

    virtual string getB() { return "";  };
    virtual void setB(const string&) {}
};

然后,在PartialObject 中实现具体的数据字段,它继承自Object

class PartialObject {
public:
    PartialObject() : k(KeyValueStore::invalidKey()) {}
    virtual string getA() { return a;  }
    virtual void setA(const string& str) { a = str; }

protected:
    KeyValueStore::key k; // This is for persistency

private:
    string a;
};

并实现FullObject,与其余数据字段并具有持久性功能:

class FullObject : public PartialObject {
public:
    FullObject(const PartialObject& partial) {
        *this = partial; 
    }

    virtual string getB() { return b; };
    virtual void setB(const string& s) { b = s;  }

    // This function persists the full object, and returns a partial object, so that the full object can be deleted.
    PartialObject save(KeyValueStore& kvStore) {
        if (!KeyValueStore::isKeyValid(k))
            k = kvStore.generateNewKey();
        kvStore.setValue(k, serialize());
        return PartialObject(reinterpret_cast<PartialObject&>(*this));
    }

    void load(KeyValueStore& kvStore) {
        deseriaize(kvStore.getValue(k));
    }
private:
    string serialize() {
        // Here you should implement the code for serializing all fields, and return serialized data as string.
    }
    void deseriaize(const string& str) {
        // Here you should set "this" to the deserialized version of str.
    }
private: 
    string b;
};

KeyValueStore 类是数据访问层:

class KeyValueStore {
public:
    typedef unsigned long key;

    string getValue(unsigned long long key);
    void setValue(unsigned long long key, const string& value);

    static bool isKeyValid(key k) { return k > 0; }
    static key invalidKey() { return 0; }
    key generateNewKey();
};

【讨论】:

  • 这是一种胖解决方案。我尝试很简单getSingleValueFromSql(valueId, objectId)
【解决方案2】:

让扩展持有关于它正在扩展什么的信息而不是让对象本身持有对其可选扩展的引用可能不是那么有用。但是这个设计决定显然已经做出了。

一个简单的方法是让对象持有一个指向扩展的私有指针和一个公共方法来获取这个指针。然后只在有人需要时才对该指针进行惰性求值。

class Object {
  private:
  string importantData;
  ObjectExtended *extension;

  public:
  ObjectExtended *getExtension () {
    if (extension == NULL) {
      // Load from file or database
      extension = loadFromFile (id);
    }
    return extension;
  }

您甚至可以通过将 getExtension() 设为私有并将扩展数据的公共访问器方法添加到依次调用 getExtension 的 Object 来进一步推动这一点。这将完全隐藏内存中 extension 数据的存在或不存在。如果内存紧张,您可以遍历对象列表并删除扩展指针,从而释放内存。

根据您的问题的确切性质和内存考虑,在 Object 中保存有关最近/最常使用的扩展数据的附加信息可能很有用,因此您可以更好地决定 当内存紧张时,究竟要清除什么

【讨论】:

    猜你喜欢
    • 2011-04-13
    • 2021-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多