Объектно-ориентированное программирование - Глоссарий
Объектно-ориентированное программирование (ООП) - парадигма разработки, которая направлена на создание программ через совокупность взаимосвязанных данных и объектов (классов), где каждый обладает особыми свойствами и методами. Парадигма организует код, чтобы сделать его упорядоченным, гибким, масштабируемым.
История и эволюция
Объектно-ориентированный подход в разработку пришел вместо процедурного. Первым языком был Симула, созданный в 1967 году. Подходы Симулы усовершенствовали в языке Smalltalk Алан Кэй и Дэн Ингаллс.
В настоящее время список языков, реализующих объектно-ориентированную парадигму, является самым большим. С++, Delphi, C#, Java воплощают модель Симулы, а Python, Ruby опираются на модель Smalltalk.
Основные принципы
Абстракция
Абстракция - это делегирование основных функций без уточнения нюансов. Допустим, для приготовления кофе в кофемашине нам нужно провести подготовку (налить воду, насыпать кофе). Тонкости, которые происходят в процессе заваривания кофе машиной, скрыты от пользователя.
public abstract class CoffeeMachine { public void PourWater() { Console.WriteLine("Заливаем воду..."); } public void AddBeans() { Console.WriteLine("Засыпаем зерна..."); } // Абстрактный метод, специфичный для каждой кофемашины public abstract void BrewCoffee(); } public class EspressoMachine : CoffeeMachine { public override void BrewCoffee() { Console.WriteLine("Варим с использованием пара под высоким давлением..."); } } public class DripCoffeeMachine : CoffeeMachine { public override void BrewCoffee() { Console.WriteLine("Пропускаем горячую воду через зерна..."); } }
В обоих случаях результат один - готовый напиток, но способ приготовления (абстрагированный процесс) различается.
Инкапсуляция
Инкапсуляция - это объединение данных и кода в одном объекте с целью скрыть внутреннее состояние и функции и дать доступ только через открытый набор функций.
Допустим, у нас есть музыкальное приложение, где можно добавлять или удалять песни из избранного списка.
public class MusicApp { private List<string> _favoriteSongs = new List<string>(); public void AddToFavorites(string songName) { if(!string.IsNullOrEmpty(songName) && !_favoriteSongs.Contains(songName)) { _favoriteSongs.Add(songName); } } public void RemoveFromFavorites(string songName) { _favoriteSongs.Remove(songName); } public List<string> GetFavorites() { return new List<string>(_favoriteSongs); } }
Список избранных песен (_favoriteSongs) скрыт (инкапсулирован) и не позволяет работать с ним напрямую.
Наследование
Наследование напоминает биологический процесс, когда новые абстракции похожи на существующие и в то же время отличаются от них, как дети от родителей.
Рассмотрим на примере онлайн-кинотеатра, который предоставляет подписки своим пользователям - базовую с основным контентом и премиальную с эксклюзивным.
public class BasePlan { public void StreamStandardContent() { Console.WriteLine("Показываем контент базового плана..."); } } public class PremiumPlan : BasePlan { public void StreamExclusiveShows() { Console.WriteLine("Показываем контент премиального плана..."); } }
PremiumPlan добавляет эксклюзивный контент плану BasePlan.
Полиморфизм
Полиморфизм - это способ адаптации, когда один интерфейс применяется для управления разными результатами.
Снова вернемся к аудиоплееру, который умеет распознавать музыкальные форматы. Вместо создания способа воспроизведения для каждого аудиоформата можно применить общий метод Play, выполнение которого меняется в зависимости от формата.
public class MusicPlayer { public virtual void Play() { Console.WriteLine("Воспроизводим аудио в стандартном формате..."); } } public class Mp3Player : MusicPlayer { public override void Play() { Console.WriteLine("Воспроизводим mp3..."); } } public class WavPlayer : MusicPlayer { public override void Play() { Console.WriteLine("Воспроизводим wav..."); } } public class FlacPlayer : MusicPlayer { public override void Play() { Console.WriteLine("Воспроизводим flac..."); } }
Объекты и классы
Что такое классы и объекты в ООП?
Класс означает шаблон кода, по типу которого создается объект и напоминает рецепт приготовления блюда. Он сам ничего не делает, но предоставляет список составляющих для создания нового объекта.
Объект - это экземпляр класса со своими свойствами, которые записан в полях, и поведением, которое записано в методах.
Примеры использования на языках программирования
Пример на Python:
class Employee: 'Common base class for all employees' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1 def displayCount(self): print "Total Employee %d" % Employee.empCount def displayEmployee(self): print "Name : ", self.name, ", Salary: ", self.salary
Переменная empCount является переменной класса. Ее значение является общим для всех экземпляров, доступ к которому можно получить как Employee.empCount.
Пример на Java:
Для создания объекта используется ключевое слово new:
public class Test { public static void main(String[] args) { Box box = new Box(); } }
Внимание на оператора присваивания =. Все, что справа от = - это объект. Все, что слева - ссылка на него. Ссылка имеет тип (Box) и имя (box). Теперь через ссылку можно работать с объектом, вызывая у него методы и получая доступ к свойствам.
Примеры объектно-ориентированных языков программирования
ООП в C и его особенности
Принцип инкапсуляции наглядно передают модификаторы доступа: public, protected, private.
Модификатор в C++ устанавливается только на область:
class Employee { public: String firstName; String lastName; int age };
Указанные поля будут доступны, пока не встретится другой модификатор доступа. Чтобы скрыть информацию, необходимо добавить ниже:
private: int IDnumber };
ООП в Python: основные концепции и примеры
Язык Python способен создавать модули, которые легко заменяются и используются повторно. Такая способность упрощает программирование, тестирование и сопровождение кода и делает язык универсальным и надежным для работы с производительными приложениями или Big Data.
Создание класса с наследованием свойств и методов Animal:
class Animal: def run(self): print ('Running') class Dog(Animal): def runAsDog(self): super().run()
Слово super вызывает методы родительского класса в дочернем.
Основа полиморфизма - в переназначении методов:
class Animal: def run(self): return 'Running' class Dog(Animal): def run(self): return 'Running like a dog' class Cat(Animal): pass
Здесь run() переназначен в подгруппе Dog, но не переназначен в Cat. При создании экземпляров реализация метода разная: Cat наследует и не переназначает его, а Dog наследует и переназначает. Автоматически наследуются все методы суперкласса.
dog = Dog() cat = Cat() print(dog.run()) # Running like a dog print(cat.run()) # Running
Инкапсуляция в Python происходит через 2 подчеркивания (private) перед именем переменной или метода. Либо 1 подчеркиванием (protected).
class BankAccount: def __init__(self, balance): self.__balance = balance def get_balance(self): return self.__balance def _do(self): return 'Do something' def set_balance(self, balance): self.__balance = balance
Созданный BankAccount содержит переменную balance. Два подчеркивания перед именем переменной делают ее приватной и исключает изменение напрямую.
Для доступа к приватным переменным применяются геттеры (get) и сеттеры (set). Геттеры возвращают значение приватной переменной, а сеттеры устанавливают ее значение.
ООП в Java: наследование и полиморфизм
Наследование в Java.
Вернемся к ситуации создания подкласса с наследованием свойств и методов Animal. Здесь необходимо использовать ключевое слово extend:
public class Animal { //... } package ru.topjava; public class Dog extends Animal { //... }
Полиморфизм в Java. Для него опишем Person и Employee. Employee наследуется от Person с помощью ключевого слова extends. Наследник Employee переназначает поведение walk() Person.
class Person { void walk() { System.out.println("Can Run...."); } } package ru.topjava; class Employee extends Person { void walk() { System.out.println("Running Fast..."); } public static void main(String[] arg) { Person p = new Employee(); p.walk(); } }
Преимущества и недостатки ООП
Достоинства:
- Модульность. Каждый элемент представляет собой отдельный модуль, что делает его более понятным и управляемым, с возможностью независимо программировать и тестировать;
- Повторное использование в разных частях программы за счет наследования, что упрощает процесс разработки и уменьшает количество дубликатов;
- Управление большими объемами исходного кода благодаря принципам инкапсуляции;
- Простая модификация программ;
- Создание и использование библиотек.
Ограничения и недостатки:
- Перегрузка памяти из-за использования виртуальных способов;
- Высокий порог вхождения. Парадигма сложна для понимания и освоения новичками;
- Производительность ниже, чем в процедурном программировании из-за более громоздкого кода.
Когда использовать ООП?
ООП относится к традиционным парадигмам и используется во многих сферах разработки. Подход подразумевает создание новых элементов и расширение действующих с помощью добавления методов.
С помощью этой концепции разработано множество программ: промышленные системы в финтехе, телекоммуникации на транспорте и производстве.
Ограничения и альтернативы
Рекомендуем учитывать специфику требований и задач, чтобы создать надежный и простой код.
Концепция хорошо показывает себя в процессе написания прикладных программ, операционных систем, компиляторов, СУБД, когда требуется моделировать статические элементы программ. Например, Microsoft Office, Adobe Photoshop и Illustrator. Моделирование динамических процессов лучше доверить функциональной парадигме.