Например, давайте посмотрим на интерфейс для добавления функциональности read в класс:
struct Read_Via_Inheritance
{
virtual void read_members(void) = 0;
};
Каждый раз, когда я хочу добавить другой источник чтения, я должен унаследовать от класса и добавить определенный метод:
struct Read_Inherited_From_Cin
: public Read_Via_Inheritance
{
void read_members(void)
{
cin >> member;
}
};
Если я хочу читать из файла, базы данных или USB, это требует еще 3 отдельных класса. Комбинации начинают появляться очень плохо с несколькими объектами и несколькими источниками.
Если я использую функтор , который напоминает шаблон дизайна Visitor :
struct Reader_Visitor_Interface
{
virtual void read(unsigned int& member) = 0;
virtual void read(std::string& member) = 0;
};
struct Read_Client
{
void read_members(Reader_Interface & reader)
{
reader.read(x);
reader.read(text);
return;
}
unsigned int x;
std::string& text;
};
С помощью вышеуказанного основания объекты могут считывать данные из разных источников, просто предоставляя различные считыватели для метода read_members
:
struct Read_From_Cin
: Reader_Visitor_Interface
{
void read(unsigned int& value)
{
cin>>value;
}
void read(std::string& value)
{
getline(cin, value);
}
};
Мне не нужно менять какой-либо код объекта (хорошо, потому что он уже работает). Я также могу применить читатель к другим объектам.
Обычно я использую наследование, когда выполняю общее программирование . Например, если у меня есть класс Field
, я могу создать Field_Boolean
, Field_Text
и Field_Integer
. Он может поместить указатели на их экземпляры в vector<Field *>
и назвать его записью. Запись может выполнять общие операции над полями и не заботится о том, какой вид поля обрабатывается или не знает.