读者/编写者和派生类的解耦持久性

时间:2010-10-06 01:11:53

标签: c++ design-patterns

我正在重构一堆旧代码。我的主要目标是将行为与数据库分离。当前的实现直接访问数据库,这使得很难测试并执行诸如实现缓存层之类的事情......直到这一点,我一直在使用依赖倒置和读者/编写器的组合取得巨大成功,直到我达到了一些我们更复杂的类型。

我有一个抽象的基本用户类,它封装了我们所有用户类型共有的信息。从基类继承的是基本用户类型的许多特化,每个特化都封装了特定于该类型的信息。

如果我有一个指针或对基类的引用,我需要将该用户持久保存到数据库,我如何知道要使用哪个编写器?如果我将编写器用于基类,则特定于派生类的信息将丢失。在基类上放置一个抽象的getUserType()方法然后必须由每个派生实现,这看起来有点像黑客。这可能是双重调度的情况,但实现细节对我来说有点模糊。

class User
{
public:
    std::string
    name() const
    {
        return m_name;
    }

    void
    name(const std::string& name)
    {
        m_name = name;
    }

private:
    std::string m_name;
}

class EmailUser : User
{
public:
    std::list<std::string>
    emails() const
    {
        return m_emails;
    }

    void
    emails(const std::string<std::string>& emails)
    {
        m_emails = emails;
    }

private:
    std::set<std::string>   m_emails;
}

class UserWriter
{
public:
    virtual void
    write(User& user) = 0;
}

class DBUserWriter : UserWriter
{
public:
    void
    write(User& user)
    {
        SQLExecute("SOME SQL UPDATE STMT %s", user.name());
    }
}

class DBEmailUserWriter : UserWriter
{
public:
    void
    write(User& user)
    {
        m_baseUserWriter.write(user);
        SQLExecute("SOME SQL UPDATE STMT %s", user.email.front());
    }
private:
    DBUserWriter    m_baseUserWriter;
}

1 个答案:

答案 0 :(得分:3)

以下是使用维基百科页面中指定的双重调度实现的示例:

#include <iostream>
using namespace std;

class Writer;

class User
{
public:
    std::string name() const { return m_name; }
    void name(const std::string& name) { m_name = name; }
    virtual void accept(Writer & writer) const;

private:
    std::string m_name;
};

class EmailUser : public User
{
public:
    std::string email() const { return m_email; }
    void email(const std::string& email) { m_email = email; }
    virtual void accept(Writer & writer) const;

private:
    std::string m_email;
};

class Writer
{
public:
  void process(const User& user) { user.accept(*this); }
  virtual void write(const User& user) { cout << user.name() << endl; }
  virtual void write(const EmailUser& user) { cout << user.email() << endl; }
};

class SubWriter : public Writer
{
public:
  void process(const User& user) { user.accept(*this); }
  void write(const User& user) { cout << "[" << user.name() << endl; }
  void write(const EmailUser& user) { cout << "[" << user.email() << endl; }
};

void User::accept(Writer & writer) const { writer.write(*this); }
void EmailUser::accept(Writer & writer) const { writer.write(*this); }

int main()
{
  User u;
  EmailUser eu;
  Writer w;
  SubWriter s;

  u.name("Hugo Peixoto");
  eu.email("hugo.peixoto@gmail.com");

  w.process(u);
  w.process(eu);

  s.process(u);
  s.process(eu);

  return 0;
}