Что такое ООП на примерах. Для чайников



















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


Итак, уже в начале статьи я уже упомянул такой термин "методология". Применительно к программированию этот термин подразумевает наличие какого-либо набора способов организации кода, методов его написания, придерживаясь которых, программист сможет писать
вполне годные программы.


ООП (или объектно-ориентированное программирование) представляет собой способ организации кода программы, когда основными строительными блоками программы являются объекты и классы, а логика работы программы построена на их взаимодействии.



Об объектах и классах


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


Допустим, нам нужно написать программу, рассчитывающую периметр и площадь треугольника, который задан двумя сторонами и углом между ними. Для написания такой программы используя ООП, нам необходимо будет создать класс (то есть структуру) Треугольник.
Класс Треугольник будет хранить три поля (три переменные): сторона А, сторона Б, угол между ними; и два метода (две функции): посчитать периметр, посчитать площадь. Данным классом мы можем описать любой треугольник и вычислить периметр и площадь.
Так вот, конкретный треугольник с конкретными сторонами и углом между ними будет называться экземпляром класса Треугольник. Таким образом класс - это шаблон, а экземпляр - конкретная реализация шаблона. А вот уже экземпляры являются объектами, то
есть конкретными элементами, хранящими конкретные значения.



Одним из самых распространенных объектно-ориентированных языков программирования является язык java. Там без использования объектов просто не обойтись. Вот как будет выглядеть код класса, описывающего треугольник на этом языке:


/**
* Класс Треугольник.
*/
class Triangle {

/**
* Специальный метод, называемый конструктор класса.
* Принимает на вход три параметра:
* длина стороны А, длина стороны Б,
* угол между этими сторонами(в градусах)
*/
Triangle(double sideA, double sideB, double angleAB) {
this.sideA = sideA;
this.sideB = sideB;
this.angleAB = angleAB;
}

double sideA; //Поле класса, хранит значение стороны А в описываемом треугольнике
double sideB; //Поле класса, хранит значение стороны Б в описываемом треугольнике
double angleAB; //Поле класса, хранит угла(в градусах) между двумя сторонами в описываемом треугольнике

/**
* Метод класса, который рассчитывает площадь треугольника
*/
double getSquare() {
double square = this.sideA * this.sideB * Math.sin(this.angleAB * Math.PI / 180);
return square;
}

/**
* Метод класса, который рассчитывает периметр треугольника
*/
double getPerimeter() {
double sideC = Math.sqrt(Math.pow(this.sideA, 2) + Math.pow(this.sideB, 2) - 2 * this.sideA * this.sideB * Math.cos(this.angleAB * Math.PI / 180));
double perimeter = this.sideA + this.sideB + sideC;
return perimeter;
}
}

Если мы внутрь класса добавим следующий код:


/**
* Именно в этом месте запускается программа
*/
public static void main(String[] args) {
//Значения 5, 17, 35 попадают в конструктор класса Triangle
Triangle triangle1 = new Triangle(5, 17, 35);
System.out.println("Площадь треугольника1: "+triangle1.getSquare());
System.out.println("Периметр треугольника1: "+triangle1.getPerimeter());

//Значения 6, 8, 60 попадают в конструктор класса Triangle
Triangle triangle2 = new Triangle(6, 8, 60);
System.out.println("Площадь треугольника1: "+triangle2.getSquare());
System.out.println("Периметр треугольника1: "+triangle2.getPerimeter());
}

то программу уже можно будет запускать на выполнение. Это особенность языка java. Если в классе есть такой метод


public static void main(String[] args)

то этот класс можно выполнять. Разберем код подробнее. Начнем со строки


Triangle triangle1 = new Triangle(5, 17, 35);

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


System.out.println("Площадь треугольника1: "+triangle1.getSquare());
System.out.println("Периметр треугольника1: "+triangle1.getPerimeter());

выводят рассчитанные площадь треугольника и его периметр в консоль.


Аналогично все происходит и для второго экземпляра класса Triangle.


Понимание сути классов и конструирования конкретных объектов - это уверенный первый шаг к пониманию методологии ООП.


Еще раз, самое важное:


ООП - это способ организации кода программы;


Класс - это пользовательская структура данных, которая воедино объединяет данные и функции для работы с ними(поля класса и методы класса);


Объект - это конкретный экземпляр класса, полям которого заданы конкретные значения.



Три волшебных слова


ООП включает три ключевых подхода: наследование, инкапсуляцию и полиморфизм. Для начала, приведу определения из wikipedia:



Инкапсуляция — свойство системы, позволяющее объединить данные и методы, работающие с ними, в классе. Некоторые языки (например, С++) отождествляют инкапсуляцию с сокрытием, но большинство (Smalltalk, Eiffel, OCaml) различают эти понятия.




Наследование — свойство системы, позволяющее описать новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью. Класс, от которого производится наследование, называется базовым, родительским или суперклассом.
Новый класс — потомком, наследником, дочерним или производным классом.




Полиморфизм — свойство системы, позволяющее использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.



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



Итак, точка — это самая малая геометрическая фигура, которая является основой всех прочих построений (фигур). Поэтому именно точка выбрана в качестве базового родительского класса. Напишем класс точки на java:


/**
* Класс точки. Базовый класс
*/
class Point {

/**
* Пустой конструктор
*/
Point() {}

/**
* Метод класса, который рассчитывает площадь фигуры
*/
double getSquare() {
return 0;
}

/**
* Метод класса, который рассчитывает периметр фигуры
*/
double getPerimeter() {
return 0;
}

/**
* Метод класса, возвращающий описание фигуры
*/
String getDescription() {
return "Точка";
}
}

У получившегося класса Point пустой конструктор, поскольку в данном примере мы работаем без конкретных координат, а оперируем только параметрами значениями сторон. Так как у точки нет никаких сторон,
то и передавать ей никаких параметров не надо. Также заметим, что класс имеет методы Point::getSquare() и Point::getPerimeter() для расчета площади и
периметра, оба возвращают 0. Для точки оно и логично.



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


/**
* Класс Отрезок
*/
class LineSegment extends Point {

LineSegment(double segmentLength) {
this.segmentLength = segmentLength;
}

double segmentLength; // Длина отрезка

/**
* Переопределенный метод класса, который рассчитывает площадь отрезка
*/
double getSquare() {
return 0;
}

/**
* Переопределенный метод класса, который рассчитывает периметр отрезка
*/
double getPerimeter() {
return this.segmentLength;
}

String getDescription() {
return "Отрезок длиной: " + this.segmentLength;
}
}

Запись


class LineSegment extends Point

означает, что класс LineSegment наследуется от класса Point. Методы LineSegment::getSquare() и LineSegment::getPerimeter() переопределяют соответствующие методы базового класса. Площадь отрезка всегда равняется нулю, а площадь периметра равняется длине этого отрезка.


Теперь, подобно классу отрезка, опишем класс треугольника(который также наследуется от класса точки):


/**
* Класс Треугольник.
*/
class Triangle extends Point {

/**
* Конструктор класса. Принимает на вход три параметра:
* длина стороны А, длина стороны Б,
* угол между этими сторонами(в градусах)
*/
Triangle(double sideA, double sideB, double angleAB) {
this.sideA = sideA;
this.sideB = sideB;
this.angleAB = angleAB;
}

double sideA; //Поле класса, хранит значение стороны А в описываемом треугольнике
double sideB; //Поле класса, хранит значение стороны Б в описываемом треугольнике
double angleAB; //Поле класса, хранит угла(в градусах) между двумя сторонами в описываемом треугольнике

/**
* Метод класса, который рассчитывает площадь треугольника
*/
double getSquare() {
double square = (this.sideA * this.sideB * Math.sin(this.angleAB * Math.PI / 180))/2;
return square;
}

/**
* Метод класса, который рассчитывает периметр треугольника
*/
double getPerimeter() {
double sideC = Math.sqrt(Math.pow(this.sideA, 2) + Math.pow(this.sideB, 2) - 2 * this.sideA * this.sideB * Math.cos(this.angleAB * Math.PI / 180));
double perimeter = this.sideA + this.sideB + sideC;
return perimeter;
}

String getDescription() {
return "Треугольник со сторонами: " + this.sideA + ", " + this.sideB + " и углом между ними: " + this.angleAB;
}
}

Тут нет ничего нового. Также, методы Triangle::getSquare() и Triangle::getPerimeter() переопределяют соответствующие методы базового класса.
Ну а теперь,
собственно, тот самый код, который показывает волшебство полиморифзма и раскрывает мощь ООП:


class Main {
/**
* Именно в этом месте запускается программа
*/
public static void main(String[] args) {
//ArrayList - Это специальная структура данных в java,
// позволяющая хранить объекты определенного типа в массиве.
ArrayList figures = new ArrayList();

//добавляем три разных объекта в массив figures
figures.add(new Point());
figures.add(new LineSegment(133));
figures.add(new Triangle(10, 17, 55));

for (int i = 0; i

Мы создали массив объектов класса Point, а поскольку классы LineSegment и Triangle наследуются от класса Point, то и их мы можем помещать в этот массив. Получается, каждую фигуру, которая есть в массиве figures мы можем рассматривать как объект класса Point.
В этом и заключается полиморфизм: неизвестно, к какому именно классу принадлежат находящиеся в массиве figures объекты, но поскольку все объекты внутри этого массива принадлежат одному базовому классу Point,
то все методы, которые применимы к классу Point также и применимы к его классам-наследникам.



Теперь о инкапсуляции. То, что мы поместили в одном классе параметры фигуры и методы расчета площади и периметра - это и есть инкапсуляция, мы инкапсулировали фигуры в отдельные классы. То, что у нас для расчета периметра используется специальный
метод в классе - это и есть инкапсуляцию, мы инкапсулировали расчет периметра в метод getPerimiter(). Иначе говоря, инкапсуляция - это сокрытие реализции (пожалуй, самое короткое, и в то же время
емкое определением инкапсуляции).



Полный код примера:


import java.util.ArrayList;

class Main {
/**
* Именно в этом месте запускается программа
*/
public static void main(String[] args) {
//ArrayList - Это специальная структура данных в java,
// позволяющая хранить объекты определенного типа в массиве.
ArrayList figures = new ArrayList();

//добавляем три разных объекта в массив figures
figures.add(new Point());
figures.add(new LineSegment(133));
figures.add(new Triangle(10, 17, 55));

for (int i = 0; i
30293   5  

Comments

  1. Сергей
    Сергей 5 лет назад
    Отличная статья, все доступно
  2. Nū Kolupa
    Nū Kolupa 5 лет назад
    <p>Good article!</p>
  3. АГГ
    АГГ 5 лет назад
    <p>Хоть что-то стало понятно.</p>
  4. Lana
    Lana 4 года назад
    <p>полный код обрывается на <strong>for (int i = 0; i</strong></p>
  5. Начинающий
    Начинающий 3 года назад
    <p>Честно сказать, я вот начинающий, все написано терминами непонятными, для начинающих непонятно написано. Тема сисек не раскрыта!</p>