Абстрактные классы в C#

Абстрактные классы

В предыдущих статьях (sealed классы в C# и статические классы в C#) я рассказывал про достаточно специфические классы, это классы, которые не могут быть использованы в качестве базовых классов (это sealed классы) и классы, объекты которых нельзя создавать в своих программах (это статические классы, хотя, они и в наследовании участия принимать тоже не могут). Так вот в этой статье, я хочу рассказать о еще одном типе классов, это абстрактные классы.

Абстрактные классы, тоже имеют ряд особенностей. Их объекты нельзя создавать (как и в случае статических классов), но предназначены абстрактные классы, именно для того, чтобы служить базовыми для других (т.е. полная противоположность sealed классам).

Таким образом, без механизма наследования, абстрактные классы практически бессмысленны!

А теперь, давайте разберемся подробнее, для чего же нужны абстрактные классы… Дело в том, что когда программист продумывает иерархию классов, на её вершине оказывается самый обобщенный класс. Возьмем для примера иерархию классов из урока № 22 базового курса C#:

//Базовый класс, описывающий оружие
class Weapon
{
    //Атака оружием
    public void Attack()
    {
        Console.WriteLine("Weapon Attack!");
    }
}

//Класс-наследник оружия, описывает нож
class Knife : Weapon
{
    //Атака ножом
    public void Attack()
    {
        Console.WriteLine("Knife Attack!");
    }
}

//Класс-наследник оружия, описывает ружьё
class Gun : Weapon
{
    //Атака ружьем
    public void Attack()
    {
        Console.WriteLine("Gun Attack!");
    }
}

Как видно, в приведенном примере есть три класса, на вершине иерархии находится класс «Weapon» (некое абстрактное оружие), а его наследниками, являются классы «Knife» (нож) и «Gun» (ружье), вполне себе конкретные классы. Так вот класс «Weapon» действительно представляет собой некое абстрактное оружие, и совсем непонятно, как должна выглядеть атака этим оружием… Создавать объекты этого класса нет никакого смысла, да и метод «Attack» нужно бы сделать виртуальным! Т.е. приведенный выше пример явно нуждается в доработке. И сейчас я покажу как это можно сделать.

А поступим мы так, класс «Weapon» мы сделаем абстрактным, и метод «Attack» в нем тоже будет абстрактным, у него не будет тела, т.е. реализации, так как мы действительно и не знаем что он должен делать в контексте класса  «Weapon«. Плюс ко всему, метод «Attack» автоматически станет виртуальным, и в классах наследниках, мы его просто переопределим. Вот так это будет выглядеть на практике:

//Базовый класс, описывающий оружие (теперь абстрактный)
abstract class Weapon
{
    //Атака оружием
    public abstract void Attack();
}

//Класс-наследник оружия, описывает нож
class Knife : Weapon
{
    //Атака ножом (теперь переопределенный метод, добавили override)
    public override void Attack()
    {
        Console.WriteLine("Knife Attack!");
    }
}

//Класс-наследник оружия, описывает ружьё
class Gun : Weapon
{
    //Атака ружьем (теперь переопределенный метод, добавили override)
    public override void Attack()
    {
        Console.WriteLine("Gun Attack!");
    }
}

Всё довольно просто, изменения уложились в несколько строк (они выделены). Мы сделали класс «Weapon» абстрактным (добавили ключевое слово abstract перед его объявлением), так же сделали абстрактным метод «Attack» этого класса, а в классах наследника переопределили его (добавив ключевое слово override). И что мы получили в результате? Мы убрали всю конкретику (которая была не нужна) из класса «Weapon«, запретили создавать объекты этого класса (которые в принципе бессмысленны), сделали метод «Attack» автоматически виртуальным (объявив его абстрактным).  Таким образом, мы убрали «всё ненужное» из первоначального примера и добились необходимого результата, используя абстрактный класс.

Зачем нужны виртуальные методы рассказывается в уроке № 22 базового курса.

В завершение, хочу отметить, что если в классе наследнике, не переопределить абстрактные сущности (в нашем случае, метод «Attack») базового класса, то класс наследник тоже придется делать абстрактным (и объекты этого класса мы создавать не сможем).

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

Добавить комментарий