c++ - Should i make a class instead of the repetitive get/set statements? -
consider following code (which simplified version of original one):
#include <string> namespace { class ctype; } class my::ctype { private: std::string simple_name; public: explicit ctype(std::string simple_name) : simple_name{std::move(requireidentifier(simple_name))} // duplicate code { } std::string get_simple_name() const { return simple_name; } void set_simple_name(std::string value) { simple_name = std::move(requireidentifier(value)); // duplicate code } };
the requireidentifier
function returns reference value
value
not empty otherwise throw std::invalid_argument
exception.
i have many classes similar ctype
class definition, of them have private member variable of type std::string
, subsequently check value not empty following expression:
std::move(requireidentifier(value))
here problem, repeated in of classes! suppose have 10 classes similar ctype
, there should 20=2*10 expressions above case! don't want repeat similar code in of 20 places of code! seams wrong.
i think should define new class type:
class my::identifier { private: std::string name; public: explicit identifier(std::string name) : name{std::move(requireidentifier(name))} // duplicate code { } std::string get() const { return name; } void set(std::string value) { name = std::move(requireidentifier(value)); // duplicate code } };
and change definition of ctype
class following code:
class my::ctype { private: identifier simple_name; public: explicit ctype(identifier simple_name) : simple_name{std::move(simple_name)} { } identifier get_simple_name() const { return simple_name; } void set_simple_name(identifier value) { simple_name = std::move(value); } };
btw, can't avoid repetitive std::move
section , not problem.
but if don't overload =
operator of identifier
class , if don't declare implicit conversion std::string
identifier
class, identifier
class not work string
class. isn't bad choice of design? proper way avoid repetitive code in such case (afaik, shouldn't subclass stl container)?
edit: requireidentifier
function defined following code:
inline std::string& my::requestidentifier(std::string& value) { for(char c : value) { if(!isalnum(c) && c != '_') { throw std::invalid_argument{""}; } } return value; } inline std::string& my::requireidentifier(std::string& value) // here { if(value.empty()) { throw std::invalid_argument{""}; } else { return my::requestidentifier(value); } }
conclusion
as can see if put "extraction" of underlying string in ctype
class (as mentioned in accepted answer tony d), , if want work string
type, have call get
, set
member functions of identifier
class in of 10 classes repetitively had called requireidentifier
function. therefore making class eliminating 1 function call redundancy, techinically not approach.
on other hand , regardless of code redundancy, should declare identifer
class if think new type string
type cannot represent well, , can declare implicit user-defined constructor make compatible string
type, , purpose of identifier
class not get
, set
accessor string
type, purpose new type.
but if don't overload = operator of identifier class , if don't declare implicit conversion std::string identifier class, identifier class not work string class. isn't bad choice of design? proper way avoid repetitive code in such case?
so... put "extraction" of underlying string
in ctype
class:
class my::ctype { private: identifier simple_name; public: explicit ctype(identifier simple_name) : simple_name{std::move(simple_name)} { } std::string get_simple_name() const { return simple_name.get(); } void set_simple_name(std::string value) { simple_name.set(std::move(value)); } };
update: requested in comments, below - illustration of using check_invariants()
function. there's still redundancy, can repeat arbitrary number of checks without further modifying each mutating function.
class my::ctype { private: std::string simple_name; void check_invariants() { if (!is_identifier(simple_name)) throw std::invalid_argument("empty identifier); } public: explicit ctype(std::string simple_name) : simple_name{std::move(simple_name)) { check_invariants(); } std::string get_simple_name() const { return simple_name; } void set_simple_name(std::string value) { simple_name = std::move(value); check_invariants(); } };
...with...
bool is_identifier(const std::string& s) { return !s.empty() && (isalpha(s[0]) || s[0] == '_') && std::all_of(s.begin() + 1, s.end(), [](char c) { return isalnum(c) || c == '_'; }); }
Comments
Post a Comment