I am doing a final project in a C++ class writing a very simple usenet-like client/server. I figure that since it’s usenet-like I’ll have a NewsGroup
class and an Article
class, I’m now working on my Article
class. I can’t really decide on a good design, though (this class is not about design patterns and I haven’t taken any).
This is what I have:
class Article {
public:
const std::string author;
std::string title;
std::string text;
const uint32_t id;
system_clock::time_point timestamp;
Article(const std::string auth&,
const std::string tit&,
const std::string txt&,
const system_clock::time_point&);
private:
Article(const Article&);
void operator=(const Article&);
static uint32_t id_pool;
};
My motivation is this:
author
should never change -> const
title
could change if edited -> non-const
text
could change if edited -> non-const
id
should never change -> const
timestamp
could change but probably shouldn’t, I’m not sure what to do with it yet.
id_pool
is just where I get IDs from. One specification is that IDs should never be reused so I just increment id_pool
in the Article constructor.
Coming from Java with all the getters and setters that I really don’t like, and then some Python with it’s “Everything public” and properties, that I do like, I’m getting a bit confused here.
I mean, I could make the members private and have getters and setters (I can’t think of any natural “verb-methods” that an Article can “do”), but it feels like I would gain very little.
I would be thankful for some pointers on good design in this case.
7
Start with the interface you want the class to expose to its users. Do not think immediately about the implementation, e.g., whether to use std::string
for the title, etc. I would come up with something like below:
class Article {
public:
typedef implementation_defined Id;
Article(const char* author, const char* title);
~Article();
Id GetId() const;
const char* GetAuthor() const;
const char* GetTitle() const;
const char* GetText() const;
system_clock::time_point GetTime() const;
void SetTitle(const char* text);
void SetText(const char* text);
void SetTime(system_clock::time_point timestamp);
};
The intention is that the caller should use the type Article::Id
instead of something like int
. I assume the Id is uniquely generated on creation, the text can be empty, and the default timestamp is the current time.
As you are not exposing the implementation to the caller, you are free to change the implementation later. E.g., maybe the interface dictates the UTF-8 encoding, but the real implementation uses UTF-16 (wchar_t
) on Windows (just to show, not that it is something you should/want to do). You can also make some checks in the Set...
methods.
This might be trivial, but I think the principle is that you need to: 1) think from the user of the class; 2) make it flexible and reduce the dependency.
For starters it would make sense to make the member variables private and expose public get/set functions where necessary. The advantage this provides is that you could then modify the internal member variables if necessary (future refactoring, say) without having to change the calling contexts because the internals are hidden behind the external class interface.
This would generally be considered good extensible, encapsulated design in any language when working on an object oriented project that you plan to maintain.
1