Абстрактные классы — это классы, у которых не реализован один или больше методов.
Введение
Согласно одному из принципов проектирования следует при создании системы классов программировать на уровне интерфейсов, а не их фактических реализаций. Под интерфейсами в этом случае нужно понимать не только типы языка Cи, которые определены при помощи ключевого слова interface, а определение функционала без учета его фактической реализации. То есть, под такое определение могут попасть как непосредственно интерфейсы, так и абстрактные классы, способные обладать абстрактными методами без конкретной реализации.
С этой точки зрения у абстрактных классов и интерфейсов имеется много общего. Часто при формировании программ в паттернах, пользователь может заменить абстрактные классы на интерфейсы, а также справедливо и обратное утверждение. При этом у них конечно имеются и некоторые отличия.
Абстрактные классы и интерфейсы
Абстрактные классы необходимо применять в следующих случаях:
- когда необходимо определить общий функционал для родственных объектов;
- когда выполняется проектирование достаточно большой функциональной единицы, содержащей много базового функционала;
- когда требуется, чтобы все производные классы на всех уровнях наследования обладали некоторой общей реализацией. При применении абстрактных классов, если нужно изменить базовый функционал во всех наследниках, то можно просто изменить его в абстрактном базовом классе;
- если же потребуется изменить название или параметры метода интерфейса, то в таком случае необходимо уже вносить коррективы также во всех классах, которые этот интерфейс призван реализовать.
Интерфейсы следует использовать в следующих случаях:
- когда необходимо определить функционал для группы разрозненных объектов, которые могут не обладать никакими взаимными связями;
- когда осуществляется проектирование небольшого функционального типа.
Ключевыми здесь могут считаться первые пункты, которые могут быть сведены к следующему принципу:
- когда классы принадлежат единой системе классификации, то следует выбрать абстрактный класс;
- в противном случае следует выбрать интерфейс.
Приведем конкретный пример. Предположим, что имеется система транспортных средств, таких как, легковые автомобили, автобусы, трамваи, поезда и так далее. Так как все эти объекты могут считаться родственными, то есть, можно выявить у них общие признаки, то в таком случае имеется возможность использования абстрактных классов:
publíc abstract class Vehícle
{
publíc abstract voíd Move();
}
publíc class Car : Vehícle
{
publíc override voíd Move()
{
Console.WríteLíne(«Машина передвигается»);
}
}
publíc class Bus : Vehícle
{
publíc overríde voíd Move()
{
Console.WríteLine(«Автобус передвигается»);
}
}
publíc class Tram : Vehícle
{
publíc overríde voíd Move()
{
Console.WríteLine(«Трамвай передвигается»);
}
}
Абстрактный класс Vehicle должен определять абстрактный метод передвижения Move(), а классы, которые являются наследниками, его реализуют.
Но, представим, что приведенная в примере выше система транспорта не ограничивается вышеперечисленным набором транспортных средств. К примеру, имеется возможность добавления к ней самолетов и лодок. Вероятно, что можно также добавить и лошадь, то есть, животное, способное также исполнять функции транспортного средства. Также может быть добавлен, например, дирижабль. В итоге может получиться довольно обширная совокупность объектов, объединенных лишь тем обстоятельством, что они считаются транспортными средствами и обязаны осуществить реализацию некоторого метода Move(), выполняющего передвижение.
Поскольку объекты мало связаны между собой, то, для того чтобы определить общий для всех них функционал, следует выбрать определение интерфейса. Тем более, что некоторые из данных объектов могут присутствовать в границах параллельных систем классификаций. К примеру, лошадь может являться классом в структуре системы классов животного мира.
Реализовать такой интерфейс можно, например, следующим образом:
public interface IMovable
{
void Move();
}
public abstract class Vehicle
{}
public class Car : Vehicle, IMovable
{
public void Move()
{
Console.WriteLine(«Машина передвигается»);
}
}
public class Bus : Vehicle, IMovable
{
public void Move()
{
Console.WriteLine(«Автобус передвигается»);
}
}
public class Hourse : IMovable
{
public void Move()
{
Console.WriteLine(«Лошадь передвигается»);
}
}
public class Aircraft : IMovable
{
public void Move()
{
Console.WriteLine(«Самолет пролетает»);
}
}
В данном варианте метод Move() определяется в интерфейсе IMovable, а конкретные классы осуществляют его реализацию.
Если говорить о применении абстрактных классов и интерфейсов, то можно провести еще и такую аналогию, как состояние и действие. Обычно абстрактные классы бывают сфокусированы на общем состоянии классов, являющихся наследниками. В то время как интерфейсы выстраиваются, как правило, вокруг какого-нибудь общего действия.
К примеру, солнце, костер, батарея отопления и электрический нагреватель могут выполнять функции нагревания или излучения тепла. По большому счету излучение тепла является единственным общим для них признаком. Можно для них сформировать общий абстрактный класс, но это не оптимальное решение, поскольку могут быть какие-то еще родственные сущности, которые, вполне вероятно, нужно будет в дальнейшем использовать. По этой причине для каждой, перечисленной выше, сущности может быть определена своя система классификации. К примеру, в одной системе классов, которые наследуются от общего абстрактного класса, могут быть звезды, включая солнце, планеты, астероиды и так далее. То есть все те объекты, которые способны обладать каким-то общим с солнцем состоянием. В границах другой системы классов можно определить электрические приборы, в том числе электрический нагреватель.