1

1


• Программа — это группа объектов, указывающих друг другу, что де¬лать, посредством сообщений. Чтобы обратиться с запросом к объекту, вы «посылаете ему сообщение». Более наглядно можно представить сооб¬щение как вызов метода, принадлежащего определенному объекту.
• Каждый объект имеет собственную «память», состоящую из других объектов. Иными словами, вы создаете новый объект с помощью встраи¬вания в него уже существующих объектов. Таким образом, можно сконст¬руировать сколь угодно сложную программу, скрыв общую сложность за простотой отдельных объектов.
• У каждого объекта есть тип. В других терминах, каждый объект являет¬ся экземпляром класса, где «класс» является аналогом слова «тип». Важ¬нейшее отличие классов друг от друга как раз и заключается в ответе на вопрос: «Какие сообщения можно посылать объекту?»
• Все объекты определенного типа могут получать одинаковые сообще¬ния. Как мы вскоре убедимся, это очень важное обстоятельство. Так как объект типа «круг» также является объектом типа «фигура», справедливо утверждение, что «круг» заведомо способен принимать сообщения для «фигуры». А это значит, что можно писать код для фигур и быть уверен¬ным в том, что он подойдет для всего, что попадает под понятие фигуры. Взаимозаменяемость представляет одно из самых мощных понятий ООП.
Буч предложил еще более лаконичное описание объекта:
Объект обладает состоянием, поведением и индивидуальностью.
Суть сказанного в том, что объект может иметь в своем распоряжении внут¬ренние данные (которые и есть состояние объекта), методы (которые опреде¬ляют поведение), и каждый объект можно уникальным образом отличить от любого другого объекта — говоря более конкретно, каждый объект обладает уникальным адресом в памяти .
Объект имеет интерфейс
Вероятно, Аристотель был первым, кто внимательно изучил понятие типа\ он говорил о «классе рыб и классе птиц». Концепция, что все объекты, будучи уникальными, в то же время являются частью класса объектов со сходными ха¬рактеристиками и поведением, была использована в первом объектно-ориенти¬рованном языке Simula-67, с введением фундаментального ключевого слова class, которое вводило новый тип в программу.
Язык Simula, как подразумевает его имя, был создан для развития и модели¬рования ситуаций, подобных классической задаче «банковский кассир». У вас есть группы кассиров, клиентов, счетов, платежей и денежных единиц — много «объектов». Объекты, идентичные во всем, кроме внутреннего состояния во время работы программы, группируются в «классы объектов». Отсюда и пришло ключевое слово class. Создание абстрактных типов данных есть фун¬даментальное понятие во всем объектно-ориентированном программировании. Абстрактные типы данных действуют почти так же, как и встроенные типы: вы можете создавать переменные типов (называемые объектами или экземплярами в терминах ООП) и манипулировать ими (что называется посылкой сообщений или запросом; вы производите запрос, и объект решает, что с ним делать). Чле¬ны (элементы) каждого класса обладают сходством: у каждого счета имеется баланс, каждый кассир принимает депозиты, и т. п. В то же время все члены от¬личаются внутренним состоянием: у каждого счета баланс индивидуален, каж¬дый кассир имеет человеческое имя. Поэтому все кассиры, заказчики, счета, пе¬реводы и прочее могут быть представлены уникальными сущностями внутри компьютерной программы. Это и есть суть объекта, и каждый объект принад¬лежит к определенному классу, который определяет его характеристики и по¬ведение.
Таким образом, хотя мы реально создаем в объектных языках новые типы данных, фактически все эти языки используют ключевое слово «класс». Когда видите слово «тип», думайте «класс», и наоборот .
Поскольку класс определяет набор объектов с идентичными характеристи¬ками (элементы данных) и поведением (функциональность), класс на самом деле является типом данных, потому что, например, число с плавающей запя¬той тоже имеет ряд характеристик и особенности поведения. Разница состоит в том, что программист определяет класс для представления некоторого аспек¬та задачи, вместо использования уже существующего типа, представляющего единицу хранения данных в машине. Вы расширяете язык программирования, добавляя новые типы данных, соответствующие вашим потребностям. Система программирования благосклонна к новым классам и уделяет им точно такое же внимание, как и встроенным типам.
Объектно-ориентированный подход не ограничен построением моделей. Со¬гласитесь вы или нет, что любая программа — модель разрабатываемой вами
Объект имеет интерфейс 21
системы, независимо от вашего мнения ООП-технологии упрощают решение широкого круга задач.
После определения нового класса вы можете создать любое количество объ¬ектов этого класса, а затем манипулировать ими так, как будто они представля¬ют собой элементы решаемой задачи. На самом деле одной из основных трудно¬стей в ООП является установление однозначного соответствия между объектами пространства задачи и объектами пространства решения.

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

Имя типа
Интерфейс
Light It = new LightO,
It on().
Интерфейс определяет, какие запросы вы вправе делать к определенному объекту. Однако где-то должен существовать и код, выполняющий запросы. Этот код, наряду со скрытыми данными, составляет реализацию. С точки зре¬ния процедурного программирования происходящее не так уж сложно. Тип со¬держит метод для каждого возможного запроса, и при получении определенно¬го запроса вызывается нужный метод. Процесс обычно объединяется в одно целое: и «отправка сообщения» (передача запроса) объекту, и его обработка объектом (выполнение кода).
В данном примере существует тип (класс) с именем Light (лампа), конкрет¬ный объект типа Light с именем It, и класс поддерживает различные запросы к объекту Light: выключить лампочку, включить, сделать ярче или притушить. Вы создаете объект Light, определяя «ссылку» на него (It) и вызывая оператор new для создания нового экземпляра этого типа. Чтобы послать сообщение объ¬екту, следует указать имя объекта и связать его с нужным запросом знаком точки. С точки зрения пользователя заранее определенного класса, этого вполне дос¬таточно для того, чтобы оперировать его объектами.
Диаграмма, показанная выше, следует формату UML (Unified Modeling Lan¬guage). Каждый класс представлен прямоугольником, все описываемые поля данных помещены в средней его части, а методы (функции объекта, которому вы посылаете сообщения) перечисляются в нижней части прямоугольника.
Часто на диаграммах UML показываются только имя класса и открытые методы, а средняя часть отсутствует. Если же вас интересует только имя класса, то мо¬жете пропустить и нижнюю часть.
Объект предоставляет услуги
В тот момент, когда вы пытаетесь разработать или понять структуру программы, часто бывает полезно представить объекты в качестве «поставщиков услуг». Ваша программа оказывает услуги пользователю, и делает она это посредством услуг, предоставляемых другими объектами. Ваша цель — произвести (а еще лучше отыскать в библиотеках классов) тот набор объектов, который будет оп¬тимальным для решения вашей задачи.
Для начала спросите себя: «если бы я мог по волшебству вынимать объекты из шляпы, какие бы из них смогли решить мою задачу прямо сейчас?» Предпо¬ложим, что вы разрабатываете бухгалтерскую программу. Можно представить себе набор объектов, предоставляющих стандартные окна для ввода бухгалтер¬ской информации, еще один набор объектов, выполняющих бухгалтерские рас¬четы, объект, ведающий распечаткой чеков и счетов на всевозможных принте¬рах. Возможно, некоторые из таких объектов уже существуют, а для других объектов стоит выяснить, как они могли бы выглядеть. Какие услуги могли бы предоставлять те объекты, и какие объекты понадобились бы им для выполне¬ния своей работы? Если вы будете продолжать в том же духе, то рано или позд¬но скажете: «Этот объект достаточно прост, так что можно сесть и записать его», или «Наверняка такой объект уже существует». Это разумный способ рас¬пределить решение задачи на отдельные объекты.
Представление объекта в качестве поставщика услуг обладает дополнитель¬ным преимуществом: оно помогает улучшить связуемостъ (cohesiveness) объекта. Хорошая связуемостъ — важнейшее качество программного продукта: она озна¬чает, что различные аспекты программного компонента (такого как объект, хотя сказанное также может относиться к методу или к библиотеке объектов) хорошо «стыкуются» друг с другом. Одной из типичных ошибок, допускаемых при проектировании объекта, является перенасыщение его большим количест¬вом свойств и возможностей. Например, при разработке модуля, ведающего распечаткой чеков, вы можете захотеть, чтобы он «знал» все о форматировании и печати. Если подумать, скорее всего, вы придете к выводу, что для одного объекта этого слишком много, и перейдете к трем или более объектам. Один объект будет представлять собой каталог всех возможных форм чеков, и его можно будет запросить о том, как следует распечатать чек. Другой объект или набор объектов станут отвечать за обобщенный интерфейс печати, «знающий» все о различных типах принтеров (но ничего не «понимающий» в бухгалте¬рии — такой объект лучше купить, чем разрабатывать самому). Наконец, тре¬тий объект просто будет пользоваться услугами описанных объектов, для того чтобы выполнить задачу. Таким образом, каждый объект представляет собой связанный набор предлагаемых им услуг. В хорошо спланированном объект¬но-ориентированном проекте каждый объект хорошо справляется с одной конкретной задачей, не пытаясь при этом сделать больше нужного. Как было показано, это не только позволяет определить, какие объекты стоит приобрести (объект с интерфейсом печати), но также дает возможность получить в итоге объект, который затем можно использовать где-то еще (каталог чеков).
Представление объектов в качестве поставщиков услуг значительно упроща¬ет задачу. Оно полезно не только во время разработки, но и когда кто-либо по¬пытается понять ваш код или повторно использовать объект — тогда он сможет адекватно оценить объект по уровню предоставляемого сервиса, и это значи¬тельно упростит интеграцию последнего в другой проект.
Скрытая реализация
Программистов полезно разбить на создателей классов (те, кто создает новые типы данных) и программистов-клиентов (потребители классов, использующие типы данных в своих приложениях). Цель вторых — собрать как можно больше классов, чтобы заниматься быстрой разработкой программ. Цель создателя класса — построить класс, открывающий только то, что необходимо программи¬сту-клиенту, и прячущий все остальное. Почему? Программист-клиент не смо¬жет получить доступ к скрытым частям, а значит, создатель классов оставляет за собой возможность произвольно их изменять, не опасаясь, что это кому-то повредит. «Потаенная» часть обычно и самая «хрупкая» часть объекта, которую легко может испортить неосторожный или несведущий программист-клиент, поэтому сокрытие реализации сокращает количество ошибок в программах.