Промежуточным решением между одиночным и множественным наследованием классов может быть использование классов-мандатов. Так, класс Horse можно произвести от двух базовых классов — Animal и Displayable, причем последний добавляет только некоторые методы отображения объектов на экране.
Классом-мандатом называется класс, открывающий доступ к ряду методов, но не содержащий никаких данных (или, по крайней мере, содержащий минимальный набор данных).
Методы класса-мандата передаются в производные классы с помощью обычного наследования. Единственное отличие классов-мандатов от других классов состоит в том, что они практически не содержат никаких данных. Различие довольно субъективное и отражает только общую тенденцию программирования, сводящуюся к тому, что добавление функциональности классам не должно сопровождаться усложнением программы. Использование классов-мандатов также снижает вероятность возникновения неопределенностей при использовании в производном классе данных, унаследованных из других базовых классов.
Например, предположим, что класс Horse производится от двух классов — Animal и Displayable, причем последний добавляет только новые методы, но не содержит данных. В таком случае все наследуемые данные класса Horse происходят только от одного базового класса Animal, а методы наследуются от обоих классов.
Классы-мандаты (capability class) иногда еще называют миксинами (mixin). Этот термин произошел от названия десерта, представляющего собой смесь пирожного с мороженым, политую сверху шоколадной глазурью. Этот десерт продавался в супермаркетах Sommerville в штате Массачусетс. Видимо, это блюдо когда-то попробовал один из программистов, занимающийся разработкой средств объектно-ориентированного программирования для языка SCOOPS, где этот термин впервые появился.
В объектном программировании довольно часто создаются иерархии логически связанных классов. Например, представим класс Shape, от которого произведены классы Rectangle и Circle. Затем от класса Rectangle производится класс Sguare, как частный вид прямоугольника.
В каждом из производных классов замещаются методы Draw(), GetArea() и др. Основной костяк программы с классом Shape и производными от него Rectangle и Circle показан в листинге 13.7.
Листинг 13.7. Классы семейства Shape
1: // Листинг 13.7. Классы семейства Shape
2:
3: #include
4:
5:
6: class Shape
7: {
8: public:
9: Shape(){ }
10: virtual ~Shape() { }
11: virtual long GetArea() { return -1; }
12: virtual long GetPerim() { return -1; }
13: virtual void Draw() { }
14: private:
15: };
16:
17: class Circle : public Shape
18: {
19: public:
20: Circle(int radius):itsRadius(radius) { }
21: ~Circle() { }
22: long GetArea() { return 3 * itsRadius * itsRadius; }
23: long GetPerim() { return 6 * itsRadius; }
24: void Draw();
25: private:
26: int itsRadius;
27: int itsCircumference;
28: };
29:
30: void Circle::Draw()
31: {
32: cout << "Circle drawing routine here!\n";
33: }
34:
35:
36: class Rectangle : public Shape
37: {
38: public:
39: Rectangle(int len, int width);
40: itsLength(len), itsWidth(width) { }
41: virtual ~Rectangle() { }
42: virtual long GetArea() { return itsLength * itsWidth; }
43: virtual long GetPerim() { return 2*itsLength + 2*itsWidth; }
44: virtual int GetLength() { return itsLength; }
45: virtual int GetWidth() { return itsWidth; }
46: virtual void Draw();
47: private:
48: int itsWidth;
49: int itsLength;
50: };
51:
52: void Rectangle::Draw()
53: {
54: for (int i = 0; i
55: {
56: for (int j = 0; j
57: cout << "x ";
58:
59: cout << "\n";
60: }
61: }
62:
63: class Square : public Rectangle
64: {
65: public:
66: Square(int len);
67: Square(int len, int width);
68: ~Square() { }
69: long GetPerim() { return 4 * GetLength();}
70: };
71:
72: Square::Square(int len):
73: Rectangle(len,len)
74: { }
75:
76: Square::Square(int len, int width):
77: Rectangle(len,width) 78:
79: {
80: if (GetLength() != GetWidth())
81: cout << "Error, not a sguare... a Rectangle??\n";
82: }
83:
84: int main()
85: {
86: int choice;
87: bool fQuit = false;
88: Shape * sp;
89:
90: while ( ! fQuit )
91: {
92: cout << "(1)Circle (2)Rectangle (3)Square (0)Quit:";
93: cin >> choice;
94:
95: switch (choice)
96: {
97: case 0: fQuit = true;
98: break;
99: case 1: sp = new Circle(5);
100: break;
101: case 2: sp = new Rectangle(4,6);
102: break;
103: case 3: sp = new Square(5);
104: break;
105: default: cout << "Please enter a number between 0 and 3" << endl;
106: continue;
107: break;
108: }
109: if(! fQuit)
110: sp->Draw();
111: delete sp;
112: cout << "\n";
113: }
114: return 0;
115: }
Результат:
(1)Circle (2)Rectangle (3)Square (0)Quit: 2
x x x x x x
X X X X X X
X X X X X X
X X X X X X
(1)Circle (2)Rectangle (3)Square (0)Quit:3
X X X X X
X X X X x
X X X X X
X X X X X
X X X X X
(1)Circle (2)Rectangle (3)Square (0)Quit:0
Анализ: В строках 6—15 объявляется класс Shape. Методы GetArea() и GetPerim() возвращают -1 как сообщение об ошибке, а метод Draw() не выполняет никаких действий. Давайте подумаем, можно ли в принципе нарисовать форму? Можно нарисовать окружность, прямоугольник или квадрат, но форма — это абстракция, которую невозможно изобразить.
Читать дальше