I'm refactoring a bunch of old code. My primary objective is to decouple the behavior from the database. The current implementation accesses the database directly which makes it hard to test and do things like implement caching layers etc... Up until this point I've been using a combination of dependency inversion and readers/writers with great success, that is until I reached some of our more complex types.
I have an abstract base user class which encapsulates information common to all of our user types. Inheriting from the base class is a number of specializations of the base user type, each of which encapsulate information specific to that type.
If I have a pointer or reference to the base class and I need to persist that user to the database how do I know which writer to use? If I were to use the writer for the base class, information specific to the derived classes would be lost. Putting an abstract getUserType() method on the base class which then must be implemented by each derived seems like a bit of a hack. This might be a case for double dispatch, but the implementation details are a little fuzzy for me.
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;
}