Абстрактные классы и интерфейсы

 

Вполне логично предположить, что корабль не может перевозить сферические грузы в вакууме, у которых есть только вес. Соответственно экземпляры класса Cargo никогда не будут создаваться. Чтобы жестко зафиксировать это правило в коде мы можем сделать Cargo абстрактным классом.

public abstract class Cargo

{

private double _mass = 0;

public double Mass

{

get { return _mass; }

}

 

public Cargo(double mass)

{

_mass = mass;

}

 

}

 

Теперь код вида

Cargo cargo = new Cargo(1);

Вызовет ошибку прямо в компиляторе.

В абстрактный класс можно включить описание абстрактных методов - они пустые, у них нет базовой реализации, но они обязательно должны быть реализованы во всех дочерних классах, пусть и по-разному. У любого груза должен быть хозяин. При этом хозяином человека будет он сам, по крайней мере пока не разрешат рабство, вряд ли в системе управления кораблем надо учитывать такую возможность. Добавим в уже абстрактный класс Cargo абстрактную функцию GetOwner();

public abstract class Cargo

{

private double _mass = 0;

public double Mass

{

get { return _mass; }

}

 

public abstract Person GetOwner();

 

public Cargo(double mass)

{

_mass = mass;

}

}

 

И программа перестанет компиллироваться с говорящей ошибкой 'TestConsoleApplication.Person' does not implement inherited abstract member 'TestConsoleApplication.Cargo.GetOwner()' - так как теперь в каждый класс, наследующийся у Cargo должна быть добавлена такая функция

Как уже упоминалось выше в C# свойства вида

public double Mass

{

get { return _mass; }

}

 

На самом деле представляют из себя функции со специальной формой записи, добавленной для удобства - чтобы не писать каждый раз две функции на Get и Set, так что абстрактным может быть и такое свойство

public abstract class Cargo

{

private double _mass = 0;

public double Mass

{

get { return _mass; }

}

 

public abstract Person Owner { get; }

 

public Cargo(double mass)

{

_mass = mass;

}

}

 

Так или иначе, его надо реализовать во всех наследниках абстрактного класса.
В Person

public override Person Owner

{

get

{

return this;

}

}

 

В Cat

public override Person Owner

{

get

{

return Master;

}

}

 

 

Для демонстрации изменений можно использовать такой цикл

foreach (Cargo currCargo in cargoList)

{

Console.WriteLine("Вес " + currCargo.Mass + ", владелец " + currCargo.Owner.Fio);

}

 

Можно было бы добавить новые типы грузов - багаж и контейнеры, какие-то методы для хозяина и корабля, но суть понятна и без этого.

Абстрактный класс может включать себя и абстрактные и обычные методы. Но реализующий эти методы класс может наследовать их только от одного абстрактного класса. Причина такого ограничения в том, что если мы наследуем методы и свойства сразу от нескольких классов, то получаем очень сильную зависимость от изменений в базовых классах, или классах, которые являются базовыми для наших базовых классов. Стив Макконел сравнивает множественное наследование с неисправной бензопилой, которая может внезапно заработать сама по себе. Но абстрактные методы не страдают от этого недостатка.

Интерфейс - это по сути дела абстрактный класс, в котором могут быть только абстрактные методы без данных на уровне класса. Класс может наследовать, а точнее говоря релизовывать много интерфейсов. По сути интерфейс описывает требование к классу иметь определенный метод или методы.

Можно зайти с другой стороны. Абстрактный класс - это интерфейс, включающий в себя уже реализованные методы и свойства-данные.

В рамках нашего несчастного примера, надуманность которого растет с каждой новой строчкой кода, можно добавить интерфейс IMovable для способных двигаться объектов с методом Move - перемещающим экземпляр в точку с заданными координатами.

public interface IMovable

{

void Move(int x, int y);

 

}

 

public class Cat : Cargo, IMovable

{

public override string ToString()

{

return Name + ", вес " + Mass + " кг., хозяин " + Master.Fio;

}

 

public string Name = "";

 

public void Move(int x, int y)

{

// реализация перемещения

}

 

public override Person Owner

{

get

{

return Master;

}

}

 

public Person Master = null;

 

public Cat(string name, Person master, double mass)

: base(mass)

{

Name = name;

Master = master;

}

 

}

 

Пример интерфейса из реальной жизни - IEquatable, включающий в себя всего один метод IEquatable.Equals или IComparable с методом IComparable.CompareTo.

Более глубокое раскрытие темы интерфейсов выходит за рамки руководства для начинающих. Как минимум вы теперь можете ответить на вопрос "Чем отличается абстрактный класс от интерфейса?", незнание ответа на которой часто приводят в качестве примера низкого уровня знаний у программистов.