Начиная писать программу, современный разработчик прежде всего должен продумать архитектуру будущего проекта. С точки зрения объектно-ориентированного подхода к разработке программного обеспечения, это значит, что программист должен создать код, который будет состоять из логических блоков (классов и объектов), и продумать их взаимодействие. Код должен быть поддерживаемым. Это значит, что в дальнейшем данную программу можно будет безболезненно дополнить или расширить, не изменяя ее основные составляющие.
В объектно-ориентированной парадигме разработки существуют три основных инструмента, которыми пользуются разработчики для написания хорошего кода:
- Инкапсуляция.
- Наследование.
- Полиморфизм.
- Наследование.
Наследование
Это процесс создания нового класса на основе уже существующего. Объектно-ориентированный подход прежде всего применяется для того, чтобы описать понятия и явления реального мира языком программирования. Поэтому, чтобы понять ООП, необходимо научиться описывать задачу, стоящую перед программистом, языком классов и объектов.
Для того, чтобы понять суть процесса наследования, можно привести пример из жизни. Существует абстрактное понятие «человек». У любого человека есть общие характеристики, которыми его можно описать. Это рост, вес, цвет волос, тембр голоса, и так далее. Иванов Иван, ученик 1 «А» класса — это конкретный представитель понятия «человек», то есть, выражаясь языком объектно-ориентированного подхода, понятие Иванов Иван — наследник понятия человек. Или, к примеру, соседский кот Барсик — наследник понятия «кот». Можно составить более сложное наследование: понятие Барсик наследуется от понятия мейн-кун, которое в свою очередь наследуется от понятия «кот».
Как это описывается языком программирования?
В разработке программного обеспечения процесс наследования дает возможность создавать класс на основе других. Класс-родитель называется базовым, а остальные-наследники — это производные. Заимствование заключается в в том, что потомкам переходят методы, свойства и поля родителей. Синтаксис наследования классов в языке программирования C# может выглядеть следующим образом. Например:
class Ivan : Person
{
// тело класса
{
Пример механизма наследования классов:
class Person
{
public int Age { get; set; }
}
class Ivan : Person
{
public void Talk()
{
// Иван разговаривает
}
}
class Peter : Person
{
public void Walk()
{
// Петр идет
}
}
class Program
{
static void Main(string[] args)
{
Ivan ivan = new Ivan();
ivan.Age = 29; // обозначаем возраст
Peter peter = new Peter();
peter.Age = 35
ivan.Talk();
peter.Walk()();
}
}
На основе базового класса Person создаются классы-наследники, и свойство Age доступно для обоих классов.
Конструктор базового класса
Создавая класс с объявленным конструктором и класс-наследник, возникает логичный вопрос: какие конструкторы за что будут отвечать при создании объектов этих классов?
Если он задан только в наследуемом классе, то при создании объекта-наследника вызывается конструктор по умолчанию (родительский), а потом уже его приемник.
Если же конструкторы заданы и в родительском, и в дочернем классах, то для вызова базового используется ключевое слово base. При объявлении такой структуры наследования для задания конструктора применяется следующий синтаксис:
public Ivan (int age) : base (age)
{
//тело конструктора
}
Наследование методов классов — важнейшее свойство ООП. При создании объекта класса-наследника аргументы конструктора базового класса передаются в него же, после чего вызывается его конструктор, а затем выполняются остальные вызовы конструктора наследника.
Доступ к полям из дочернего класса
Наследование доступа к свойствам и методам базового класса из класса-наследника становится возможным, если все они объявляются со следующими ключевыми словами:
1. Public.
Предоставляет неограниченный доступ к членам класса: из Main, из классов-наследников, из других аналогов.
2. Protected.
Доступ к члену остается только из исходного класса, или из производных аналогов.
3. Internal.
Ограничение к члену локализовано только данной сборкой проекта.
4. Protected internal.
Ограничение к члену лимитировано только данной сборкой проекта или из производных классов.
Наследование доступа к членам класса зависит от ключевых слов. Если при объявлении члена как таковое не указано, то доступность устанавливается по умолчанию (private).
Множественное наследование
Интерфейсы — это контракт, предоставляющий гарантии, что набор методов будет реализован в классе-наследнике интерфейса. Последние не содержат статических членов, полей, констант, или конструкторов. Нелогично и нельзя создавать объекты интерфейса.
Данные структуры создаются за пределами классов, объявляются, используя ключевое слово interface:
interface IMyInterfave
{
void MyMethod (int someVar);
}
Правило хорошего тона при создании интерфейсов — начинать имя конструкции с заглавной буквы I, чтобы в дальнейшем их легко было различать. В теле интерфейса объявляются сигнатуры его членов.
Для указания того, что класс будет реализовывать интерфейс, необходимо так же, как и при обычно наследовании, указать имя конструкции после объявления класса:
class MyClass : IMyInterface
{
public void MyMethod (int someVar)
}
Интерфейсы необходимы для того, чтобы реализовывать множественное наследование. В языке программирования С++, ножественное наследование классов возможно; в языке C# для этих целей используются интерфейсы. Класс может реализовывать многочисленное количество интерфейсов, в таком случае они перечисляются через запятую после объявления класса:
class MyClass : IMyFirstInterface, IMySecondInterface
{
//тело класса
}
По умолчанию все свойства и методы интерфейсов имеют доступ public, потому что должны определять некий функционал, который в дальнейшем будет реализован в классе. Наследование в C# почти всегда включает в себя использование этого удобного инструмента.
Еще немного о наследовании классов
Java, С#, С++ - все объектно-ориентированные языки по-своему реализуют механизм наследования. Но суть везде одна: передать свойства родителя объекту-потомку. Интерфейсы, абстрактные классы - это все удобные инструменты для воплощения этой задачи.