
5.3 Абстрактные классы
При создании иерархии объектов для исключения повторяющегося кода часто бывает логично выделить их общие свойства в один родительский класс. При этом может оказаться, что создавать экземпляры такого класса не имеет смысла, потому что никакие реальные объекты им не соответствуют. Такие классы называют абстрактными.
Абстрактный класс служит только для порождения потомков. Как правило, в нем задается набор методов, которые каждый из потомков будет реализовывать по-своему. Абстрактные классы предназначены для представления общих понятий, которые предполагается конкретизировать в производных классах.
Абстрактный класс задает интерфейс для всей иерархии, при этом методам класса может не соответствовать никаких конкретных действий. В этом случае методы имеют пустое тело и объявляются со спецификаторомabstract.
Абстрактный класс может содержать и полностью определенные методы, в отличие от сходного с ним по предназначению специального вида класса, называемого интерфейсом. Интерфейсы рассматриваются в следующей главе.
Если в классе есть хотя бы один абстрактный метод, весь класс также должен быть описан как абстрактный, например:
abstract class Spirit
{
public abstract void Passport();
}
class Monster : Spirit
{
. . .
override public void Passport()
{
Console.WriteLine( "Monster {0} \t health = {1} ammo = {2}",
name, health, ammo );
}
. . .
}
class Daemon : Monster
{
. . .
override public void Passport()
{
Console.WriteLine(
"Daemon {0} \t health = {1} ammo = {2} brain = {3}",
Name, Health, Ammo, brain );
}
. . .
}
Абстрактные классы используются при работе со структурами данных, предназначенными для хранения объектов одной иерархии, и в качестве параметров методов. Если класс, производный от абстрактного, не переопределяет все абстрактные методы, он также должен описываться как абстрактный.
Можно создать метод, параметром которого является абстрактный класс. На место этого параметра при выполнении программы может передаваться объект любого производного класса. Это позволяет создавать полиморфные методы, работающие с объектом любого типа в пределах одной иерархии. Полиморфизм в различных формах является мощным и широко применяемым инструментом ООП.
Мы уже использовали полиморфизм в лабораторной работе № 16 для того, чтобы метод PrintArray мог работать с массивом любого типа.
5.4 Бесплодные классы
В С# есть ключевое слово sealed, позволяющее описать класс, от которого, в противоположность абстрактному, наследовать запрещается:
sealed class Spirit
{
. . .
}
// class Monster : Spirit { ... } ошибка!
Большинство встроенных типов данных описано как sealed.Если необходимо использовать функциональность бесплодного класса, применяется не наследование, авложение, иливключение: в классе описывается поле соответствующего типа.
Вложение классов, когда один класс включает в себя поля, являющиеся классами, является альтернативой наследованиюпри проектировании. Например, если есть объект «двигатель», а требуется описать объект «самолет», логично сделать двигатель полем этого объекта, а не его предком.
Поскольку поля класса обычно закрыты, возникает вопрос, как же пользоваться методами включенного объекта. Общепринятый способ состоит в том, чтобы описать метод объемлющего класса, из которого вызвать метод включенного класса. Такой способ взаимоотношений классов известен как модель бключения-делегирования. Пример приведен в листинге 4.
Листинг 4 Модель включения-делегирования
using System;
namespace ConsoleApplication
{
class Двигатель
{
public void Запуск()
{
Console.WriteLine( "вжжжж!!" );
}
}
class Самолет
{ public Самолет()
{
левый = new Двигатель();
правый = new Двигатель();
}
public void Запустить_двигатели()
{
левый.Запуск();
правый.Запуск();
}
Двигатель левый, правый;
}
class Class1
{ static void Main()
{
Самолет AH24_1 = new Самолет();
АН24_1.3апустить_двигатели();
}
}
}
Результат работы программы:
вжжжж!!
вжжжж!!
В методе Запустить_двигателизапрос на запуск двигателей передается, или, как принято говорить,делегируется вложенному классу.
В отличие от наследования, когда производный класс «является» (isа) разновидностью базового, модель включения-делегирования реализует отношение «имеет» (has а). При проектировании классов следует выбирать модель, наиболее точно отражающую смысл взаимоотношений классов, например, моделируемых объектов предметной области.