HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-21
1 <p><a>#Руководства</a></p>
1 <p><a>#Руководства</a></p>
2 <ul><li>24 янв 2020</li>
2 <ul><li>24 янв 2020</li>
3 <li>0</li>
3 <li>0</li>
4 </ul><p>Узнайте истинную мощь наследования и полиморфизма! Раскрываем секреты абстрактных классов и интерфейсов.</p>
4 </ul><p>Узнайте истинную мощь наследования и полиморфизма! Раскрываем секреты абстрактных классов и интерфейсов.</p>
5 <p>vlada_maestro / shutterstock</p>
5 <p>vlada_maestro / shutterstock</p>
6 <p>Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.</p>
6 <p>Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.</p>
7 <p>В<a>предыдущей статье</a>мы увидели, насколько удобнее становится ООП благодаря наследованию. Но оно может стать ещё лучше, если использовать абстрактные классы и интерфейсы.</p>
7 <p>В<a>предыдущей статье</a>мы увидели, насколько удобнее становится ООП благодаря наследованию. Но оно может стать ещё лучше, если использовать абстрактные классы и интерфейсы.</p>
8 <ul><li><a>Что такое классы и объекты</a>.</li>
8 <ul><li><a>Что такое классы и объекты</a>.</li>
9 <li><a>Особенности работы с объектами</a>.</li>
9 <li><a>Особенности работы с объектами</a>.</li>
10 <li><a>Модификаторы доступа, инкапсуляция</a>.</li>
10 <li><a>Модификаторы доступа, инкапсуляция</a>.</li>
11 <li><a>Полиморфизм и перегрузка методов</a>.</li>
11 <li><a>Полиморфизм и перегрузка методов</a>.</li>
12 <li><a>Полиморфизм</a>.</li>
12 <li><a>Полиморфизм</a>.</li>
13 <li><a>Наследование и ещё немного полиморфизма</a>.</li>
13 <li><a>Наследование и ещё немного полиморфизма</a>.</li>
14 <li><a>Абстрактные классы и интерфейсы</a>.</li>
14 <li><a>Абстрактные классы и интерфейсы</a>.</li>
15 <li><a>Практикум</a>.</li>
15 <li><a>Практикум</a>.</li>
16 </ul><p><strong>В широком смысле абстракция</strong> - это когда мы фокусируемся на тех свойствах системы, которые важны в рамках текущей задачи, а менее существенные отбрасываем.</p>
16 </ul><p><strong>В широком смысле абстракция</strong> - это когда мы фокусируемся на тех свойствах системы, которые важны в рамках текущей задачи, а менее существенные отбрасываем.</p>
17 <p>Абстракции часто встречаются в повседневной жизни. Например, когда мы набираем и отправляем сообщения в мессенджере, то работаем лишь с клавиатурой и кнопкой "Отправить". Мы не задумываемся о версии приложения, о том, какую кодировку использует операционная система, сколько весит наше сообщение и т.д.</p>
17 <p>Абстракции часто встречаются в повседневной жизни. Например, когда мы набираем и отправляем сообщения в мессенджере, то работаем лишь с клавиатурой и кнопкой "Отправить". Мы не задумываемся о версии приложения, о том, какую кодировку использует операционная система, сколько весит наше сообщение и т.д.</p>
18 <p>Другой пример - вождение автомобиля. Водитель думает лишь о том, куда поворачивать руль и когда нажимать педали. Он не задумывается о том, какого цвета его автомобиль, из какого материала сделана обивка салона, сколько присадок в моторном масле, и прочих вещах, которые не влияют на процесс вождения.</p>
18 <p>Другой пример - вождение автомобиля. Водитель думает лишь о том, куда поворачивать руль и когда нажимать педали. Он не задумывается о том, какого цвета его автомобиль, из какого материала сделана обивка салона, сколько присадок в моторном масле, и прочих вещах, которые не влияют на процесс вождения.</p>
19 <p>Похожая ситуация в объектно-ориентированном программировании, только там мы имеем дело с абстракцией данных и методов. Например, чтобы рассчитать зависимость количества потребляемого топлива от веса автомобиля, нам достаточно использовать атрибуты "вес" и "номер" (чтобы различать автомобили) и метод "ехать" (для моделирования пробега).</p>
19 <p>Похожая ситуация в объектно-ориентированном программировании, только там мы имеем дело с абстракцией данных и методов. Например, чтобы рассчитать зависимость количества потребляемого топлива от веса автомобиля, нам достаточно использовать атрибуты "вес" и "номер" (чтобы различать автомобили) и метод "ехать" (для моделирования пробега).</p>
20 class Car { private string id_number; private int weight; //константа пути (100 км) private int const distance = 100; public Car(string id_number, int weight){ this.id_number = id_number; this.weight = weight; } int Go(){ //формула для расчета количества потребляемого топлива } };<p>Конечно, реальные автомобили отличаются цветом, габаритами, размером колес, типом шин, материалом обивки салона и т.д. А еще они умеют не только ехать, но и заводиться, тормозить и глохнуть. Но все эти подробности не имеют отношения к поставленной задаче, а значит, мы можем их смело опустить.</p>
20 class Car { private string id_number; private int weight; //константа пути (100 км) private int const distance = 100; public Car(string id_number, int weight){ this.id_number = id_number; this.weight = weight; } int Go(){ //формула для расчета количества потребляемого топлива } };<p>Конечно, реальные автомобили отличаются цветом, габаритами, размером колес, типом шин, материалом обивки салона и т.д. А еще они умеют не только ехать, но и заводиться, тормозить и глохнуть. Но все эти подробности не имеют отношения к поставленной задаче, а значит, мы можем их смело опустить.</p>
21 <p><strong>Абстрактные классы в объектно-ориентированном программировании</strong> - это базовые классы, которые можно наследовать, но нельзя реализовывать. То есть на их основе нельзя создать объект.</p>
21 <p><strong>Абстрактные классы в объектно-ориентированном программировании</strong> - это базовые классы, которые можно наследовать, но нельзя реализовывать. То есть на их основе нельзя создать объект.</p>
22 <p>Пример такого класса из жизни - животное. Рыбы, пауки и насекомые являются животными, но каждое из них также принадлежит своему подклассу с набором специфических свойств. При этом, просто "животных" в природе не существует.</p>
22 <p>Пример такого класса из жизни - животное. Рыбы, пауки и насекомые являются животными, но каждое из них также принадлежит своему подклассу с набором специфических свойств. При этом, просто "животных" в природе не существует.</p>
23 <p>В программировании абстрактные классы могут понадобиться, чтобы объединить реализацию нескольких схожих классов. Например, в вашей игре должны быть персонаж игрока и NPC<em>(неигровые персонажи).</em>У них могут быть общие свойства<em>(имя, координаты)</em>и методы<em>(перемещение, изменение анимации)</em>.</p>
23 <p>В программировании абстрактные классы могут понадобиться, чтобы объединить реализацию нескольких схожих классов. Например, в вашей игре должны быть персонаж игрока и NPC<em>(неигровые персонажи).</em>У них могут быть общие свойства<em>(имя, координаты)</em>и методы<em>(перемещение, изменение анимации)</em>.</p>
24 <p>Чтобы не повторять код несколько раз, можно вынести реализацию этих свойств и методов в абстрактный класс<strong>Character</strong>. Для его объявления используют ключевое слово<strong><strong>abstract</strong></strong>:</p>
24 <p>Чтобы не повторять код несколько раз, можно вынести реализацию этих свойств и методов в абстрактный класс<strong>Character</strong>. Для его объявления используют ключевое слово<strong><strong>abstract</strong></strong>:</p>
25 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(); }<p>Тут всё как у обычных классов, но в конце можно заметить объявление свойства и метода без реализации. Реализация этих абстрактных свойств должна находиться в дочернем классе:</p>
25 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(); }<p>Тут всё как у обычных классов, но в конце можно заметить объявление свойства и метода без реализации. Реализация этих абстрактных свойств должна находиться в дочернем классе:</p>
26 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}]"); } }<p>Когда объявляется реализация такого члена класса, необходимо указать ключевое слово<strong>override.</strong>Абстрактными могут быть следующие члены класса:</p>
26 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}]"); } }<p>Когда объявляется реализация такого члена класса, необходимо указать ключевое слово<strong>override.</strong>Абстрактными могут быть следующие члены класса:</p>
27 <ul><li>методы;</li>
27 <ul><li>методы;</li>
28 <li>свойства;</li>
28 <li>свойства;</li>
29 <li>индексаторы;</li>
29 <li>индексаторы;</li>
30 <li>события.</li>
30 <li>события.</li>
31 </ul><p>Дочерний класс должен реализовывать все члены родительского абстрактного класса, кроме тех случаев, когда дочерний класс тоже абстрактный.</p>
31 </ul><p>Дочерний класс должен реализовывать все члены родительского абстрактного класса, кроме тех случаев, когда дочерний класс тоже абстрактный.</p>
32 <p>В остальном всё очень похоже на обычные классы. Например, поле<strong>Y</strong>класса Character публичное, чтобы можно было использовать его в свойстве<strong>Y</strong>дочерних классов.</p>
32 <p>В остальном всё очень похоже на обычные классы. Например, поле<strong>Y</strong>класса Character публичное, чтобы можно было использовать его в свойстве<strong>Y</strong>дочерних классов.</p>
33 <p><strong>Важно!</strong></p>
33 <p><strong>Важно!</strong></p>
34 <p><strong>Абстрактный класс должен быть публичным.</strong></p>
34 <p><strong>Абстрактный класс должен быть публичным.</strong></p>
35 <p>Интерфейс похож на абстрактный класс: вы так же не можете объявить его экземпляр, дочерний класс должен реализовывать все члены интерфейса. Реализация членов может находиться как в интерфейсе, так и в дочернем классе.</p>
35 <p>Интерфейс похож на абстрактный класс: вы так же не можете объявить его экземпляр, дочерний класс должен реализовывать все члены интерфейса. Реализация членов может находиться как в интерфейсе, так и в дочернем классе.</p>
36 <p>Сходств очень много, но у интерфейсов есть особенность: один класс может наследовать несколько интерфейсов сразу.</p>
36 <p>Сходств очень много, но у интерфейсов есть особенность: один класс может наследовать несколько интерфейсов сразу.</p>
37 <p>Объявляется интерфейс следующим образом:</p>
37 <p>Объявляется интерфейс следующим образом:</p>
38 //Используется ключевое слово 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; //Статические поля //Поля объявлять нельзя }<p>Также можно использовать индексаторы и события<em>(это тема для отдельной статьи).</em>Теперь рассмотрим применение этого интерфейса.</p>
38 //Используется ключевое слово 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; //Статические поля //Поля объявлять нельзя }<p>Также можно использовать индексаторы и события<em>(это тема для отдельной статьи).</em>Теперь рассмотрим применение этого интерфейса.</p>
39 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}"); } }<p>В отличие от абстрактных методов, методы интерфейса не нужно реализовывать с ключевым словом<strong>override.</strong></p>
39 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}"); } }<p>В отличие от абстрактных методов, методы интерфейса не нужно реализовывать с ключевым словом<strong>override.</strong></p>
40 <p>Также есть одна особенность: метод, реализация которого находится внутри интерфейса, не может использовать этот метод - класс нужно привести к интерфейсу. Для примера добавим в класс<em><strong>Player</strong></em>следующий метод:</p>
40 <p>Также есть одна особенность: метод, реализация которого находится внутри интерфейса, не может использовать этот метод - класс нужно привести к интерфейсу. Для примера добавим в класс<em><strong>Player</strong></em>следующий метод:</p>
41 public void Interact(IInteractive obj) { if(obj.IsInteractive()) { obj.Interact(this); } }<p>В качестве параметра в этот метод можно передавать любой класс, который использует интерфейс<em><strong>IInteractive</strong></em>.</p>
41 public void Interact(IInteractive obj) { if(obj.IsInteractive()) { obj.Interact(this); } }<p>В качестве параметра в этот метод можно передавать любой класс, который использует интерфейс<em><strong>IInteractive</strong></em>.</p>
42 Player p = new Player("Gamer", 5, 10); NPC npc = new NPC("Cube", 10, 10); p.Interact(npc);<p>Это очень удобно в разработке игр, в которых взаимодействовать можно с самыми разными объектами - от NPC до предметов.</p>
42 Player p = new Player("Gamer", 5, 10); NPC npc = new NPC("Cube", 10, 10); p.Interact(npc);<p>Это очень удобно в разработке игр, в которых взаимодействовать можно с самыми разными объектами - от NPC до предметов.</p>
43 <p>Более подробно об отличиях интерфейсов и абстрактных классов на примерах из Java можно прочитать в <a>другой нашей статье</a>.</p>
43 <p>Более подробно об отличиях интерфейсов и абстрактных классов на примерах из Java можно прочитать в <a>другой нашей статье</a>.</p>
44 <p>Создайте игру, в которой будут использоваться абстрактные классы<em>Character</em>и<em>Item</em>, а также интерфейсы<em>IInteractive, ITalkable, IMovable.</em>Методы и свойства придумайте, исходя из названий.</p>
44 <p>Создайте игру, в которой будут использоваться абстрактные классы<em>Character</em>и<em>Item</em>, а также интерфейсы<em>IInteractive, ITalkable, IMovable.</em>Методы и свойства придумайте, исходя из названий.</p>
45 <p>Вот мы и рассмотрели основные части объектно-ориентированного программирования. Дальше вас ждёт практикум, в котором мы поработаем над полноценным проектом, чтобы закрепить полученные знания и узнать ещё немного полезностей.</p>
45 <p>Вот мы и рассмотрели основные части объектно-ориентированного программирования. Дальше вас ждёт практикум, в котором мы поработаем над полноценным проектом, чтобы закрепить полученные знания и узнать ещё немного полезностей.</p>
46 <a><b>Бесплатный курс по Python ➞</b>Мини-курс для новичков и для опытных кодеров. 4 крутых проекта в портфолио, живое общение со спикером. Кликните и узнайте, чему можно научиться на курсе. Смотреть программу</a>
46 <a><b>Бесплатный курс по Python ➞</b>Мини-курс для новичков и для опытных кодеров. 4 крутых проекта в портфолио, живое общение со спикером. Кликните и узнайте, чему можно научиться на курсе. Смотреть программу</a>