Объектно ориентированное программирование для чайников
Объектно-ориентированное программирование для чайников
Если эта статья кажется вам банальной — загляните в продвинутый учебник по ООП.
Классы и объекты
Начнем с самого базового понятия, — класса. Класс, он же тип, чтобы не мучить, и не сбивать с толку формулировками, можно объяснить на примере: класс это Собака, либо Кошка , либо Мышка , — всё это классы. А вот Жучка , Мурчик и Микки-Маус , — это экземпляры классов Собака , Кошка , Мышка . Экземпляры классов еще называют объектами. Итак, — Жучка это экземпляр класса Собака , Мурчик , — экземпляр класса Кошка , а Микки-Маус , — экземпляр класса Мышка , другими словами объект. Класс — единственный и неповторимый. У вас могут быть классы очень похожие, как овчарка и доберман, но это всё равно разные классы, хоть и очень похожие. Класс представляет собой набор характеристик/свойств, которые описывают его состояние и действия/методы, которые он может выполнить. Например, — нас есть класс Овчарка, к числу её свойств можно отнести «возраст» и «кличку». Экземпляр класса может выполнять действия, описанные в классе (лаять, сидеть, лежать), а также содержит значения свойств класса (класс Овчарка имеет свойство «возраст», а объект Барбос имеет значение, — 3 года). Помимо свойств и методов, объект имеет собственное имя, не путать со свойством «кличка», вот как это может выглядеть:
Что означает, что у объекта « МояСобака », есть кличка, и эта кличка — Барбос . А бывает и такая ситуация:
Тут у нас объект « ХозяинСобаки », у которого свойство « имя » имеет значение « Василий », а отчество равно « Иванович ».
Похожим образом работают методы (действия) объектов, например:
Методы могут параметризироваться, например, мы знаем что метод «лаять» заданное количество раз:
Объект всегда должен быть создан. Класс существует как факт, то есть, всегда. Создание объекта выглядит в разных языках программирования по-разному, хотя чаще всего это выглядит так:
МояСобака = new Собака()
Так создается новый экземпляр класса Собака. Иногда он может параметризироваться дополнительной информацией, необходимой для объекта (зависит от реализации класса):
МояСобака = new Собака(«Барбос»)
Разум неподготовленный слегка теряется от этих нескольких терминов, но эта модель, если подумать, очень напоминает окружающий нас мир, который довольно просто раскладывается на классы, объекты, свойства и методы. Этот навык очень помогает создавать программистам простую и понятную архитектуру приложения.
Наследование, инкапсуляция, полиморфизм
Три кита объектного ориентирования это: наследование, инкапсуляция и полиморфизм. Без четкого понимания этих вещей программисту тяжело написать хороший объектно-ориентированный код, использовать всю силу этого подхода, а главное устроится на хорошую работу.
В прошлый раз я рассказывал о классах, объектах, свойствах и методах, если с этими понятиями проблем не возникает, дальше тоже всё будет очень просто. Для наглядности я буду приводить короткие куски кода на PHP (на самом деле язык тут не важен, просто мне кажется это самый распространенный на сегодня синтаксис, хоть и более классическим для примеров ООП является java, мне кажется PHP будет более полезным), поясняющие идею, и давать короткие описания.
Как видите здесь Корова (Cow) унаследовала функционал от Животного (Animal) , изменив реализацию метода draw (конкретизируя как корова на самом деле выглядит), и оставив реализацию метода eat() . Это и есть наследование. Теперь инкапсуляция. Сам по себе этот термин означает «сокрытие». Инкапсуляция, это способ сделать невозможным изменения критичных для работы класса свойств или вызова внутренних методов. Например у нас есть требование: каждое животное должно иметь кличку, и кличка, в течении его жизни не должна меняться. Самое правильное в таком случае это принимать кличку в качестве параметра конструктора (метода выполняемого при создании класса), и хранить его во внутреннем, сокрытом свойстве. Например так:
Вот это и есть инкапсуляция. Нет способа изменить кличку снаружи класса, и вы можете быть уверены, что в любом случае, кличка у экземпляра класса будет именно та, что была задана при создании. Ну а теперь полиморфизм. Это тут тоже начнем с примера. Добавим класс Sheep (овца) .
Теперь, предположим что у нас есть класс какого-то животного (любого), и мы всегда можем узнать как оно выглядит, абсолютно независимо от его типа (другими словами с экземпляром какого класса мы имеем дело).
Данный пример будет случайным образом генерировать экземпляр Коровы и Овцы, и рисовать их.
Нужно сказать что данный пример не совсем «чистый» полиморфизм. Дело в том что полиморфизм подразумевает собой реализацию одного и того же интерфейса в разных классах. Объясню: если бы мы не реализовали метод draw() в одном из классов, у нас периодически возникала бы ошибка обращения к несуществующему методу, а в языках со строгой типизицией, ошибка бы возникала еще на стадии компиляции. Чтобы избежать подобных казусов, нужно использовать итерфейсы (interface):
Как только вы указали, что класс должен реализовывать интерфейс, компилятор или интерпретатор берет на себя обязательство проконтролировать что в классе реализованы методы, описанные в интерфейсе, что позволяет отлавливать ошибки еще до запуска приложения. Вот собственно и всё.
Введение в ООП с примерами на C#. Часть первая. Все, что нужно знать о полиморфизме
Введение в ООП с примерами на C#. Часть первая. Все, что нужно знать о полиморфизме
- Переводы , 14 июля 2016 в 21:00
- Пётр Соковых
Я много писал на смежные темы, вроде концепции MVC, Entity Framework, паттерна «Репозиторий» и т.п. Моим приоритетом всегда было полное раскрытие темы, чтобы читателю не приходилось гуглить недостающие детали. Этот цикл статей опишет абсолютно все концепции ООП, которые могут интересовать начинающих разработчиков. Однако эта статья предназначена не только для тех, кто начинает свой путь в программировании: она написана и для опытных программистов, которым может потребоваться освежить свои знания.
Сразу скажу, далеко в теорию мы вдаваться не будем — нас интересуют специфичные вопросы. Где это будет нужно, я буду сопровождать повествование кодом на C#.
Что такое ООП и в чём его плюсы?
«ООП» значит «Объектно-Ориентированное Программирование». Это такой подход к написанию программ, который основывается на объектах, а не на функциях и процедурах. Эта модель ставит в центр внимания объекты, а не действия, данные, а не логику. Объект — реализация класса. Все реализации одного класса похожи друг на друга, но могут иметь разные параметры и значения. Объекты могут задействовать методы, специфичные для них.
ООП сильно упрощает процесс организации и создания структуры программы. Отдельные объекты, которые можно менять без воздействия на остальные части программы, упрощают также и внесение в программу изменений. Так как с течением времени программы становятся всё более крупными, а их поддержка всё более тяжёлой, эти два аспекта ООП становятся всё более актуальными.
Что за концепции ООП?
Сейчас коротко о принципах, которые мы позже рассмотрим в подробностях:
- Абстракция данных: подробности внутренней логики скрыты от конечного пользователя. Пользователю не нужно знать, как работают те или иные классы и методы, чтоб их использовать. Подходящим примером из реальной жизни будет велосипед — когда мы ездим на нём или меняем деталь, нам не нужно знать, как педаль приводит его в движение или как закреплена цепь.
- Наследование: самый популярный принцип ООП. Наследование делает возможным повторное использование кода — если какой-то класс уже имеет какую-то логику и функции, нам не нужно переписывать всё это заново для создания нового класса, мы можем просто включить старый класс в новый, целиком.
- Инкапсуляция: включение в класс объектов другого класса, вопросы доступа к ним, их видимости.
- Полиморфизм: «поли» значит «много», а «морфизм» — «изменение» или «вариативность», таким образом, «полиморфизм» — это свойство одних и тех же объектов и методов принимать разные формы.
- Обмен сообщениями: способность одних объектов вызывать методы других объектов, передавая им управление.
Ладно, тут мы коснулись большого количества теории, настало время действовать. Я надеюсь, это будет интересно.
Полиморфизм
В этой статье мы рассмотрим буквально все сценарии использования полиморфизма, использование параметров и разные возможные типы мышления во время написания кода.
Перегрузка методов
- Давайте создадим консольное приложение InheritanceAndPolymorphism и класс Overload.cs с тремя методами DisplayOverload с параметрами, как ниже:
В главном методе Program.cs теперь напишем следующее:
И теперь, когда мы это запустим, вывод будет следующим:
DisplayOverload 100
DisplayOverload method overloading
DisplayOverload method overloading100
Класс Overload содержит три метода, и все они называются DisplayOverload , они различаются только типами параметров. В C# (как и в большистве других языков) мы можем создавать методы с одинаковыми именами, но разными параметрами, это и называется «перегрузка методов». Это значит, что нам нет нужды запоминать кучу имён методов, которые совершают одинаковые действия с разными типами данных.
Что нужно запомнить: метод идентифицируется не только по имени, но и по его параметрам.
Если же мы запустим следующий код:
Мы получим ошибку компиляции:
Error: Type ‘InheritanceAndPolymorphism.Overload’ already defines a member called ‘DisplayOverload’ with the same parameter types
Здесь вы можете видеть две функции, которые различаются только по возвращаемому типу, и скомпилировать это нельзя.
Что нужно запомнить: метод не идентифицируется по возвращаемому типу, это не полиморфизм.
Если мы попробуем скомпилировать
…то у нас это не получится:
Error: Type ‘InheritanceAndPolymorphism.Overload’ already defines a member called ‘DisplayOverload’ with the same parameter types
Здесь присутствуют два метода, принимающих целое число в качестве аргумента, с той лишь разницей, что один из них помечен как статический.
Что нужно запомнить: модификаторы вроде static также не являются свойствами, идентифицирующими метод.
Если мы запустим нижеследующий код, в надежде, что теперь-то идентификаторы у методов будут разными:
То нас ждёт разочарование:
Error: Cannot define overloaded method ‘DisplayOverload’ because it differs from another method only on ref and out
Что нужно запомнить: на идентификатор метода оказывают влияние только его имя и параметры (их тип, количество). Модификаторы доступа не влияют. Двух методов с одинаковыми идентификаторами существовать не может.
Роль ключевого слова params в полиморфизме
Параметры могут быть четырёх разных видов:
- переданное значение;
- преданная ссылка;
- параметр для вывода;
- массив параметров.
С первыми тремя мы, вроде, разобрались, теперь подробнее взглянем на четвёртый.
- Если мы запустим следующий код:
То получим две ошибки:
Error1: The parameter name ‘a’ is a duplicate
Error2: A local variable named ‘a’ cannot be declared in this scope because it would give a different meaning to ‘a’, which is already used in a ‘parent or current’ scope to denote something else
Отсюда следуют вывод: имена параметров должны быть уникальны. Также не могут быть одинаковыми имя параметра метода и имя переменной, созданной в этом же методе.
- Теперь попробуем запустить следующий код:
Overload.cs
Program.cs
Мы получим следующий вывод:
Akhil
Akhil 1
Akhil 2
Akhil 3
Мы можем передавать одинаковые ссылочные параметры столько раз, сколько захотим. В методе Display строка name имеет значение «Akhil». Когда мы меняем значение x на «Akhil1», на самом деле мы меняем значение name , т.к. через параметр x передана ссылка именно на него. То же и с y — все эти три переменных ссылаются на одно место в памяти.
Overload.cs
Program.cs
Это даст нам такой вывод:
Akhil 100
Mittal 100
OOP 100
Akhil 200
Нам часто может потребоваться передать методу n параметров. В C# такую возможность предоставляет ключевое слово params .
Важно: это ключевое слово может быть применено только к последнему аргументу метода, так что метод ниже работать не будет:
- В случае DisplayOverload первый аргумент должен быть целым числом, а остальные — сколь угодно много строк или наоборот, ни одной.
200 100
300 100
100 200
Важно запомнить: C# достаточно умён, чтоб разделить обычные параметры и массив параметров, даже если они одного типа.
- Посмотрите на следующие два метода:
- Следует упомянуть, что последний аргумент не обязательно заполнять отдельными объектами, можно его использовать, будто это обычный аргумент, принимающий массив, то есть:
Разница между ними в том, что первый запустится, и такая синтаксическая конструкция будет подразумевать, что в метод будет передаваться n массивов строк. Вторая же выдаст ошибку:
Error: The parameter array must be a single dimensional array
Запомните: массив параметров должен быть одномерным.
Overload.cs
Program.cs
Вывод будет следующим:
Akhil 3
Ekta 3
Arsh 3
Однако такой код:
Уже вызовет ошибку:
Error: The best overloaded method match for ‘InheritanceAndPolymorphism.Overload.DisplayOverload(int, params string[])’ has some invalid arguments
Error:Argument 2: cannot convert from ‘string[]’ to ‘string’
Думаю, тут всё понятно — или, или. Смешивать передачу отдельными параметрами и одним массивом нельзя.
- Теперь рассмотрим поведение следующей программы:
Overload.cs
Program.cs
После её выполнения мы получим в консоли:
Это происходит из-за того, что при подобном синтаксисе массив передаётся по ссылке. Однако стоит отметить следующую особенность:
Результатом выполнения такого кода будет
Ведь из переданных параметров C# автоматически формирует новый, временный массив.
- Теперь поговорим о приоритете языка в выборе методов. Предположим, у нас есть такой код:
C# рассматривает методы с массивом параметров последними, так что во втором случае будет вызван метод, принимающий два целых числа. В первом и третьем случае будет вызван метод с params , так как ничего кроме него запустить невозможно. Таким образом, на выходе мы получим:
parameterArray
The two integers 200 300
parameterArray
- Теперь кое-что интересное. Как вы думаете, каким будет результат выполнения следующей программы?
Overload.cs
Program.cs
В консоли мы увидим:
System.Int32 System.String System.Double
System.Object[] System.Object[] System.Int32 System.String System.Double
То есть, в первом и в четвёртом случаях массив передаётся именно как массив, заменяя собой objectParamArray , а во втором и третьем случаях массив передаётся как единичный объект, из которого создаётся новый массив из одного элемента.
В заключение
В этой статье мы рассмотрели перегрузку методов, особенности компиляции, с ней связанные, и буквально все возможные случаи использования ключевого слова params . В следующей мы рассмотрим наследование. Напоследок ещё раз повторим основные пункты, которые нужно запомнить:
- Метод идентифицируется не только по имени, но и по его параметрам.
- Метод не идентифицируется по возвращаемому типу.
- Модификаторы вроде static также не являются свойствами, идентифицирующими метод.
- На идентификатор метода оказывают влияние только его имя и параметры (их тип, количество). Модификаторы доступа не влияют. Двух методов с одинаковыми идентификаторами существовать не может.
- Имена параметров должны быть уникальны. Также не могут быть одинаковыми имя параметра метода и имя переменной, созданной в этом же методе.
- Ключевое слово params может быть применено только к последнему аргументу метода.
- C# достаточно умён, чтоб разделить обычные параметры и массив параметров, даже если они одного типа.
- Массив параметров должен быть одномерным.
Объектно-ориентированное программирование: на пальцах
Статья не мальчика, но мужа.
Настало время серьёзных тем: сегодня расскажем про объектно-ориентированное программирование, или ООП. Это тема для продвинутого уровня разработки, и мы хотим, чтобы вы его постигли.
Из этого термина можно сделать вывод, что ООП — это такой подход к программированию, где на первом месте стоят объекты. На самом деле там всё немного сложнее, но мы до этого ещё доберёмся. Для начала поговорим про ООП вообще и разберём, с чего оно начинается.
Обычное программирование (процедурное)
Чаще всего под обычным понимают процедурное программирование, в основе которого — процедуры и функции. Функция — это мини-программа, которая получает на вход какие-то данные, что-то делает внутри себя и может отдавать какие-то данные в результате вычислений. Представьте, что это такой конвейер, который упакован в коробочку.
Например, в интернет-магазине может быть функция «Проверить email». Она получает на вход какой-то текст, сопоставляет со своими правилами и выдаёт ответ: это правильный электронный адрес или нет. Если правильный, то true, если нет — то false.
Функции полезны, когда нужно упаковать много команд в одну. Например, проверка электронного адреса может состоять из одной проверки на регулярные выражения, а может содержать множество команд: запросы в словари, проверку по базам спамеров и даже сопоставление с уже известными электронными адресами. В функцию можно упаковать любой комбайн из действий и потом просто вызывать их все одним движением.
Что не так с процедурным программированием
Процедурное программирование идеально работает в простых программах, где все задачи можно решить, грубо говоря, десятком функций. Функции аккуратно вложены друг в друга, взаимодействуют друг с другом, можно передать данные из одной функции в другую.
Например, вы пишете функцию «Зарегистрировать пользователя интернет-магазина». Внутри неё вам нужно проверить его электронный адрес. Вы вызываете функцию «Проверить email» внутри функции «Зарегистрировать пользователя», и в зависимости от ответа функции вы либо регистрируете пользователя, либо выводите ошибку. И у вас эта функция встречается ещё в десяти местах. Функции как бы переплетены.
Тут приходит продакт-менеджер и говорит: «Хочу, чтобы пользователь точно знал, в чём ошибка при вводе электронного адреса». Теперь вам нужно научить функцию выдавать не просто true — false, а ещё и код ошибки: например, если в адресе опечатка, то код 01, если адрес спамерский — код 02 и так далее. Это несложно реализовать.
Вы залезаете внутрь этой функции и меняете её поведение: теперь она вместо true — false выдаёт код ошибки, а если ошибки нет — пишет «ОК».
И тут ваш код ломается: все десять мест, которые ожидали от проверяльщика true или false, теперь получают «ОК» и из-за этого ломаются.
Теперь вам нужно:
- либо переписывать все функции, чтобы научить их понимать новые ответы проверяльщика адресов;
- либо переделать сам проверяльщик адресов, чтобы он остался совместимым со старыми местами, но в нужном вам месте как-то ещё выдавал коды ошибок;
- либо написать новый проверяльщик, который выдаёт коды ошибок, а в старых местах использовать старый проверяльщик.
Задача, конечно, решаемая за час-другой.
Но теперь представьте, что у вас этих функций — сотни. И изменений в них нужно делать десятки в день. И каждое изменение, как правило, заставляет функции вести себя более сложным образом и выдавать более сложный результат. И каждое изменение в одном месте ломает три других места. В итоге у вас будут нарождаться десятки клонированных функций, в которых вы сначала будете разбираться, а потом уже нет.
Это называется спагетти-код, и для борьбы с ним как раз придумали объектно-ориентированное программирование.
Объектно-ориентированное программирование
Основная задача ООП — сделать сложный код проще. Для этого программу разбивают на независимые блоки, которые мы называем объектами.
Объект — это не какая-то космическая сущность. Это всего лишь набор данных и функций — таких же, как в традиционном функциональном программировании. Можно представить, что просто взяли кусок программы и положили его в коробку и закрыли крышку. Вот эта коробка с крышками — это объект.
Программисты договорились, что данные внутри объекта будут называться свойствами, а функции — методами. Но это просто слова, по сути это те же переменные и функции.
Объект можно представить как независимый электроприбор у вас на кухне. Чайник кипятит воду, плита греет, блендер взбивает, мясорубка делает фарш. Внутри каждого устройства куча всего: моторы, контроллеры, кнопки, пружины, предохранители — но вы о них не думаете. Вы нажимаете кнопки на панели каждого прибора, и он делает то, что от него ожидается. И благодаря совместной работе этих приборов у вас получается ужин.
Объекты характеризуются четырьмя словами: инкапсуляция, абстракция, наследование и полиморфизм.
Инкапсуляция — объект независим: каждый объект устроен так, что нужные для него данные живут внутри этого объекта, а не где-то снаружи в программе. Например, если у меня есть объект «Пользователь», то у меня в нём будут все данные о пользователе: и имя, и адрес, и всё остальное. И в нём же будут методы «Проверить адрес» или «Подписать на рассылку».
Абстракция — у объекта есть «интерфейс»: у объекта есть методы и свойства, к которым мы можем обратиться извне этого объекта. Так же, как мы можем нажать кнопку на блендере. У блендера есть много всего внутри, что заставляет его работать, но на главной панели есть только кнопка. Вот эта кнопка и есть абстрактный интерфейс.
В программе мы можем сказать: «Удалить пользователя». На языке ООП это будет «пользователь.удалить()» — то есть мы обращаемся к объекту «пользователь» и вызываем метод «удалить». Кайф в том, что нам не так важно, как именно будет происходить удаление: ООП позволяет нам не думать об этом в момент обращения.
Например, над магазином работают два программиста: один пишет модуль заказа, а второй — модуль доставки. У первого в объекте «заказ» есть метод «отменить». И вот второму нужно из-за доставки отменить заказ. И он спокойно пишет: «заказ.отменить()». Ему неважно, как другой программист будет реализовывать отмену: какие он отправит письма, что запишет в базу данных, какие выведет предупреждения.
Наследование — способность к копированию. ООП позволяет создавать много объектов по образу и подобию другого объекта. Это позволяет не копипастить код по двести раз, а один раз нормально написать и потом много раз использовать.
Например, у вас может быть некий идеальный объект «Пользователь»: в нём вы прописываете всё, что может происходить с пользователем. У вас могут быть свойства: имя, возраст, адрес, номер карты. И могут быть методы «Дать скидку», «Проверить заказ», «Найти заказы», «Позвонить».
На основе этого идеального пользователя вы можете создать реального «Покупателя Ивана». У него при создании будут все свойства и методы, которые вы задали у идеального покупателя, плюс могут быть какие-то свои, если захотите.
Идеальные объекты программисты называют классами.
Полиморфизм — единый язык общения. В ООП важно, чтобы все объекты общались друг с другом на понятном им языке. И если у разных объектов есть метод «Удалить», то он должен делать именно это и писаться везде одинаково. Нельзя, чтобы у одного объекта это было «Удалить», а у другого «Стереть».
При этом внутри объекта методы могут быть реализованы по-разному. Например, удалить товар — это выдать предупреждение, а потом пометить товар в базе данных как удалённый. А удалить пользователя — это отменить его покупки, отписать от рассылки и заархивировать историю его покупок. События разные, но для программиста это неважно. У него просто есть метод «Удалить()», и он ему доверяет.
Такой подход позволяет программировать каждый модуль независимо от остальных. Главное — заранее продумать, как модули будут общаться друг с другом и по каким правилам. При таком подходе вы можете улучшить работу одного модуля, не затрагивая остальные — для всей программы неважно, что внутри каждого блока, если правила работы с ним остались прежними.
Плюсы и минусы ООП
У объектно-ориентированного программирования много плюсов, и именно поэтому этот подход использует большинство современных программистов.
- Визуально код становится проще, и его легче читать. Когда всё разбито на объекты и у них есть понятный набор правил, можно сразу понять, за что отвечает каждый объект и из чего он состоит.
- Меньше одинакового кода. Если в обычном программировании одна функция считает повторяющиеся символы в одномерном массиве, а другая — в двумерном, то у них большая часть кода будет одинаковой. В ООП это решается наследованием.
- Сложные программы пишутся проще. Каждую большую программу можно разложить на несколько блоков, сделать им минимальное наполнение, а потом раз за разом подробно наполнить каждый блок.
- Увеличивается скорость написания. На старте можно быстро создать нужные компоненты внутри программы, чтобы получить минимально работающий прототип.
А теперь про минусы:
- Сложно понять и начать работать. Подход ООП намного сложнее обычного функционального программирования — нужно знать много теории, прежде чем будет написана хоть одна строчка кода.
- Требует больше памяти. Объекты в ООП состоят из данных, интерфейсов, методов и много другого, а это занимает намного больше памяти, чем простая переменная.
- Иногда производительность кода будет ниже. Из-за особенностей подхода часть вещей может быть реализована сложнее, чем могла бы быть. Поэтому бывает такое, что ООП-программа работает медленнее, чем функциональная (хотя с современными мощностями процессоров это мало кого волнует).
Что дальше
Впереди нас ждёт разговор о классах, объектах и всём остальном важном в ООП. Крепитесь, будет интересно!
hyyudu
Что на глаза попалось
Всем привет! В силу некоторых причин и пинка от windler «а то в блогах щас все про политику и про политику — надо разбавлять» решил я написать еще один пост из серии «компьютерные технологии для чайников на пальцах». Хоть он у меня и пройдет под тегом «сайтостроение», непосредственно прямой практики тут не будет. А будет тут рассказ «Объектно-ориентированное программирование, и зачем его едят».
Объектно-ориентированное программирование (ООП) — одна из парадигм, моделей написания программ. Их вообще есть еще несколько — функциональное, логическое, императивное ака процедурное и т.д. Если захотите — в Випипедии найдете. Наш же рассказ пойдет в особенности про хоббитов. Точнее, про объекты.
Первый постулат теории ООП прост как блин: «Все есть объект». В качестве минутки самолюбования скажу, что этот же постулат (вместе с еще несколькими положениями ООП) с моей легкой руки переполз в ХСную модель трансфигурации, да ну не о ней речь.
У любого объекта есть свойства (они же поля) и функции (они же методы, они же действия). У всякого свойства есть имя и значение, у всякого метода могут быть аргументы. Еще объект может реагировать на какие-то события.
Теперь пять секунд мотаем головой, приводя мысли в порядок, и начинаем разбирать конкретные примеры.
Есть собака Шарик рыжего цвета весом 10 кг и возрастом 2 года. Но это для нас она собака Шарик рыжего цвета и т.д. Для ООП это объект, у которого есть четыре свойства с именами «Имя», «Цвет», «Вес» и «Возраст» и значениями, соответственно, «Шарик», «рыжий», «10» и «2».
Собака Шарик умеет лаять. С точки зрения ООП объект Шарик имеет метод «лаять». У этого метода могут быть аргументы — например, «лаять (на кого?)». В зависимости от того, на кого он лает, Шарик лает по-разному — на гордо гуляющего по улице холеного дога он будет лаять завистливо, на соседскую кошку — злобно, а на приходящего с работы хозяина — радостно.
Еще Шарик умеет реагировать на события. Например, если он слышит, как хозяин звякает шариковой миской, он бежит есть, а если его дергают за хвост — кусает обидчика. У объекта Шарик есть обработчик события «хозяин гремит миской» — это функция «бежать есть» (которая состоит из двух последовательно вызываемых методов Бежать (к миске) и Есть (еду в миске)), и обработчик события «дернули за хвост (кто-то)» — «укусить (этого кто-то)».
Кстати говоря, методы и свойства могут быть открытыми для окружающих (публичными) и закрытыми (приватными). На публичные свойства Шарика могут оказывать воздействие другие объекты с помощью своих методов. На приватные может воздействовать только он сам. Например, у него есть приватное свойство «уровень сытости», которое извне не изменить — оно поменяется с «низкого» на «высокий», когда Шарик поест (т.е. будет пущен в дело его метод «кушать»). А вот «цвет» — вполне себе публичное свойство, и движимый тягой экспериментаторства объект СынХозяинаШарика может применить к нему метод ПокраситьГуашью, что напрямую изменит Шарику свойство «цвет».
При написании программы с использованием ООП строится математическая модель — это упрощенное представление объектов реального мира с той степенью абстракции, которая нам требуется для решения поставленной задачи. Например, если мы программируем движение Шарика от леса, где он бегал и гулял, обратно к его будке, нам в сущности совершенно не интересны его свойства Цвет, Вес, Возраст, УровеньСытости — нас интересует только его местоположение в определенный момент. Также мы можем сделать допущение, что в пути от леса до будки он не может услышать громыхание миски, и за хвост его никто дергать не будет, поэтому обработчики событий нам тоже не понадобятся. Да и из всех его методов потребуется только один-единственный — Бежать.
Обычно при рассказе об ООП все идут от общего к частному. Я наоборот — пойду от частного к общему.
Кроме объектов, в ООП есть такое понятие, как класс. Класс — это совокупность объектов, имеющих одинаковый набор свойств и методов. Например, есть класс Собаки, экземплярами которого являются объект Шарик, объект в соседнем дворе Бобик и объект в доме напротив Трезор. У них у всех есть имя, вес, цвет, возраст, все они умеют лаять и реагировать на дерганье за хвост. Правда, Бобик — это бульдог, и у него свойство РазмерХвоста имеет очень маленькое значение, поэтому уже знакомому нам объекту СынХозяинаШарика довольно сложно применить к нему метод ПодергатьЗаХвост. Но тем не менее, обработчик события у Бобика есть, так что если кто-то умудрится его таки дернуть — он его таки цапнет.
Классы могут наследоваться друг от друга. Раз уж мы идем снизу вверх, найдем родителя для класса Собаки — это может быть, например, класс ДомашниеЖивотные. А у того, соответственно, родительский класс — Животные. Можно посмотреть и с другой стороны: есть класс Животные, у него есть потомок ДомашниеЖивотные, у того есть потомок — Собаки (и еще потомки Кошки, Коровы и Лошади). У каждого класса может быть сколько угодно потомков и только один предок. Каждый класс наследует методы и свойства от родителя, а некоторые из них каким-то образом меняет.
То бишь по факту класс — это болванка, заготовка, модель, прототип, на основании которой клепаются похожие детальки — объекты. Есть класс Собаки, в котором описывается какой-то прототип собаки как пушистого зверя с четырьмя лапами и хвостом, который умеет бегать, лаять и кусать, и у которого есть определенные цвет, размер, имя, порода и т.д. Все члены класса Собаки имеют одинаковые методы, то есть бегают они одинаково, лают одинаково и кусают одинаково — вплоть до их свойств. Т.е., например, борзая Дуська больше болонки Таськи и бегает быстрее, но сам по себе бег что у борзой, что у болонки, что у какой-нибудь чау-чау совершенно одинаковый — все они бегают, перебирая четырьмя лапами, никто не бегает на хвосте или на ушах.
ООП, как плоская земля, покоится на трех китах: инкапсуляции, наследовании и полиморфизме.
Инкапсуляция — это объединение какого-то набора данных и функций для обработки этих данных в единую сущность. Ну, это мы уже наблюдали — набор данных Имя, Рост, Вес, Цвет и Порода плюс методы Бежать, Есть, Лаять, Кусать и т.д. вместе составляют сущность — экземпляр класса Собаки. Без инкапсуляции у нас методы живут отдельно, свойства отдельно, и вообще имеют обыкновение смешиваться в желе.
А с инкапсуляцией мы можем делать из объектов «черные ящики», которые имеют некоторое количество публичных методов и свойств, а также обработчиков событий, которые в сумме называются интерфейсом класса. При этом программист, который использует класс, написанный другим программистом, может совершенно не вникать во внутреннюю «кухню» этого класса — ему достаточно знать, что объект работает так, а не иначе. Иначе говоря, сторонний программист может совершенно не знать, что у нашего Шарика есть свойство УровеньСытости — он просто знает, что если применить к Шарику метод Покормить, он из голодного превратится в сытого.
Другая аналогия: на заводе собрали телевизор. Покупатель может не иметь никакого представления о том, как там бегают электроны в электронно-лучевой трубке, или как устроена жидкокристаллическая матрица. Все это скрыто от него, т.е. эти свойства у телевизора — приватные. Зато есть публичный интерфейс, выраженный в кнопке включения. И пользователь телевизора знает, что если он нажмет на кнопку включения — телевизор включится, а если он уже был включен — выключится.
Наследование — это то, о чем я уже рассказывал. Класс-потомок расширяет и углубляет функционал класса-предка. Например, от класса Собаки может быть унаследован класс ДрессированныеСобаки, в котором останутся все методы Собак (Бежать, Есть, Кусать, Лаять) и добавятся новые (ПринестиПалку, например). Кроме того, некоторые методы предка могут быть перегружены, т.е. переопределены. Например, метод Кусать будет выглядеть уже не так:
Кусать(противник) <
Подойти(противник)
Укусить(противник)
>
а
Кусать(противник) <
Подойти(противник)
Если (ХозяинРазрешил)
Укусить(противник)
>
Кстати, функционал предка местами может быть даже не реализован, т.е. быть абстрактным. Например, класс Животное имеет метод ПодатьГолос, но реально этот метод не делает ничего — просто потому, что мы не можем сказать, как именно подает голос какое-то абстрактное Животное. У Животного есть класс-потомок ДомашнееЖивотное, но и у него этот метод тоже абстрактный. А вот у потомков ДомашнегоЖивотного — у Собак, Коров, Лошадей — этот метод уже реализован, причем по-разному — собаки лают, коровы мычат, лошади ржут. Это, собственно, называется Полиморфизмом.
Написано много, непонятно и путано, так что если у вас после этого остался еще мозг, и вы хотите его довынести окончательно — задавайте вопросы