Полиморфизм, абстрактные классы и интерфейсы

В чем заключаются преимущества и недостатки объектно-ориентированного подхода в программировании?

Преимущества:

  • Объектная модель вполне естественна, поскольку в первую очередь ориентирована на человеческое восприятие мира, а не на компьютерную реализацию.
  • Классы позволяют проводить конструирование из полезных компонентов, обладающих простыми инструментами, что позволяет абстрагироваться от деталей реализации.
  • Данные и операции над ними образуют определенную сущность, и они не разносятся по всей программе, как нередко бывает в случае процедурного программирования, а описываются вместе. Локализация кода и данных улучшает наглядность и удобство сопровождения программного обеспечения.
  • Инкапсуляция позволяет привнести свойство модульности, что облегчает распараллеливание выполнения задачи между несколькими исполнителями и обновление версий отдельных компонентов.
  • Возможность создавать расширяемые системы.
  • Использование полиморфизма оказывается полезным при:
    • Обработке разнородных структур данных. Программы могут работать, не различая вида объектов, что существенно упрощает код. Новые виды могут быть добавлены в любой момент.
    • Изменении поведения во время исполнения. На этапе исполнения один объект может быть заменен другим, что позволяет легко, без изменения кода, адаптировать алгоритм в зависимости от того, какой используется объект.
    • Реализации работы с наследниками. Алгоритмы можно обобщить настолько, что они уже смогут работать более чем с одним видом объектов.
    • Возможности описать независимые от приложения части предметной области в виде набора универсальных классов, или фреймворка, который в дальнейшем будет расширен за счет добавления частей, специфичных для конкретного приложения.
  • Повторное использование кода:
    • Сокращается время на разработку, которое может быть отдано другим задачам.
    • Компоненты многоразового использования обычно содержат гораздо меньше ошибок, чем вновь разработанные, ведь они уже не раз подвергались проверке.
    • Когда некий компонент используется сразу несколькими клиентами, улучшения, вносимые в его код, одновременно оказывают положительное влияние и на множество работающих с ним программ.
    • Если программа опирается на стандартные компоненты, ее структура и пользовательский интерфейс становятся более унифицированными, что облегчает ее понимание и упрощает использование.

Недостатки:

  • В сложных иерархиях классов поля и методы обычно наследуются с разных уровней. И не всегда легко определить, какие поля и методы фактически относятся к данному классу.
  • Код для обработки сообщения иногда «размазан» по многим методам (иначе говоря, обработка сообщения требует не одного, а многих методов, которые могут быть описаны в разных классах).
  • Документирование классов — задача более трудная, чем это было в случае процедур и модулей. Поскольку любой метод может быть переопределен, в документации должно говориться не только о том, что делает данный метод, но и о том, в каком контексте он вызывается.
  • Неэффективность и неэкономное распределения памяти на этапе выполнения (по причине издержек на динамическое связывание и проверки типов на этапе выполнения).
  • Излишняя универсальность. Часто содержится больше методов, чем это реально необходимо текущей программе. А поскольку лишние методы не могут быть удалены, они становятся мертвым грузом.

Абстракция

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

Пример:

from abc import ABC,abstractmethod
class employee(ABC):
def emp_id(self,id,name,age,salary):    //Abstraction
pass

class childemployee1(employee):
def emp_id(self,id):
print("emp_id is 12345")

emp1 = childemployee1()
emp1.emp_id(id)

Вывод: emp_id – 12345.

Объяснение: Как вы можете видеть в приведенном выше примере, мы импортировали абстрактный метод, а остальная часть программы имеет родительский и производный классы. Создается экземпляр объекта для базового класса childemployee, и используются функциональные возможности абстрактного.

Python не имеет спецификаторов доступа, таких как «частный», как в java. Он поддерживает большинство терминов, связанных с «объектно-ориентированным» языком программирования, за исключением строгой инкапсуляции. Следовательно, он не полностью объектно-ориентирован.

«Online-курс по основам Java программирования» от IT-Academy

Длительность: 6 уроков.

Форма обучения: дистанционный онлайн-курс.

Обратная связь: есть с преподавателем.

Программа: курс рассчитан на новичков, входящих в разработку на Java без стартовых знаний. Слушатели освоят основы структурного программирования, а также получат информацию о синтаксисе и особенностях языка.

Чему научитесь

  1. Структурное программирование и синтаксис.
  2. Подготовка к более сложному программированию.
  1. Структурированный курс с понятным для новичков стилем изложения.
  2. Доступна инструкция по регистрации и последующей работе с курсом.
  3. Со слушателями работает опытный преподаватель.
  4. Хороший старт для входа в Java-разработку.
  1. Доступ к учебным материалам открыт только во время прохождения обучения.

«Уроки по основам Java с нуля» от Олега Шпагина

Пройти курс

Длительность: 16 видеоуроков (до 18 минут каждый).

Форма обучения: видеоуроки.

Программа: слушатели узнают о среде разработки и редакторе для создания приложений на Java. Осуществят разбор первой программы, рассмотрят переменные и типы данных, операции, классы, сборщики мусора и другие темы, позволяющие изучить основы Java с нуля.

Чему научитесь

  1. Основы Java.
  2. Редакторы и среды разработки.
  3. Понимание механизма и особенностей создания продуктов на Java.
  1. Курс адаптирован для новичков.
  2. Массив учебных данных разделён на смысловые блоки: можно изучать конкретные темы, избегая лишней или ранее пройденной информации.
  3. Актуальная информация, вечный доступ к учебным материалам.

Инкапсуляция в Java

Инкапсуляция — принцип, согласно которому атрибуты объекта заключаются в этот объект. Это задает для атрибутов контекст. Это также позволяет программисту ограничить доступ к атрибутам, чтобы они изменялись и использовались только через методы, которые программист собирается применять:

// Эта переменная не инкапсулирована.
// Поэтому в ней отсутствует какой-то контекст.
String name;
// БАЗОВАЯ ИНКАПСУЛЯЦИЯ
// Эти переменные и методы инкапсулированы в классе Dog. Они являются его членами.
class Dog {
    String name;
    int age;
    void bark() {
        System.out.println("Bark!");
    }
    void rename(String newName) {
        name = newName;
    }
}
// МОДИФИКАТОРЫ ДОСТУПА
// Приведенные выше члены доступны для любого другого
// класса. Чтобы определить доступы, используются модификаторы доступа:
// - default: Если модификаторы доступа отсутствуют, атрибут доступен
//    только для классов внутри одного пакета.
// - public: атрибут доступен из любого другого класса.
// - protected: То же самое, что и default, плюс он доступен для подклассов.
// - private: доступен только внутри объявленного класса.
class Dog {
    private String name;
    private int age;
    void bark() {
        System.out.println("Bark!");
    }
    void rename(String newName) {
        name = newName;
    }
    public String getName() {
        return name;
    }
    public void setAge(int newAge) {
        if(newAge > 0)
            age = newAge;
    }
    public int getAge() {
        return age;
    }
}

История развития

Основа ООП была заложена в начале 1960-х годов. Прорыв в использовании экземпляров и объектов был достигнут в MIT с PDP-1, и первым языком программирования для работы с объектами стал Simula 67. Он был разработан Кристен Найгаард и Оле-Джохан Даль в Норвегии с целью создания симуляторов. Они работали над симуляциями взрыва кораблей и поняли, что могут сгруппировать корабли в различные категории. Каждому типу судна было решено присвоить свой собственный класс, который должен содержать в себе набор уникальных характеристик и данных. Таким образом, Simula не только ввела понятие класса, но и представила рабочую модель.

Термин «объектно-ориентированное программирование» был впервые использован Xerox PARC в языке программирования Smalltalk. Понятие ООП использовалось для обозначения процесса использования объектов в качестве основы для расчетов. Команда разработчиков была вдохновлена проектом Simula 67, но они спроектировали свой язык так, чтобы он был динамичным. В Smalltalk объекты могут быть изменены, созданы или удалены, что отличает его от статических систем, которые обычно используются. Этот язык программирования также был первым, использовавшим концепцию наследования. Именно эта особенность позволила Smalltalk превзойти как Simula 67, так и аналоговые системы программирования.

Simula 67 стала новаторской системой, которая впоследствии стала основой для создания большого количества других языков программирования, в том числе Pascal и Lisp. В 1980-х годах объектно-ориентированное программирование приобрело огромную популярность, и основным фактором в этом стало появление языка С++

Концепция ООП также имела важное значение для разработки графических пользовательских интерфейсов. В качестве одного из самых ярких примеров можно привести структуру Cocoa, существующую в Mac OS X

Общие принципы модели стали применяться во многих современных языках программирования

Некоторые из них — Fortran, BASIC, Pascal. На тот момент многие программы не были разработаны с учетом ООП, что было причиной возникновения некоторых проблем совместимости. “Чистые” объектно-ориентированные языки программирования не обладали многими функциями, необходимыми программистам. Для решения этих проблем ряд исследователей предложили несколько новых языков программирования, созданных на основе принципов ООП с сохранением других, необходимых программистам, функций. Среди наиболее ярких примеров можно выделить Eiffel, Java, .NET. Даже в серьезных веб-разработках используются языки программирования, основанные на принципах ООП — PHP (у нас вы можете пройти курс ООП в PHP), Python, Ruby. По мнению экспертов, в ближайшие несколько десятилетий именно объектно-ориентированный подход будет оставаться основной парадигмой в развитии программирования

Общие принципы модели стали применяться во многих современных языках программирования. Некоторые из них — Fortran, BASIC, Pascal. На тот момент многие программы не были разработаны с учетом ООП, что было причиной возникновения некоторых проблем совместимости. “Чистые” объектно-ориентированные языки программирования не обладали многими функциями, необходимыми программистам. Для решения этих проблем ряд исследователей предложили несколько новых языков программирования, созданных на основе принципов ООП с сохранением других, необходимых программистам, функций. Среди наиболее ярких примеров можно выделить Eiffel, Java, .NET. Даже в серьезных веб-разработках используются языки программирования, основанные на принципах ООП — PHP (у нас вы можете пройти курс ООП в PHP), Python, Ruby. По мнению экспертов, в ближайшие несколько десятилетий именно объектно-ориентированный подход будет оставаться основной парадигмой в развитии программирования.

Многоуровневое наследование

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

Пример:

class employee()://Super class
def __init__(self,name,age,salary):  
self.name = name
self.age = age
self.salary = salary
class childemployee1(employee)://First child class
def __init__(self,name,age,salary):
self.name = name
self.age = age
self.salary = salary

class childemployee2(childemployee1)://Second child class
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary
emp1 = employee('harshit',22,1000)
emp2 = childemployee1('arjun',23,2000)

print(emp1.age)
print(emp2.age)

Выход: 22,23.

Объяснение:

  • Это четко объясняется в коде, написанном выше. Здесь я определил суперкласс как сотрудник, а дочерний класс, как childemployee1. Теперь childemployee1 действует, как родитель для childemployee2.
  • Я создал два объекта «emp1» и «emp2», где я передаю параметры «имя», «возраст», «зарплата» для emp1 из суперкласса «сотрудник» и «имя», «возраст», «зарплата» и «идентификатор». Из родительского класса «childemployee1 »

Принципы ООП

Объектно-ориентированное программирование определяют через четыре принципа, по которым можно понять основы работы. Иногда количество сокращают до трех — опускают понятие абстракции.

Абстракция

Абстрагирование — это способ выделить набор наиболее важных атрибутов и методов и исключить незначимые. Соответственно, абстракция — это использование всех таких характеристик для описания объекта

Важно представить объект минимальным набором полей и методов без ущерба для решаемой задачи

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

Набор атрибутов и методов, доступный извне, работает как интерфейс для доступа к объекту. Через них к нему могут обращаться другие структуры данных, причем им не обязательно знать, как именно объект устроен внутри.

Инкапсуляция

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

Инкапсуляция помогает с легкостью управлять кодом. Выше мы сказали, что для обращения к объекту не нужно понимать, как работают его методы. Начальнику разработчика Ивана не обязательно знать, как именно он программирует: главное — чтобы выполнялись поставленные задачи.

Внутреннее устройство одного объекта закрыто от других: извне «видны» только значения атрибутов и результаты выполнения методов.

Наследование

Можно создавать классы и объекты, которые похожи друг на друга, но немного отличаются — имеют дополнительные атрибуты и методы. Более общее понятие в таком случае становится «родителем», а более специфичное и подробное — «наследником».

Упомянутый программист Иван — это человек. Но «человек» — более общее определение, которое не описывает свойства, важные именно для программиста. Можно сказать, что класс «программист» унаследован от класса «человек»: программист тоже является человеком, но у него есть дополнительные свойства.

В таком случае разработчик Иван будет и человеком, и программистом одновременно. У него будут наборы свойств от обоих классов.

У одного «родителя» может быть несколько дочерних структур. Например, от «человека» можно наследовать не только «программиста», но и «директора».


Одиночное и множественное наследие

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

Полиморфизм

Одинаковые методы разных объектов могут выполнять задачи разными способами. Например, у «человека» есть метод «работать». У «программиста» реализация этого метода будет означать написание кода, а у «директора» — рассмотрение управленческих вопросов. Но глобально и то, и другое будет работой.

Тут важны единый подход и договоренности между специалистами. Если метод называется delete, то он должен что-то удалять. Как именно — зависит от объекта, но заниматься такой метод должен именно удалением. Более того: если оговорено, что «удаляющий» метод называется delete, то не нужно для какого-то объекта называть его remove или иначе. Это вносит путаницу в код.

Конструкторы

  • Их единственная цель — создавать экземпляры класса. Они вызываются в процессе создания объекта класса.
  • Если конструктор с аргументами определен в классе, то нельзя будет работать со стандартным конструктором без аргументов (no-argument constructor) — придется их прописать.
  • Java не поддерживает конструктор копирования.
  • Имя конструктора и класса совпадает.
  • Если конструктор вызывается из другого конструктора с синтаксисом this, то речь идет именно об этом объекте.
  • В Java есть стандартный конструктор.

Приватный конструктор:

  • Защищает класс от явного превращения в экземпляр.
  • Построение объекта возможно только внутри конструктора.
  • Используется в шаблоне «Одиночка» (Singleton).

Вопрос: Можно ли синхронизировать конструкторы в Java?

Нет. В Java запрещен многопоточный доступ к конструкторам объекта, поэтому необходимость в синхронизации отсутствует.

Вопрос: Наследуются ли конструкторы? Может ли подкласс вызывать конструктор родительского класса?

Конструкторы не наследуются. При переопределении конструктора суперклассов нарушается инкапсуляция языка. Конструктор родительского класса вызывается ключевым словом super.

1.4 Минимизация ошибок при изменении кода классов

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

Класс оказался настолько полезен, что вы решили его улучшить. Но если вы удалите какие-то методы этого класса, код десятков людей перестанет компилироваться. Им придется срочно все переделывать. И чем больше переделок, тем больше ошибок. Вы поломаете кучу сборок, и вас будут ненавидеть.

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

Абстракция в ООП на Java

Абстракция – в основах ООП для начинающих это процесс скрытия всего, кроме релевантной информации об объекте. Например, нам не нужно знать, как работают часы, чтобы использовать их для определения времени. Абстракция позволяет сосредоточиться на том, что делает объект, а не на том, как он это делает:


// ОСНОВНАЯ АБСТРАКЦИЯ
// Мы можем использовать System.out.println(«Hello»), чтобы вывести строку на
// консоль и не заботиться о том, как работает метод println.
// АБСТРАКТНЫЕ КЛАССЫ
// Абстрактные классы содержат только объявление метода. Их назначение —
// выполнять роль суперклассов для других классов. Они не определяют, как
// методы реализуются, а только что они реализуют. Абстрактные классы не могут
// быть установлены, и подклассы ДОЛЖНЫ реализовать абстрактные методы.
abstract class FlyingAnimal {
public abstract void Fly();
}
class Bird extends FlyingAnimal {
protected String name;
protected int age;
Bird(String nm, int newAge) {
name = nm;
age = newAge;
}
@Override
public void Fly() {
System.out.println(«Flaps wings majestically.»);
}
}
// ИНТЕРФЕЙС
// Классы могут наследоваться только от одного суперкласса, но они могут
// реализовать несколько интерфейсов. Это расширяет возможности для применения
// абстракции. Классы, которые реализуют интерфейс, ДОЛЖНЫ реализовать
// методы в интерфейсе.
// Стандартные классы
class Animal {
private String name;
private int age;
public void identify() {
System.out.println(«I am an animal!»);
}
public void rename(String newName) {
name = newName;
}
public String getName() {
return name;
}
public void setAge(int newAge) {
if(newAge > 0)
age = newAge;
}
public int getAge() {
return age;
}
}
// Интерфейс для животных, которые могут летать. Нам нет дела до того,
// как они летают, просто они могут летать.
public interface ICanFly {
void Fly();
}

// Интерфейс для животных, которые могут плавать. Нам нет дела до того,
// как они плавают, просто они могут плавать.
public interface ICanSwim {
void Swim();
}
// Утка — это животное, которое может и летать, и плавать.
class Duck extends Animal implements ICanFly, ICanSwim {
public void Quack() {
System.out.println(«QUACK!»);
}
@Override
public void Identify() {
System.out.println(«I am a duck!»);
}
@Override
public void Fly() {
System.out.println(«Flaps wings majestically.»);
}
@Override
public void Swim() {
System.out.println(«Kicks feet.»);
}
}
// Рыба — это животное, которое умеет плавать

Обратите внимание на то, что
// реализация метода Swim отличается для утки и для рыбы.
class Fish extends Animal implements ICanSwim {
@Override
public void Identify() {
System.out.println(«I am a fish!»);
}
@Override
public void Swim() {
System.out.println(«Wiggles fish-body»);
}
}
// Самолет — это не животное, но он все равно может летать.
class AirPlane implements ICanFly {
protected String name;
protected int mileage;
@Override
public void Fly() {
System.out.println(«Turns propeller»);
}
}

Пожалуйста, оставляйте свои мнения по текущей теме статьи. Мы крайне благодарны вам за ваши комментарии, лайки, дизлайки, отклики, подписки!

Вадим Дворниковавтор-переводчик статьи «Four Principles of Object-Oriented Programming with Examples in Java»

Роль наследования

Следующий принцип ООП — наследование — касается способности языка позволять
строить новые определения классов на основе определений существующих классов. По сути, наследование позволяет расширять поведение базового (или родительского) класса, наследуя основную функциональность в производном подклассе (также именуемом
дочерним классом):

Т.е. наследование представляет собой процесс, в ходе которого один объект приобретает свойства другого объекта. Это очень важный процесс, поскольку он обеспечивает
принцип иерархической классификации. Если вдуматься, то большая часть знаний
поддается систематизации благодаря иерархической классификации по нисходящей.

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

Полиморфизм времени компиляции

Полиморфизм времени компиляции, также называемый статическим полиморфизмом, разрешается во время компиляции программы. Один из распространенных примеров – «перегрузка метода». Позвольте мне показать вам быстрый пример того же.

Пример:

class employee1():
def name(self):
print("Harshit is his name")    
def salary(self):
print("3000 is his salary")

def age(self):
print("22 is his age")

class employee2():
def name(self):
print("Rahul is his name")

def salary(self):
print("4000 is his salary")

def age(self):
print("23 is his age")

def func(obj)://Method Overloading
obj.name()
obj.salary()
obj.age()

obj_emp1 = employee1()
obj_emp2 = employee2()

func(obj_emp1)
func(obj_emp2)

Вывод:

Его зовут Харшит, 3000 – его зарплата,  22 – его возраст; его зовут Рахул, 4000 – его зарплата, 23 – его возраст.

Объяснение:

  • В приведенной выше программе я создал два класса «employee1» и «employee2», создал функции для «name», «salary» и «age» и распечатал их значения, не принимая их у пользователя.
  • Теперь добро пожаловать в основную часть, где я создал функцию с параметром «obj» и вызвал все три функции, то есть «имя», «возраст» и «зарплата».
  • Позже были созданы экземпляры объектов emp_1 и emp_2 для двух классов и просто вызвана функция. Такой тип называется перегрузкой метода, который позволяет классу иметь более одного метода с одним и тем же именем.
Рейтинг
( Пока оценок нет )
Editor
Editor/ автор статьи

Давно интересуюсь темой. Мне нравится писать о том, в чём разбираюсь.

Понравилась статья? Поделиться с друзьями:
Люкс-хост
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: