Определение класса, экземпляры которого нельзя создавать напрямую, на первый взгляд может показаться странным. Однако вспомните, что базовые классы (абстрактные или нет) полезны тем, что содержат все общие данные и функциональность для производных типов. Такая форма абстракции дает возможность считать, что "идея" сотрудника является полностью допустимой, просто это не конкретная сущность. Кроме того, необходимо понимать, что хотя
На данной стадии у нас есть довольно интересная иерархия сотрудников. Мы добавим чуть больше функциональности к приложению позже, при рассмотрении правил приведения типов С#. А пока на рис. 6.4 представлено текущее проектное решение.
Полиморфные интерфейсы
Когда класс определен как абстрактный базовый (посредством ключевого слова abstract
Выражаясь упрощенно, полиморфный интерфейс абстрактного базового класса просто ссылается на его набор виртуальных и абстрактных методов. На самом деле это намного интереснее, чем может показаться на первый взгляд, поскольку данная характерная черта ООП позволяет строить легко расширяемые и гибкие приложения. В целях иллюстрации мы реализуем (и слегка модифицируем) иерархию фигур, кратко описанную в главе 5 во время обзора основных принципов ООП. Для начала создадим новый проект консольного приложения C# по имени Shapes
На рис. 6.5 обратите внимание на то, что типы Hexagon
Circle
расширяют базовый класс Shape
. Как и любой базовый класс. Shape
определяет набор членов (в данном случае свойство PetName
и метод Draw()
), общих для всех наследников.Во многом подобно иерархии классов для сотрудников вы должны иметь возможность запретить создание экземпляров класса Shape
Shape
, его можно определить как абстрактный класс. К тому же с учетом того, что производные типы должны уникальным образом реагировать на вызов метода Draw()
, пометьте его как virtual
и определите стандартную реализацию. Важно отметить, что конструктор помечен как protected
, поэтому его можно вызывать только в производных классах.// Абстрактный базовый класс иерархии.
abstract class Shape
{
protected Shape(string name = "NoName")
{ PetName = name; }
public string PetName { get; set; }
// Единственный виртуальный метод.
public virtual void Draw()
{
Console.WriteLine("Inside Shape.Draw()");
}
}
Обратите внимание, что виртуальный метод Draw()
Draw()
из базового класса Shape
. Теперь вспомните, что когда метод помечен ключевым словом virtual
, он поддерживает стандартную реализацию, которую автоматически наследуют все производные типы. Если дочерний класс так решит, то он может переопределить такой метод, но он не обязан это делать. Рассмотрим показанную ниже реализацию типов Circle
и Hexagon
:// В классе Circle метод Draw() НЕ переопределяется.
class Circle : Shape
{
public Circle() {}
public Circle(string name) : base(name){}
}
// В классе Hexagon метод Draw() переопределяется.
class Hexagon : Shape
{
public Hexagon() {}
public Hexagon(string name) : base(name){}
public override void Draw()
{
Console.WriteLine("Drawing {0} the Hexagon", PetName);
}
}