Введение в объектно-ориентированное программирование

From AsIsWiki
Jump to: navigation, search
Форум

Назад | Оглавление | Дальше


Contents

Введение в объектно-ориентированное программирование

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

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

Структурное программирование заключается в разработке набора процедур (алгоритмов) для решения поставленной задачи. Определив процедуры, программист должен задать подходящий способ хранения данных. "Алгоритмы + Структуры данных = Программы" назвал свою книгу Niklaus Wirth (создатель языка Pascal). В названии книги алгоритмы стоят на первом месте, а структура данных - на втором. Это отражает образ мышления программистов того времени. Во-первых, они решали, как манипулировать данными; затем решали, какую структуру применить для организации этих данных. Объектный подход изменил ситуацию, поместив на первое место данные и лишь на второе - алгоритмы.

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

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


Основные термины ООП

Class - это шаблон, или проект, по которому будет сделан объект. Класс можно сравнить с формой для выпечки печенья. Объект - это само печенье. Конструирование объекта на основе некоторого класса называется созданием экземпляра (instance) этого класса.

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

Encapsulation (сокрытие данных) - ключевое понятие при работе с объектами. Формально инкапсуляцией считается объединение данных и операций над ними в одном пакете и сокрытие данных от других объектов. Данные в объекте называются instance fields (поля экземпляра), а функции и процедуры, выполняющие операции над данными - methods (методы). Конкретный объект (экземпляр класса) имеет определенные значения полей. Множество этих значений называется текущим состоянием (state) объекта. Применение любого метода к некоторому объекту может изменить его состояние.

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

Класс можно сконструировать на основе других классов. Вновь созданный класс расширяет класс, на основе которого он создан. Java создан на основе глобального суперкласса Object. Все остальные объекты расширяют его.

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


Объекты

В ООП определены следующие ключевые свойства объектов:

  • Behavior (поведение) объекта - что с ним можно делать и какие методы к нему можно применить.
  • State (состояние) объекта - как он реагирует на применение методов.
  • Identity (сущность) объекта - чем он отличается от других объектов с аналогичным поведением и состоянием.

Все экземпляры одного класса ведут себя одинаково. Поведение объекта определяется его методами.

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

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

Состояние объекта может влиять на его поведение: если заказ "выполнен" или "оплачен", то объект может запретить добавление или удаление товарных изделий; и наоборот, для "пустых" заказов объект может запретить их выполнение.

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

Класс - это существительное, а метод - глагол:

Noun Class Object Method
Изделие Item Item
Заказ Order Order add(Item)
Адрес доставки Address Address
Оплата Payment Payment
Счет Account Account

Глаголы:

  • изделия заказываются
  • заказы выполняются или отменяются
  • оплата заказа осуществляется

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


Отношения между классами

Между классами существуют три отношения:

  • Dependence ("uses-a") - зависимость ("использует")
  • Aggregation ("has-s") - агрегирование ("содержит")
  • Inheritance ("is-a") - наследование ("является")

Отношение dependance наиболее очевидное. Например, в системе заказов используются классы Order и Account. Объекты Order должны иметь доступ к объектам Account, для проверки кредитоспособности заказчика. Поэтому, класс Order использует класс Account. Объект Item безразличен к состоянию счета заказчика. Следовательно, класс Item не зависит от класса Account. Резюме: класс зависит от другого класса, если его методы выполняют какие-либо действия с экземплярами этого класса.

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

Aggregation означает, что класс А содержит экземпляры класса В. Например, объект Order может содержать экземпляры класса Item.

Inheritance выражает отношения между классом и его наследником. Например, класс RushOrder является наследником класса Order. Класс RushOrder имеет специальные методы для обработки приоритетов и вычисления стоимости доставки, в то время как другие его методы, например для заказа товара и выписывания счета, унаследованы от класса Order.

UML (Unified Modeling Language) - язык описания диаграмм классов (class diagrams):

Uml.png

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

Коммерческие uml-системы:

Альтернативные uml:


Различия между ООП и процедурным программированием

В процедурном программировании сначала формулируется задача, а затем выполняются следующие действия:

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

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

Для решения простых задач процедурный метод вполне оправдан. Но в больших проектах ООП имеет существенные преимущества. Классы предоставляют удобный механизм кластеризации методов. Для реализации простого web-браузера может потребоваться около 2000 функций, или 100 классов, содержащих в среднем по 20 методов.

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

Proc oop.png

Принцип формирования объектов не очень отличается от принципа модульности - modularization. Можно писать программы, разбивая их на модули, которые обмениваются информацией с помощью вызова процедур, не прибегая к совместному использованию данных - sharing data. Если следовать этому принципу, можно обеспечить инкапсуляцию данных. Но любое отклонение от описанного подхода открывает доступ к данным из другого модуля - в этом случае принцип инкапсуляции нарушается.

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

Для более глубокого понимания ООП и UML можно обратиться к книге:
Grady Booch, Ivar Jacobson, James Rumbaugh "Unified Modelling Language User Guide" (Addison-Wesley, 1999).



Форум

Назад | Оглавление | Дальше

Personal tools
Namespaces

Variants
Actions
Navigation
Tools