А теперь вопрос, а что если вам понадобится также setter
.
Я не знаю о вас, но у меня есть (примерно) два типа классов:
BLOB-объекты - это просто свободные коллекции всех свойств бизнес-объекта. Например, Person
будет иметь surname
, firstname
, несколько адресов, несколько профессий ... поэтому Person
может не иметь логики.
Для больших двоичных объектов я обычно использую канонический приватный атрибут + getter + setter, поскольку он абстрагирует фактическую реализацию от клиента.
Однако, хотя ваш шаблон (и его развитие Игоря Зевека) действительно хороши, они не решают проблему настройки и не решают бинарную совместимость проблемы.
Полагаю, я бы прибегнул к макросам ...
Что-то вроде:
// Interface
// Not how DEFINE does not repeat the type ;)
#define DECLARE_VALUE(Object, Type, Name, Seq) **Black Magic Here**
#define DEFINE_VALUE(Object, Name, Seq) ** Black Magic Here**
// Obvious macros
#define DECLARE_VALUER_GETTER(Type, Name, Seq)\
public: boost::call_traits<Type>::const_reference Name() const
#define DEFINE_VALUE_GETTER(Object, Name)\
boost::call_traits<Name##_type>::const_reference Object::Name ()const\
{ return m_##Name; }
#define DECLARE_VALUE_SETTER(Object, Type, Name)\
public: Type& Name();\
public: Object& Name(boost::call_traits<Type>::param_type i);
#define DEFINE_VALUE_SETTER(Object, Name)\
Name##_type& Object::Name() { return m_##Name; }\
Object& Object::Name(boost::call_traits<Name##_type>::param_type i)\
{ m_##Name = i; return *this; }
Что будет использоваться как:
// window.h
DECLARE_VALUE(Window, int, width, (GETTER)(SETTER));
// window.cpp
DEFINE_VALUE(Window, width, (GETTER)); // setter needs a bit of logic
Window& Window::width(int i) // Always seems a waste not to return anything!
{
if (i < 0) throw std::logic_error();
m_width = i;
return *this;
} // Window::width
С небольшим количеством препроцессорной магии это будет работать очень хорошо!
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/tuple/rem.hpp>
#define DECLARE_VALUE_ITER(r, data, elem)\
DECLARE_VALUE_##elem ( BOOST_PP_TUPLE_REM(3)(data) )
#define DEFINE_VALUE_ITER(r, data, elem)\
DEFINE_VALUE_##elem ( BOOST_PP_TUPLE_REM(2)(data) )
#define DECLARE_VALUE(Object, Type, Name, Seq)\
public: typedef Type Name##_type;\
private: Type m_##Name;\
BOOST_PP_SEQ_FOREACH(DECLARE_VALUE_ITER, (Object, Type, Name), Seq)
#define DEFINE_VALUE(Object, Name, Seq)\
BOOST_PP_SEQ_FOREACH(DEFINE_VALUE_ITER, (Object, Name), Seq)
Ладно, не типа safe, и все, но:
- это разумный набор макросов, я думаю
- это просто в использовании, пользователю когда-либо придется беспокоиться только о 2 макросах, хотя, как и в случае с шаблонами, ошибки могут стать проблемными
- использование boost.call_traits для эффективности (выбор констант и / или значений)
там больше функциональности: дуэт геттера / сеттера
это, к сожалению, набор макросов ... и не будет жаловаться, если вы когда-либо
- это наносит ущерб методам доступа (общедоступным, защищенным, приватным), поэтому лучше не перемежать их с классом
Вот канонический пример:
class Window
{
// Best get done with it
DECLARE_VALUE(Window, int, width, (GETTER));
DECLARE_VALUE(Window, int, height, (GETTER));
// don't know which is the current access level, so better define it
public:
};