Объектно-ориентированный подход. Объектно_ориентированный_подход. Объектно ориентированный подход Мэтт Вайсфельд 5е международное издание ббк 32. 973. 2018
Скачать 5.43 Mb.
|
ЧТО ВИДЯТ ПОЛЬЗОВАТЕЛИ __________________________________________________________ Когда.в.этой.главе.речь.идет.о.пользователях,.под.ними.подразумеваются.проекти- ровщики.и.разработчики,.а.не.обязательно.конечные.пользователи..Таким.образом,. когда.мы.говорим.об.интерфейсах.в.этом.контексте,.речь.идет.об.интерфейсах. классов,.а.не.о.графических.интерфейсах.пользователей. Должным образом сконструированные классы состоят из двух частей — интер- фейса и реализации. Интерфейс Услуги, предоставляемые конечным пользователям, образуют интерфейс. В наи- более благоприятном случае конечным пользователям предоставляются толь- ко те услуги, которые им необходимы. Разумеется, то, какие услуги требуются определенному конечному пользователю, может оказаться спорным вопросом. Если вы поместите десять человек в одну комнату и попросите каждого из них спроектировать что-то независимо от других, то получите десять абсолютно разных результатов проектирования — и в этом не будет ничего плохого. Одна- ко, как правило, интерфейс класса должен содержать то, что нужно знать поль- зователям. Если говорить о примере с тостером, то им необходимо знать только то, как подключить прибор к интерфейсу (которым в данном случае является электрическая розетка) и как эксплуатировать его. ОПРЕДЕЛЕНИЕ ПОЛЬЗОВАТЕЛЕЙ ____________________________________________________ Пожалуй,.наиболее.важный.момент.при.проектировании.класса.—.определение.его. аудитории,.или.пользователей. 57 Разница.между.интерфейсом.и.реализацией. . Реализация Детали реализации скрыты от пользователей. Один из аспектов, касающихся реализации, которые нужно помнить, заключается в следующем: изменения в реализации не должны требовать внесения изменений в пользовательский код. В какой-то мере это может показаться сложным, однако в выполнении этого условия заключается суть соответствующей задачи проектирования. КАЧЕСТВО ИНТЕРФЕЙСА _____________________________________________________________ Если.интерфейс.спроектирован.правильно,.то.изменения.в.реализации.не.должны. требовать.изменений.пользовательского.кода. Помните, что интерфейс включает синтаксис для вызова методов и возврата значений. Если интерфейс не претерпит изменений, то пользователям будет все равно, изменится ли реализация. Важно лишь то, чтобы программисты смогли использовать аналогичный синтаксис и извлечь аналогичное значение. Мы сталкиваемся с этим постоянно, когда пользуемся сотовыми телефонами. Интерфейс, применяемый для звонка, прост: мы либо набираем номер, либо выбираем тот, что имеется в адресной книге. Кроме того, если оператор связи обновит программное обеспечение, то это не изменит способ, которым вы со- вершаете звонки. Интерфейс останется прежним независимо от того, как из- менится реализация. Однако я могу представить себе ситуацию, что оператор связи изменил интерфейс: это произойдет, если изменится код города. При изменении основного интерфейса, как и кода города, пользователям придется действовать уже по-другому. Бизнес старается сводить к минимуму изменения такого рода, поскольку некоторым клиентам они не понравятся или, возможно, эти люди не захотят мириться с трудностями. Напомню пример с тостером: хотя интерфейсом всегда является электрическая розетка, реализация может измениться с работающей на угле электростанции на атомную, никак не повлияв на тостер. Здесь следует сделать одну важную оговорку: работающая на угле или атомная электростанция тоже должна соот- ветствовать спецификации интерфейса. Если работающая на угле электростан- ция обеспечивает питание переменного тока, а атомная — питание постоянного тока, то возникнет проблема. Основной момент здесь заключается в том, что и потребляющее электроэнергию устройство, и реализация должны соответ- ствовать спецификации интерфейса. Пример интерфейса/реализации Создадим простой (пусть и не очень функциональный) класс DataBaseReader Мы напишем Java-код, который будет извлекать записи из базы данных. Как уже говорилось ранее, знание своих конечных пользователей всегда является Глава.2..Как.мыслить.объектно 58 наиболее важным аспектом при проектировании любого рода. Вам следует про- вести анализ ситуации и побеседовать с конечными пользователями, а затем составить список требований к проекту. Далее приведены требования, которые нам придется учитывать при создании класса DataBaseReader . У нас должна быть возможность: открывать соединение с базой данных; закрывать соединение с базой данных; устанавливать указатель над первой записью в базе данных; устанавливать указатель над последней записью в базе данных; узнавать количество записей в базе данных; определять, есть ли еще записи в базе данных (если мы достигнем конца); устанавливать указатель над определенной записью путем обеспечения ключа; извлекать ту или иную запись путем обеспечения ключа; извлекать следующую запись исходя из позиции курсора. Учитывая все эти требования, можно предпринять первую попытку спроекти- ровать класс DataBaseReader , написав возможные интерфейсы для наших ко- нечных пользователей. В данном случае класс DataBaseReader предназначается для программистов, которым требуется использовать ту или иную базу данных. Таким образом, интерфейс, в сущности, будет представлять собой интерфейс программирования приложений (API — Application Programming Interface), который станут ис- пользовать программисты. Соответствующие методы в действительности будут обертками, в которых окажется заключена функциональность, обеспечиваемая системой баз данных. Зачем нам все это нужно? Мы намного подробнее иссле- дуем этот вопрос позднее в текущей главе, а короткий ответ звучит так: нам необходимо сконфигурировать кое-какую функциональность, связанную с ба- зой данных. Например, нам может потребоваться обработать объекты для того, чтобы мы смогли записать их в реляционную базу данных. Создание соответ- ствующего промежуточного программного обеспечения — непростая задача, поскольку предполагает проектирование и написание кода, однако представля- ет собой реальный пример обертывания функциональности. Более важно то, что нам может потребоваться изменить само ядро базы данных, но при этом не придется вносить изменения в код. На рис. 2.2 показана диаграмма класса, представляющая возможный интерфейс для класса DataBaseReader 59 Разница.между.интерфейсом.и.реализацией. . Рис. 2.2. UML-диаграмма.класса.DataBaseReader Обратите внимание, что методы в этом классе открытые (помните, что рядом с именем методов, являющихся открытыми интерфейсами, присутствует знак плюс). Следует также отметить, что представлен только интерфейс, а реализация не показана. Уделите минуту на то, чтобы выяснить, отвечает ли в целом эта диаграмма класса требованиям к проекту, намеченным ранее. Если впоследствии вы обнаружите, что эта диаграмма отвечает не всем требованиям, то в этом не будет ничего плохого; помните, что объектно-ориентированное проектирова- ние — итеративный процесс, поэтому вам необязательно стараться сделать все именно с первой попытки. ОТКРЫТЫЙ ИНТЕРФЕЙС ______________________________________________________________ Помните,.что.если.метод.является.открытым,.то.прикладные.программисты.смогут. получить.к.нему.доступ,.и,.таким.образом,.он.будет.считаться.частью.интерфейса. класса..Не.путайте.термин.«интерфейс».с.ключевым.словом. interface,.используемым. в.Java.и..NET..На.эту.тему.мы.поговорим.в.последующих.главах. Для каждого из приведенных ранее требований нам необходим метод, обеспе- чивающий желаемую функциональность. Теперь нам нужно задать несколько вопросов. Чтобы эффективно использовать этот класс, нужно ли вам как программисту еще что-нибудь знать о нем? Нужно ли знать о том, как внутренний код базы данных открывает ее? Требуется ли знать о том, как внутренний код базы данных физически вы- бирает определенную запись? Нужно ли знать о том, как внутренний код базы определяет то, остались ли еще записи? Глава.2..Как.мыслить.объектно 60 Ответом на все эти вопросы будет громогласное «нет»! Вам не нужно знать что-либо из этой информации. Важно лишь получить соответствующие воз- вращаемые значения, а также то, что операции выполняются корректно. Кро- ме того, прикладные программисты, скорее всего, «будут на отдалении» как минимум еще одного абстрактного уровня от реализации. Приложение вос- пользуется вашими классами для открытия базы данных, что, в свою очередь, приведет к вызову соответствующего API-интерфейса для доступа к этой базе данных. МИНИМАЛЬНЫЙ ИНТЕРФЕЙС ________________________________________________________ Один.из.способов,.пусть.даже.экстремальных,.определить.минимальный.интерфейс. заключается.в.том,.чтобы.изначально.не.предоставлять.пользователю.никаких.от- крытых.интерфейсов..Разумеется,.соответствующий.класс.будет.бесполезным;. однако.это.заставит.пользователя.вернуться.к.вам.и.сказать:.«Эй,.мне.нужна.эта. функциональность»..Тогда.вы.сможете.начать.переговоры..Таким.образом,.у.вас.есть. возможность.добавлять.интерфейсы,.только.когда.они.запрашиваются..Никогда.не. предполагайте,.что.определенному.пользователю.что-то.требуется. Может показаться, что создание оберток — это перебор, однако их написание несет в себе множество преимуществ. Например, на рынке сегодня присутст вует большое количество промежуточных продуктов. Рассмотрим проблему отображения объектов в реляционную базу данных. Некоторые объектно-ори- ентированные базы данных могут идеально подходить для объектно-ориенти- рованных приложений. Однако есть маленькая проблема: у большинства ком- паний имеется множество данных в унаследованных системах управления реляционными базами. Как та или иная компания может использовать объ- ектно-ориентированные технологии и быть передовой, сохраняя при этом свою информацию в реляционной базе данных? Во-первых, вы можете преобразовать все свои унаследованные реляционные базы данных в совершенно новые объектно-ориентированные базы данных. Однако любому, кто испытывает острую боль от преобразования каких-либо данных, известно, что этого следует избегать любой ценой. Хотя на такие пре- образования может уйти много времени и сил, зачастую они вообще не приводят к требуемым результатам. Во-вторых, вы можете воспользоваться промежуточным продуктом для того, чтобы без проблем отобразить объекты, содержащиеся в коде вашего приложе- ния, в реляционную модель. Это более верное решение. Некоторые могут ут- верждать, что объектно-ориентированные базы данных намного эффективнее подходят для обеспечения постоянства объектов, чем реляционные базы данных. Фактически многие системы разработки без проблем обеспечивают такую функциональность. 61 Разница.между.интерфейсом.и.реализацией. . ПОСТОЯНСТВО ОБЪЕКТОВ ____________________________________________________________ Постоянство объектов.относится.к.концепции.сохранения.состояния.того.или. иного.объекта.для.того,.чтобы.его.можно.было.восстановить.и.использовать.позднее.. Объект,.не.являющийся.постоянным,.по.сути,.«умирает»,.когда.оказывается.вне.об- ласти.видимости..Состояние.объекта,.к.примеру,.можно.сохранить.в.базе.данных. В современной бизнес-среде отображение из реляционных баз данных в объ- ектно-ориентированные — отличное решение. Многие компании интегриро- вали эти технологии. Компании используют распространенный подход, при котором внешний интерфейс сайта вместе с данными располагается на мейн- фрейме. Если вы создаете полностью объектно-ориентированную систему, то практич- ным (и более производительным) вариантом может оказаться объектно-ориен- тированная база данных. Вместе с тем объектно-ориентированные базы данных даже близко нельзя назвать такими же распространенными, как объектно-ори- ентированные языки программирования. АВТОНОМНОЕ ПРИЛОЖЕНИЕ _________________________________________________________ Даже.при.создании.нового.объектно-ориентированного.приложения.с.нуля.может. оказаться.нелегко.избежать.унаследованных.данных..Даже.новое.созданное.объектно- ориентированное.приложение,.скорее.всего,.не.будет.автономным,.и.ему,.возможно,. потребуется.обмениваться.информацией,.располагающейся.в.реляционных.базах. данных.(или,.собственно.говоря,.на.любом.другом.устройстве.накопления.данных). Вернемся к примеру с базой данных. На рис. 2.2 показан открытый интерфейс, который касается соответствующего класса, и ничего больше. Когда этот класс будет готов, в нем, вероятно, окажется больше методов, при этом он, несомнен- но, будет включать атрибуты. Однако вам как программисту, который будет использовать этот класс, не потребуется что-либо знать о его закрытых методах и атрибутах. Вам, безусловно, не нужно знать, как выглядит код внутри его от- крытых методов. Вам просто понадобится быть в курсе, как взаимодействовать с интерфейсами. Как выглядел бы код этого открытого интерфейса (допустим, мы начнем с при- мера базы данных Oracle)? Взглянем на метод open() : public void open(String Name){ /* Некая специфичная для приложения обработка */ /* Вызов API-интерфейса Oracle для открытия базы данных */ /* Еще некая специфичная для приложения обработка */ }; Глава.2..Как.мыслить.объектно 62 В данной ситуации вы, выступая в роли программиста, понимаете, что методу open в качестве параметра требуется String . Объект Name , который представля- ет файл базы данных, передается, однако пояснение того, как Name отображает- ся в определенную базу данных в случае с этим примером, не является важным. Это все, что нам нужно знать. А теперь переходим к самому увлекательному — что в действительности делает интерфейсы такими замечательными! Чтобы досадить нашим пользователям, изменим реализацию базы данных. Представим, что вчера вечером мы преобразовали всю информацию из базы Oracle в информацию базы SQL Anywhere (при этом нам пришлось вынести острую боль). Эта операция заняла у нас несколько часов, но мы справились с ней. Теперь код выглядит так: public void open(String Name){ /* Некая специфичная для приложения обработка */ /* Вызов API-интерфейса SQL Anywhere для открытия базы данных */ /* Еще некая специфичная для приложения обработка */ }; К нашему великому разочарованию, этим утром не поступило ни одной жалобы от пользователей. Причина заключается в том, что, хотя реализация изменилась, интерфейс не претерпел изменений! Что касается пользователей, то совершае- мые ими вызовы остались такими же, как и раньше. Изменение кода реализации могло бы потребовать довольно много сил (а модуль с однострочным измене- нием кода пришлось бы перестраивать), однако не понадобилось бы изменять ни одной строки кода приложения, который задействует класс DataBaseReader ПЕРЕКОМПИЛЯЦИЯ КОДА ____________________________________________________________ Динамически.загружаемые.классы.загружаются.во.время.выполнения,.а.не.являют- ся.статически.связанными.с.исполняемым.файлом..При.использовании.динамически. загружаемых.классов,.как,.например,.в.случае.с.Java.и..NET,.не.придется.переком- пилировать.ни.один.из.пользовательских.классов..Однако.в.языках.программиро- вания.со.статическим.связыванием,.например.C++,.для.добавления.нового.класса. потребуется.связь. Разделяя интерфейс пользователя и реализацию, мы сможем избежать головной боли в будущем. На рис. 2.3 реализации баз данных прозрачны для конечных пользователей, видящих только интерфейс. 63 Использование.абстрактного.мышления.при.проектировании.классов. . Рис. 2.3. Интерфейс Использование абстрактного мышления при проектировании классов Одно из основных преимуществ объектно-ориентированного программирования состоит в том, что классы можно использовать повторно. Пригодные для по- вторного применения классы обычно располагают интерфейсами, которые больше абстрактны, нежели конкретны. Конкретные интерфейсы склонны быть весьма специфичными, в то время как абстрактные являются более общими. Однако не всегда можно утверждать, что очень абстрактный интерфейс более полезен, чем очень конкретный, пусть это часто и является верным. Можно создать очень полезный конкретный класс, который окажется вообще непригодным для повторного использования. Это случается постоянно, но в не- которых ситуациях в этом нет ничего плохого. Однако мы сейчас ведем речь о проектировании и хотим воспользоваться преимуществами того, что пред- лагает нам объектно-ориентированный подход. Поэтому наша цель заключает- ся в том, чтобы спроектировать абстрактные, в высокой степени пригодные для повторного применения классы, а для этого мы спроектируем очень абстрактные интерфейсы пользователя. Глава.2..Как.мыслить.объектно 64 Для того чтобы вы смогли наглядно увидеть разницу между абстрактным и кон- кретным интерфейсами, создадим такой объект, как такси. Намного полезнее располагать интерфейсом, например «отвезите меня в аэропорт», чем отдель- ными интерфейсами, например «поверните направо», «поверните налево», «поехали», «остановитесь» и т. д., поскольку, как показано на рис. 2.4, клиенту нужно лишь одно — добраться до аэропорта. Рис. 2.4. Абстрактный.интерфейс Когда вы выйдете из отеля, в котором жили, бросите свои чемоданы на заднее сиденье такси и сядете в машину, таксист повернется к вам и спросит: «Куда вас отвезти?» Вы ответите: «Пожалуйста, отвезите меня в аэропорт» (при этом, естественно, предполагается, что в городе есть только один крупный аэропорт. Например, в Чикаго вы сказали бы: «Пожалуйста, отвезите меня в аэропорт “Мидуэй”» или «Пожалуйста, отвезите меня в аэропорт “О’Хара”»). Вы сами, возможно, не будете знать, как добраться до аэропорта, но даже если и будете, то вам не придется рассказывать таксисту о том, когда и в какую сторону нужно повернуть, как показано на рис. 2.5. Каким в действительности путем таксист поедет, для вас как пассажира не будет иметь значения (однако плата за проезд в какой-то момент может стать предметом разногласий, если таксист решит сжульничать и повезти вас в аэропорт длинным путем). В чем же проявляется связь между абстрактностью и повторным использова- нием? Задайте себе вопрос насчет того, какой из этих двух сценариев более пригоден для повторного использования — абстрактный или не такой абстракт- ный? Проще говоря, какая фраза более подходит для того, чтобы использовать |