sonyps4.ru

Основные принципы ооп инкапсуляция. Проблемы структурного программирования

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

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

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


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

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

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

Одним из самых распространенных объектно-ориентированных языков программирования является язык 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

Базовые принципы объектно-ориентированного программирования

Объектно-ориентированное программирование основывается на трех основных концепциях: инкапсуляции , полиморфизме и наследовании .

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

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

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

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

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

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

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

- определить объект для заданного класса;

- построить новый класс, наследуя его из существующего класса;

- изменить поведение нового класса (изменить существующие и добавить новые функции).

Построение нового класса, наследуя его из существующего, предполагает:

- добавление в новый класс новых компонент-данных;

- добавление в новый класс новых компонент-функций;

- замену в новом классе наследуемых из старого класса компонент-функций;

Наследование может быть одиночным и множественным (рис. 1). При множественном наследовании производный (новый) класс имеет более одного наследуемого (старого) класса, из которых образуется новый класс. При этом новый класс наследует поведение этих классов.

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

При разработке объектно-ориентированных программ часто используются библиотеки классов. Библиотека может рассматриваться как заданная базовая иерархическая структура. Для разрабатываемой программы из библиотеки может быть выбрана некоторая подструктура и затем расширена новыми классами с использованием принципов наследования.

Язык программирования называется объектно-ориентированным, если:

- он поддерживает абстрактные типы данных (объекты с определенным интерфейсом и скрытым внутренним состоянием);

- объекты имеют связанные с ними типы (классы);

- поддерживается механизм наследования.

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

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

Для того чтобы стать профессионалом в программировании, необходимы талант, способность к творчеству, интеллект , знания, логика, умение строить и использовать абстракции и, самое главное, опыт .

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

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

Основные концепции ООП

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

Центральный элемент ООП - абстракция . Данные с помощью абстракции преобразуются в объекты, а последовательность обработки этих данных превращается в набор сообщений, передаваемых между этими объектами. Каждый из объектов имеет свое собственное уникальное поведение. С объектами можно обращаться как с конкретными сущностями, которые реагируют на сообщения, приказывающие им выполнить какие-то действия.

ООП характеризуется следующими принципами ( по Алану Кею):

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

Определение 10.1 . Абстрагирование (abstraction) - метод решения задачи, при котором объекты разного рода объединяются общим понятием (концепцией), а затем сгруппированные сущности рассматриваются как элементы единой категории.

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

Определение 10.2 . Инкапсуляция (encapsulation) - техника, при которой несущественная с точки зрения интерфейса объекта информация прячется внутри него.

Определение 10.3 . Наследование (inheritance) - свойство объектов, посредством которого экземпляры класса получают доступ к данным и методам классов-предков без их повторного определения.

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

Определение 10.4 .

Основные принципы и этапы объектно-ориентированного

программирования

В теории программирования ООП определяется как технология создания сложного программного обеспечения, которая основана на представлении программы в виде совокупности объектов , каждый из которых является экземпляром определенноготипа (класса ), а классы образуют иерархию с

наследованием свойств .

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

П р и м е ч а н и е. Такое представление программы впервые было использовано в языке имитационного моделирования сложных систем Simula, появившемся еще в 60-х годах.

Естественный для языков моделирования способ представления программы получил развитие в другом специализированном языке моделирования - языке Smalltalk (70-е годы), а затем был

Страница 2 из51

Основные принципы ООП

использован в новых версиях универсальных языков программирования, таких как Pascal, С++,

Основное достоинство ООП - сокращение количества межмодульных вызовов и уменьшение объемов информации, передаваемой между модулями,

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

что позволяет вести практически независимую разработку отдельных частей

(объектов) программы.

Кроме этого, объектный подход предлагает новые технологические средства разработки, такие как наследование, полиморфизм, композиция, наполнение ,

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

появляется возможность создания библиотек объектов для различных применений, и разработчикам предоставляются дополнительные возможности создания систем повышенной сложности.

Основной недостаток ООП - некоторое снижение быстродействия за счет более сложной организации программной системы.

В основу ООП положены следующие п р и н ц и п ы : абстрагирование,

ограничение доступа, модульность, иерархичность, типизация, параллелизм,

устойчивость.

Рассмотрим, что представляет собой каждый принцип.

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

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

третьем - материалы, из которых он сделан, в четвертом - закон движения

Страница 3 из51

Основные принципы ООП

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

так и определяющих его поведение) в единую программную единицу некий

абстрактный тип (класс).

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

Необходимость ограничения доступа предполагает разграничение двух частей в описании абстракции:

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

реализация - совокупность недоступных извне элементов реализации абстракции (внутренняя организация абстракции и механизмы реализации ее поведения).

Ограничение доступа в ООП позволяет разработчику:

выполнять конструирование системы поэтапно, не отвлекаясь на особенности реализации используемых абстракций;

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

Сочетание объединения всех свойств предмета (составляющих его состояния и поведения) в единую абстракцию и ограничения доступа к реализации этих свойств получило название инкапсуляции.

М о д у л ь н о с т ь - принцип разработки программной системы,

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

Страница 4 из51

Основные принципы ООП

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

отладку программы.

И е р а р х и я - ранжированная или упорядоченная система абстракций.

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

В ООП используются два вида иерархии.

Иерархия «целое/часть» - показывает, что некоторые абстракции включены

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

Иерархия «общее/частное» - показывает, что некоторая абстракция является частным случаем другой абстракции, например, « обеденный стол -

конкретный вид стола», а « столы - конкретный вид мебели». Используется при

разработке структуры классов, когда сложные классы строятся на базе более простых путем добавления к ним новых характеристик и, возможно, уточнения имеющихся.

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

наследование).

Т и п и з а ц и я - ограничение, накладываемое на свойства объектов и

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

объявляется тип, который определяет множество операций над

Страница 5 из51

Основные принципы ООП

соответствующим программным объектом. Рассматриваемые далее языки программирования на основе Паскаля используют строгую, а на основе С -

среднюю степень типизации.

Использование принципа типизации обеспечивает:

раннее обнаружение ошибок, связанных с недопустимыми операциями над программными объектами (ошибки обнаруживаются на этапе компиляции программы при проверке допустимости выполнения данной операции над программным объектом);

упрощение документирования;

возможность генерации более эффективного кода.

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

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

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

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

Реальный параллелизм достигается только при реализации задач такого типа на многопроцессорных системах, когда имеется возможность выполнения каждого процесса отдельным процессором. Системы с одним процессором имитируют параллелизм за счет разделения времени процессора между задачами управления различными процессами. В зависимости от типа используемой операционной системы (одноили мультипрограммной)

Страница 6 из51

Основные принципы ООП

разделение времени может выполняться либо разрабатываемой системой (как в

MS DOS), либо используемой ОС (как в системах Windows).

У с т о й ч и в о с т ь - свойство абстракции существовать во времени независимо от процесса, породившего данный программный объект, и/или в пространстве, перемещаясь из адресного пространства, в котором он был создан.

Различают:

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

∙ локальные объекты, существующие внутри подпрограмм, время жизни которых исчисляется от вызова подпрограммы до ее завершения;

∙ глобальные объекты, существующие пока программа загружена в память;

∙ сохраняемые объекты, данные которых хранятся в файлах внешней памяти между сеансами работы программы.

Все указанные выше принципы в той или иной степени реализованы в различных версиях объектно-ориентированных языков.

Объектно-ориентированные языки программирования.Язык считается объектно-ориентированным, если в нем реализованы первые четыре из рассмотренных семи принципов.

Особое место занимают объектные модели Delphi и C++Builder. Эти модели обобщают опыт ООП для MS DOS и включают некоторые новые средства,

обеспечивающие эффективное создание более сложных систем. На базе этих моделей созданы визуальные среды для разработки приложений Windows.

Сложность программирования под Windows удалось существенно

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

Страница 7 из51

Основные принципы ООП

Этапы разработки программных систем с использованием ООП.

Процесс разработки программного обеспечения с использованием ООП включает четыре этапа: анализ, проектирование, эволюция, модификация.

Рассмотрим эти этапы.

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

Проект ирование .Различают :

логическое проектирование, при котором принимаемые решения практически не зависят от условий эксплуатации (операционной системы и используемого оборудования);

физическое проектирование, при котором приходится принимать во внимание указанные факторы.

Логическое проектирование заключается в разработке структуры классов:

определяются поля для хранения составляющих состояния объектов и алгоритмы методов, реализующих аспекты поведения объектов. При этом используются рассмотренные выше приемы разработки классов (наследование,

композиция, наполнение, полиморфизм и т.д.). Результатом является иерархия или диаграмма классов, отражающие взаимосвязь классов, и описание классов.

Физическое проектирование включает объединение описаний классов в модули, выбор схемы их подключения (статическая или динамическая компоновка), определение способов взаимодействия с оборудованием, с

операционной системой и/или другим программным обеспечением (например,

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

Страница 8 из51

Основные принципы ООП

Э в о л ю ц и я с и с т е м ы. Это процесс поэтапной реализации и

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

работающий прототип будущей системы. Он тестируется и отлаживается.

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

Полученный вариант также тестируется и отлаживается, и так далее, до реализации всех возможностей системы.

Использование поэтапной реализации существенно упрощает тестирование и отладку программного продукта.

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

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

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

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

Страница 9 из51

Основные принципы ООП

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

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

Рассмотрение основных приемов объектного подхода начнем с объектной декомпозиции.

Объектная декомпозиция

Как уже упоминалось выше, при использовании технологии ООП решение представляется в виде результата взаимодействия отдельных функциональных элементов некоторой системы, имитирующей процессы,

происходящие в предметной области поставленной задачи.

В такой системе каждый функциональный элемент, получив некоторое входное воздействие (называемое сообщением) в процессе решения задачи,

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

Функциональные элементы системы, параметры и поведение которой определяются условием задачи, обладающие самостоятельным поведением

(т.е. « умеющие» выполнять некоторые действия, зависящие от полученных сообщений и состояния элемента), получили название объектов.

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

Страница 10 из51

Основные принципы ООП

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

Пример. Объектная декомпозиция (имитационная модель

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

Задачи такого вида обычно решаются с использованием имитационных моделей. Модель программно имитирует реальный процесс с заданными параметрами, параллельно фиксируя его характеристики. Многократно повторяя процесс имитации с различными значениями параметров обслуживания или поступления заявок, исследователь получает конкретные значения характеристик, по которым строятся графики анализируемых зависимостей.

Процесс работы бензоколонки с тремя заправочными местами можно представить в виде диаграммы.

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

Что такое объект?

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

Абстракция

Давайте теперь подумаем, как нам перейти от объекта из реального мира к объекту в программе на примере телефона. История этого средства связи превышает 100 лет и современный телефон, в отличие от своего предшественника из 19 века, представляет собой куда более сложное устройство. Когда мы пользуемся телефоном, то не задумываемся о его устройстве и процессах, происходящих внутри него. Мы просто используем функции, предоставленные разработчиками телефона - кнопки или сенсорный экран для выбора номера и совершения вызовов. Одним из первых интерфейсов телефона была рукоятка, которую нужно было вращать, чтобы сделать вызов. Разумеется, это было не очень удобно. Тем не менее, свою функцию рукоять исправно выполняла. Если посмотреть на самый современный и на самый первый телефон, можно сразу выделить самые важные детали, которые важны и для устройства конца 19-го века, и для суперсовременного смартфона. Это совершение вызова (набор номера) и приём вызова. По сути это то, что делает телефон телефоном, а не чем-то другим. Сейчас мы применили принцип в ООП - выделение наиболее важных характеристик и информации об объекте. Этот принцип называется абстракцией. Абстракцию в ООП можно также определить, как способ представления элементов задачи из реального мира в виде объектов в программе. Абстракция всегда связана с обобщением некоторой информации о свойствах предметов или объектов, поэтому главное - это отделить значимую информацию от незначимой в контексте решаемой задачи. При этом уровней абстракции может быть несколько. Попробуем применить принцип абстракции к нашим телефонам. Для начала выделим наиболее распространённые типы телефонов от самых первых и до наших дней. Например, их можно представить в виде диаграммы, приведенной на рисунке 1. Теперь с помощью абстракции мы можем выделить в этой иерархии объектов общую информацию: общий абстрактный тип объектов - телефон, общую характеристику телефона - год его создания, и общий интерфейс - все телефоны способны принимать и посылать вызовы. Вот как это выглядит на Java: public abstract class AbstractPhone { private int year; public AbstractPhone (int year) { this . year = year; } public abstract void call (int outputNumber) ; public abstract void ring (int inputNumber) ; } На основании этого абстрактного класса мы сможем создавать в программе новые типы телефонов с использованием других базовых принципов ООП Java, которые рассмотрим ниже.

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

С помощью абстракции мы выделяем общее для всех объектов. Однако каждая модель телефона - индивидуальна и чем-то отличается от других. Как же нам в программе провести границы и обозначить эту индивидуальность? Как сделать так, чтоб никто из пользователей случайно или преднамеренно не смог сломать наш телефон, или попытаться переделать одну модель в другую? Для мира реальных объектов ответ очевиден: нужно поместить все детали в корпус телефона. Ведь если этого не сделать и оставить все внутренности телефона и провода, соединяющие их снаружи, обязательно найдется любознательный экспериментатор, который захочет “улучшить” работу нашего телефона. Для исключения подобного вмешательства в конструкцию и работу объекта в ООП используют принцип инкапсуляции – еще один базовый принцип ООП, при котором атрибуты и поведение объекта объединяются в одном классе, внутренняя реализация объекта скрывается от пользователя, а для работы с объектом предоставляется открытый интерфейс. Задача программиста - определить, какие атрибуты и методы будут доступны для открытого доступа, а какие являются внутренней реализацией объекта и должны быть недоступны для изменений.

Инкапсуляция и управление доступом

Допустим, при производстве на тыльной стороне телефона гравируется информация о нем: год его выпуска или логотип компании производителя. Эта информация вполне конкретно характеризует данную модель - его состояние. Можно сказать, разработчик телефона позаботился о неизменности этой информации - вряд ли кому-то придет в голову удалять гравировку. В мире Java состояние будущих объектов описывается в классе с помощью полей, а их поведение – с помощью методов. Возможность же изменения состояния и поведения осуществляется с помощью модификаторов доступа к полям и методам – private, protected, public , а также default (доступ по умолчанию). Например, мы решили, что год создания, название производителя телефона и один из методов относятся к внутренней реализации класса и не подлежат изменению другими объектами в программе. С помощью кода класс можно описать так: public class SomePhone { private int year; private String company; public SomePhone (int year, String company) { this . year = year; this . company = company; } private void openConnection () { //findComutator //openNewConnection... } public void call () { openConnection () ; System. out. println ("Вызываю номер" ) ; } public void ring () { System. out. println ("Дзынь-дзынь" ) ; } } Модификатор private делает доступными поля и методы класса только внутри данного класса. Это означает, что получить доступ к private полям из вне невозможно, как и нет возможности вызвать private методы. Сокрытие доступа к методу openConnection , оставляет нам также возможность к свободному изменению внутренней реализации этого метода, так как этот метод гарантированно не используется другими объектами и не нарушит их работу. Для работы с нашим объектом мы оставляем открытыми методы call и ring с помощью модификатора public . Предоставление открытых методов для работы с объектом также является частью механизма инкапсуляции, тат как если полностью закрыть доступ к объекту – он станет бесполезным.

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

Давайте посмотрим еще раз на диаграмму телефонов. Можно заметить, что она представляет собой иерархию, в которой модель, расположенная ниже обладает всеми признаками моделей, расположенных выше по ветке, плюс своими собственными. Например, смартфон, использует сотовую сеть для связи (обладает свойствами сотового телефона), является беспроводным и переносным (обладает свойствами беспроводного телефона) и может принимать и делать вызовы (свойствами телефона). В этом случае мы можем говорить о наследовании свойств объекта. В программировании наследование заключается в использовании уже существующих классов для описания новых. Рассмотрим пример создания класса смартфон с помощью наследования. Все беспроводные телефоны работают от аккумуляторных батарей, которые имеют определенный ресурс работы в часах. Поэтому добавим это свойство в класс беспроводных телефонов: public abstract class WirelessPhone extends AbstractPhone { private int hour; public WirelessPhone (int year, int hour) { super (year) ; this . hour = hour; } } Сотовые телефоны наследуют свойства беспроводного телефона, мы также добавили в этот класс реализацию методов call и ring: public class CellPhone extends WirelessPhone { public CellPhone (int year, int hour) { super (year, hour) ; } @Override public void call (int outputNumber) { System. out. println ("Вызываю номер " + outputNumber) ; } @Override public void ring (int inputNumber) { System. out. println ("Вам звонит абонент " + inputNumber) ; } } И, наконец, класс смартфон, который в отличие от классических сотовых телефонов имеет полноценную операционную систему. В смартфон можно добавлять новые программы, поддерживаемые данной операционной системой, расширяя, таким образом, его функциональность. С помощью кода класс можно описать так: public class Smartphone extends CellPhone { private String operationSystem; public Smartphone (int year, int hour, String operationSystem) { super (year, hour) ; this . operationSystem = operationSystem; } public void install (String program) { System. out. println ("Устанавливаю " + program + "для" + operationSystem) ; } } Как видите, для описания класса Smartphone мы создали совсем немного нового кода, но получили новый класс с новой функциональностью. Использование этого принципа ООП java позволяет значительно уменьшить объем кода, а значит, и облегчить работу программисту.

Полиморфизм

Если мы посмотрим на все модели телефонов, то, несмотря на различия во внешнем облике и устройстве моделей, мы можем выделить у них некое общее поведение – все они могут принимать и совершать звонки и имеют достаточно понятный и простой набор кнопок управления. Применяя известный нам уже один из основных принципов ООП абстракцию в терминах программирования можно сказать, что объект телефон имеет один общий интерфейс. Поэтому пользователи телефонов могут вполне комфортно пользоваться различными моделями, используя одни и те же кнопки управления (механические или сенсорные), не вдаваясь в технические тонкости устройства. Так, вы постоянно пользуетесь сотовым телефоном, и без труда сможете совершить звонок с его стационарного собрата. Принцип в ООП, когда программа может использовать объекты с одинаковым интерфейсом без информации о внутреннем устройстве объекта, называется полиморфизмом . Давайте представим, что нам в программе нужно описать пользователя, который может пользоваться любыми моделями телефона, чтобы позвонить другому пользователю. Вот как можно это сделать: public class User { private String name; public User (String name) { this . name = name; } public void callAnotherUser (int number, AbstractPhone phone) { // вот он полиморфизм - использование в коде абстактного типа AbstractPhone phone! phone. call (number) ; } } } Теперь опишем различные модели телефонов. Одна из первых моделей телефонов: public class ThomasEdisonPhone extends AbstractPhone { public ThomasEdisonPhone (int year) { super (year) ; } @Override public void call (int outputNumber) { System. out. println ("Вращайте ручку" ) ; System. out. println ("Сообщите номер абонента, сэр" ) ; } @Override public void ring (int inputNumber) { System. out. println ("Телефон звонит" ) ; } } Обычный стационарный телефон: public class Phone extends AbstractPhone { public Phone (int year) { super (year) ; } @Override public void call (int outputNumber) { System. out. println ("Вызываю номер" + outputNumber) ; } @Override public void ring (int inputNumber) { System. out. println ("Телефон звонит" ) ; } } И, наконец, крутой видеотелефон: public class VideoPhone extends AbstractPhone { public VideoPhone (int year) { super (year) ; } @Override public void call (int outputNumber) { System. out. println ("Подключаю видеоканал для абонента " + outputNumber ) ; } @Override public void ring (int inputNumber) { System. out. println ("У вас входящий видеовызов..." + inputNumber) ; } } Создадим объекты в методе main() и протестируем метод callAnotherUser: AbstractPhone firstPhone = new ThomasEdisonPhone (1879 ) ; AbstractPhone phone = new Phone (1984 ) ; AbstractPhone videoPhone= new VideoPhone (2018 ) ; User user = new User ("Андрей" ) ; user. callAnotherUser (224466 , firstPhone) ; // Вращайте ручку //Сообщите номер абонента, сэр user. callAnotherUser (224466 , phone) ; //Вызываю номер 224466 user. callAnotherUser (224466 , videoPhone) ; //Подключаю видеоканал для абонента 224466 Используя вызов одного и того же метода объекта user , мы получили различные результаты. Выбор конкретной реализации метода call внутри метода callAnotherUser производился динамически на основании конкретного типа вызывающего его объекта в процессе выполнения программы. В этом и заключается основное преимущество полиморфизма – выбор реализации в процессе выполнения программы. В примерах классов телефонов, приведенных выше, мы использовали переопределение методов – прием, при котором изменяется реализация метода, определенная в базовом классе, без изменения сигнатуры метода. По сути это является заменой метода, и именно новый метод, определенный в подклассе, вызывается при выполнении программы. Обычно, при переопределении метода, используется аннотация @Override , которая подсказывает компилятору о необходимости проверить сигнатуры переопределяемого и переопределяющего методов. В итоге , чтобы стиль вашей программы соответствовал концепции ООП и принципам ООП java следуйте следующим советам:
  • выделяйте главные характеристики объекта;
  • выделяйте общие свойства и поведение и используйте наследование при создании объектов;
  • используйте абстрактные типы для описания объектов;
  • старайтесь всегда скрывать методы и поля, относящиеся к внутренней реализации класса.


Загрузка...