Меню
Код
Код
#Руководства
  • 8152

ООП. Часть 6. Абстрактные классы и интерфейсы

Узнайте истинную мощь наследования и полиморфизма! Раскрываем секреты абстрактных классов и интерфейсов.

В предыдущей статье мы увидели, насколько удобнее становится ООП благодаря наследованию. Но оно может стать ещё лучше, если использовать абстрактные классы и интерфейсы.

Все статьи про ООП

  1. Что такое классы и объекты.
  2. Особенности работы с объектами.
  3. Модификаторы доступа, инкапсуляция.
  4. Полиморфизм и перегрузка методов.
  5. Наследование и ещё немного полиморфизма.
  6. Абстрактные классы и интерфейсы.
  7. Практикум.

Евгений Кучерявый

Пишет о программировании, в свободное время создает игры. Мечтает открыть свою студию и выпускать ламповые RPG.


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

Особенность абстрактных классов в том, что их можно использовать только как родительский класс, то есть вы не можете создать объект. Для их объявления используется ключевое слово abstract.

Это может понадобиться, чтобы объединить реализацию других схожих классов. Например, в вашей игре должны быть персонаж игрока и NPC (неигровые персонажи). У них могут быть общие свойства (имя, координаты) и методы (перемещение, изменение анимации).

Чтобы не повторять код несколько раз, можно вынести реализацию этих свойств и методов в абстрактный класс Character:

abstract class Character
{
    private string name;

    private int x;
    public int y;

    public Character(string name, int x, int y)
    {
   	 this.name = name;

   	 this.x = x;
   	 this.y = y;
    }

    public int X     {
   	 get
   	 {
   		 return this.x;
   	 }
    }

    public void ShowName()
    {
   	 Console.WriteLine(this.name);
    }

    public abstract int Y { get; }

    public abstract void ShowPosition();
} 

Тут всё как у обычных классов, но в конце можно заметить объявление свойства и метода без реализации. Реализация этих абстрактных свойств должна находиться в дочернем классе:

class Player : Character
{
    public Player(string name, int x, int y)
   	 :base(name, x, y)
    {

    }

    public override int Y
    {
   	 get
   	 {
   		 return this.y;
   	 }
    }

    public override void ShowPosition()
    {
   	 Console.WriteLine($"[{X}, {Y}]");
    }
}

Когда объявляется реализация такого члена класса, необходимо указать ключевое слово override. Абстрактными могут быть следующие члены класса:

  • методы;
  • свойства;
  • индексаторы;
  • события.

Дочерний класс должен реализовывать все члены родительского абстрактного класса, кроме тех случаев, когда дочерний класс тоже абстрактный.

В остальном всё очень похоже на обычные классы. Например, поле Y класса Character публичное, чтобы можно было использовать его в свойстве Y дочерних классов.

Важно!

Абстрактный класс должен быть публичным.

Интерфейсы

Интерфейс похож на абстрактный класс: вы так же не можете объявить его экземпляр, дочерний класс должен реализовывать все члены интерфейса. Реализация членов может находиться как в интерфейсе, так и в дочернем классе.

Сходств очень много, но у интерфейсов есть особенность: один класс может наследовать несколько интерфейсов сразу.

Объявляется интерфейс следующим образом:

//Используется ключевое слово interface
interface IInteractive //Названия интерфейсов принято начинать с заглавной буквы I
{
    void Interact(Player p); //Метод без реализации

    //public bool Accessor { get; set; }

    //Доступно начиная с C# 8.0

    bool IsInteractive() //Метод с реализацией
    {
   	 return true;
    }

    //const bool Interactive = true; //Константы

    //static bool Enabled = true; //Статические поля

    //Поля объявлять нельзя
}

Также можно использовать индексаторы и события (это тема для отдельной статьи). Теперь рассмотрим применение этого интерфейса.

class NPC : Character, IInteractive
{
    public NPC(string name, int x, int y)
   	 :base(name, x, y)
    {

    }

    public override int Y
    {
   	 get
   	 {
   		 return this.y;
   	 }
    }

    public override void ShowPosition()
    {
   	 Console.WriteLine($"[{X}, {Y}]");
    }

    public void Interact(Player p)
    {
   	 Console.WriteLine($"{p.Name} interacting with {this.Name}");
    }
}

В отличие от абстрактных методов, методы интерфейса не нужно реализовывать с ключевым словом override.

Также есть одна особенность: метод, реализация которого находится внутри интерфейса, не может использовать этот метод — класс нужно привести к интерфейсу. Для примера добавим в класс Player следующий метод:

public void Interact(IInteractive obj)
{
    if(obj.IsInteractive())
    {
   	 obj.Interact(this);
    }
}

В качестве параметра в этот метод можно передавать любой класс, который использует интерфейс IInteractive.

Player p = new Player("Gamer", 5, 10);
NPC npc = new NPC("Cube", 10, 10);

p.Interact(npc);

Это очень удобно в разработке игр, в которых взаимодействовать можно с самыми разными объектами — от NPC до предметов.

Домашние задание

Создайте игру, в которой будут использоваться абстрактные классы Character и Item, а также интерфейсы IInteractive, ITalkable, IMovable. Методы и свойства придумайте, исходя из названий.

Заключение

Вот мы и рассмотрели основные части объектно-ориентированного программирования. Дальше вас ждёт практикум, в котором мы поработаем над полноценным проектом, чтобы закрепить полученные знания и узнать ещё немного полезностей.

Свои отзывы можете присылать мне на почту или в telegram. Они помогают делать статьи лучше.

Ну а если вы хотите основательно закрепить знания об ООП, записывайтесь на наш курс по C# — работать с классами и объектами вам придётся на протяжении всего обучения.

Курс

C#-разработчик с нуля до PRO


130 часов — и вы научитесь писать программы на языке, созданном Microsoft. Вы создадите 5 проектов для портфолио, даже если до этого никогда не программировали. После обучения — гарантированное трудоустройство.

Понравилась статья?
Да