Объектно-ориентированный подход. Объектно_ориентированный_подход. Объектно ориентированный подход Мэтт Вайсфельд 5е международное издание ббк 32. 973. 2018
Скачать 5.43 Mb.
|
Ссылки Гради Буч, Роберт А. Максимчук, Майкл У. Энгл, Бобби Дж. Янг, Джим Коналлен и Келли А. Хьюстон, «Объектно-ориентированный анализ и про- ектирование с примерами приложений» (Object-Oriented Analysis and Design with Applications). — 3-е изд. — Бостон, штат Массачусетс: Addison-Wesley, 2007. 161 Ссылки. . Петер Коуд и Марк Мейфилд, «Проектирование на Java» (Java Design). — Аппер Сэддл Ривер, штат Нью-Джерси: Prentice-Hall, 1997. Стивен Гилберт и Билл Маккарти, «Объектно-ориентированное проектиро- вание на Java» (Object-Oriented Design in Java). — Беркли, штат Калифорния: The Waite Group Press (Pearson Education), 1998. Скотт Майерс, «Эффективное использование C++» (Effective C++). — 3-е изд. — Бостон, штат Массачусетс: Addison-Wesley Professional, 2005. Глава 8 ФРЕЙМВОРКИ И ПОВТОРНОЕ ИСПОЛЬЗОВАНИЕ: ПРОЕКТИРОВАНИЕ С ПРИМЕНЕНИЕМ ИНТЕРФЕЙСОВ И АБСТРАКТНЫХ КЛАССОВ В главе 7 вы узнали, что наследование и композиция играют главные роли в про- ектировании объектно-ориентированных систем. В этой главе приведены более подробные сведения о концепциях интерфейсов в стиле Java, протоколов и аб- страктных классов. Интерфейсы, протоколы и абстрактные классы — это мощные механизмы по- вторного использования кода, обеспечивающие фундамент для того, что я на- зываю концепцией контрактов. В этой главе мы рассмотрим такие темы, как повторное использование кода, фреймворки, контракты, интерфейсы, протоко- лы и абстрактные классы (если не указано иное, я буду употреблять термин «интерфейс» в том числе и для обозначения протоколов). В конце главы мы рассмотрим пример того, как все эти концепции могут быть применены в реаль- ной ситуации. Код: использовать повторно или нет? Программисты решают вопрос повторного использования кода с момента на- писания своей первой строки кода. Во многих парадигмах разработки программ- ного обеспечения повторное использование кода подчеркивается как основная часть процесса. С тех пор, когда программное обеспечение только начало по- являться, концепция повторного использования кода переосмысливалась не- сколько раз. Объектно-ориентированная парадигма ничем не отличается в этом плане. Одно из основных преимуществ, расхваливаемых сторонниками объ- ектно-ориентированного подхода, заключается в том, что если вы надлежащим 163 Что.такое.фреймворк?. . образом напишете код изначально, то сможете использовать его повторно сколько вашей душе угодно. Однако это правда лишь отчасти. Как и в случае со всеми подходами к проек- тированию, полезность кода, а также его пригодность к повторному использо- ванию зависят от того, насколько хорошо он был спроектирован и реализован. Объектно-ориентированное проектирование «не владеет патентом» на повтор- ное использование кода. Нет ничего, что мешает кому-либо написать очень надежный и пригодный для повторного использования код на том или ином языке программирования, который не является объектно-ориентированным. Безусловно, несметное количество программ и функций, написанных на струк- турных языках вроде COBOL, C и традиционного VB, имеют высокое качество и вполне пригодны для повторного использования. Таким образом, ясно, что приведенная далее объектно-ориентированная па- радигма является не единственным способом разработки кода, пригодного для повторного использования. Однако объектно-ориентированный подход преду сматривает несколько механизмов, облегчающих разработку такого кода. Один из способов создания пригодного для повторного использования кода заключается в создании фреймворков. В этой главе мы сосредоточимся на использовании интерфейсов и абстрактных классов для создания фрейм- ворков и способствования разработке кода, пригодного для повторного ис- пользования. Что такое фреймворк? Рука об руку с концепцией повторного использования кода идет концепция стандартизации, которую иногда называют концепцией «включил и работай» (plug and play). Идея фреймворка «вращается» вокруг принципа «включил и работай», а также принципа повторного использования. Один из классиче- ских примеров фреймворка — настольное приложение. Возьмем в качестве примера приложение из офисного пакета. В редакторе документов имеется лента, включающая разные вкладки. Эти вкладки подобны тем, что есть в пре- зентационном пакете и программном обеспечении для работы с электронными таблицами. Фактически первые два элемента меню ( Главная , Вставка ) одина- ковы во всех трех программах. Пункты меню схожи, кроме того, многие из подменю тоже подобны ( Создать , Открыть , Сохранить и т. д.). Под лентой на- ходится область документа — будь то документ, презентация или электронная таблица. Общий фреймворк облегчает освоение различных приложений, со- держащихся в офисном пакете. В то же время он облегчает жизнь разработчи- кам, позволяя по максимуму повторно использовать код, а также части того, что было спроектировано ранее. То, что все эти строки меню выглядят схожими, явно не случайно. Фактически при проектировании в большинстве интегрированных сред разработки на кон- Глава.8..Фреймворки.и.повторное.использование 164 кретной платформе, например Microsoft Windows или Linux, вы получаете определенные вещи без необходимости самим создавать их. При создании окна в среде Windows у вас автоматически появятся такие элементы, как строка ос- новного заголовка и кнопка закрытия файлов, находящаяся в правом верхнем углу. Действия тоже стандартизированы: когда вы дважды щелкаете кнопкой мыши на строке основного заголовка, окно всегда сворачивается/разворачива- ется. Когда вы нажимаете кнопку закрытия в правом верхнем углу, выполнение приложения всегда завершается. Все это является частью фреймворка. На рис. 8.1 приведен скриншот приложения Microsoft Word. Обратите внимание на строки меню, панели инструментов и прочие элементы, которые являются частью фреймворка. Рис. 8.1. Фреймворк.для.обработки.текста Фреймворк для обработки текста, как правило, позволяет выполнять такие операции, как создание, открытие и сохранение документов, удаление, копиро- вание и вставка текста, поиск в документах и т. д. Для того чтобы можно было воспользоваться этим фреймворком, разработчик должен прибегнуть к предо- пределенному интерфейсу для создания приложения. Этот интерфейс соот- ветствует стандартному фреймворку, у которого имеется два очевидных преиму- щества. Во-первых, как мы видели ранее, внешний вид является единообразным, и конечным пользователям не придется осваивать новый фреймворк. Во-вторых, разработчик сможет воспользоваться преимуществами кода, уже написанного и протестированного (а то, что тестирование кода уже было проведено, являет- ся огромным преимуществом). Зачем писать код для создания совершенно нового диалогового окна Открыть , если он уже есть и был тщательно протести- рован? В сфере бизнеса, где время имеет решающее значение, людям не хочет- 165 Что.такое.фреймворк?. . ся, чтобы им приходилось осваивать новые вещи, если только это не является абсолютно необходимым. ЕЩЕ РАЗ О ПОВТОРНОМ ИСПОЛЬЗОВАНИИ __________________________________________ В.главе.7.мы.говорили.о.повторном.использовании.кода.в.том.плане,.в.каком.оно. касается.наследования,.—.по.сути,.один.класс.наследует.от.другого..Эта.глава. посвящена.фреймворкам.и.повторному.использованию.целых.или.частичных.си- стем. Сам собой напрашивается следующий вопрос: если вам понадобится диалоговое окно, то каким образом вы будете использовать диалоговое окно, обеспечивае- мое фреймворком? Ответ прост: вам потребуется следовать правилам, которые предусмотрены для соответствующего фреймворка. А где эти правила можно найти? Правила, касающиеся фреймворка, можно найти в документации. Че- ловек или люди, написавшие класс, классы или библиотеки классов, должны были обеспечить документацию по использованию открытых интерфейсов класса, классов или библиотек классов (по крайней мере, мы надеемся на это). Во многих случаях все это имеет форму интерфейса программирования при- ложений (API — Application Programming Interface). Например, для того чтобы создать строку меню на Java, вам потребовалось бы воспользоваться API-документацией к классу JMenuBar и взглянуть на открытые интерфейсы, которые там представлены. На рис. 8.2 показана часть Java API. Рис. 8.2. API-документация Глава.8..Фреймворки.и.повторное.использование 166 Используя такие API-интерфейсы, вы сможете создавать полноценные Java- апплеты, придерживаясь при этом требуемых стандартов. Если вы будете со- блюдать эти стандарты, то ваши апплеты смогут работать в браузерах с вклю- ченной поддержкой Java. Что такое контракт? В контексте этой главы мы будем рассматривать контракт как любой механизм, требующий от разработчиков соблюдения спецификаций того или иного API- интерфейса. Часто бывает так, что API-интерфейс называют фреймворком. Онлайн-словарь Merriam-Webster ( http://www.merriam-webster.com ) определяет контракт как «соглашение, связывающее обязательствами двух и более человек или сторон, в частности если оно имеет законную силу». Именно это и происходит, когда разработчик использует API-интерфейс, — менеджер проекта, частный предприниматель или отраслевой стандарт при этом обеспечивает принудительное следование требуемым нормам. При использо- вании контрактов разработчик обязан соблюдать правила, определенные для фреймворка. Сюда входят имена методов, количество параметров и т. д. (подпи- си и т. п.). Коротко говоря, стандарты создаются с целью способствовать ис- пользованию правильных методик разработки. ТЕРМИН «КОНТРАКТ» _________________________________________________________________ Термин.«контракт».широко.используется.во.многих.областях.бизнеса,.включая.раз- работку.программного.обеспечения..Не.путайте.представленную.здесь.концепцию. с.другими.возможными.концепциями.проектирования.программного.обеспечения,. которые.тоже.называются.контрактами. Принудительное следование требуемым нормам жизненно важно, поскольку всегда существует вероятность того, что разработчик нарушит контракт. Без принудительного следования требуемым нормам жуликоватый разработчик может решить «изобрести велосипед» и написать код по-своему, вместо того чтобы придерживаться спецификации, предусмотренной для определенного фреймворка. От стандарта мало пользы, если люди игнорируют или обходят его. При использовании таких языков программирования, как Java и .NET, два способа реализации контрактов заключаются в применении абстрактных клас- сов и интерфейсов соответственно. Абстрактные классы Один из способов реализации контракта состоит в использовании абстрактно- го класса. Абстрактный класс — это класс, содержащий один или несколько методов, которые не имеют какой-либо обеспеченной реализации. Допустим, 167 Что.такое.контракт?. . у вас есть абстрактный класс Shape . Он является абстрактным потому, что нель- зя создать его экземпляр. Если вы попросите кого-нибудь нарисовать фигуру, то первый вопрос, который вам зададут, скорее всего, будет звучать так: «Какой формы?» Таким образом, концепция фигуры является абстрактной. Однако если кто-нибудь попросит вас нарисовать круг, то в этом случае проблема будет не совсем такой же, поскольку круг является конкретной концепцией. Вы зна- ете, как выглядит круг. Вы также знаете, как нарисовать фигуры других форм, например прямоугольники. Как все это применимо к контрактам? Предположим, вам требуется создать приложение для рисования фигур. Наша цель заключа- ется в рисовании всевозможных фигур, представленных в текущей конструкции, а также тех, что могут быть добавлены позднее. Есть два условия, которых мы должны придерживаться в данном случае. Во-первых, нам необходимо, чтобы для рисования всех фигур использовался один и тот же синтаксис. Например, мы хотим, чтобы любой класс, который представляет ту или иную фигуру и реализован в нашей системе, содержал метод с именем draw() . Таким образом, опытные разработчики будут косвенно знать, что для рисования той или иной фигуры вы вызовете метод draw() , неза- висимо от того, какой она будет формы. Теоретически это сокращает количество времени, затрачиваемого на копание в руководствах, а также способствует снижению количества синтаксических ошибок. Во-вторых, важно, чтобы каждый класс отвечал за свои действия. Таким образом, несмотря на необходимость того, чтобы в классе был предусмотрен метод с име- нем draw() , этот класс должен обеспечивать собственную реализацию кода. Например, в обоих классах Circle и Rectangle будет присутствовать метод draw() , однако очевидно, что в классе Circle будет иметься код для рисования кругов, а в Rectangle , как и следовало ожидать, будет содержаться код для ри- сования прямоугольников. Когда мы, в конечном счете, создадим классы с име- нами Circle и Rectangle , которые будут подклассами Shape , им потребуется реализовать собственную версию Draw (рис. 8.3). Рис. 8.3. Иерархия.абстрактных.классов Глава.8..Фреймворки.и.повторное.использование 168 АБСТРАКТНЫЕ МЕТОДЫ ______________________________________________________________ Обратите.внимание.на.то,.что.абстрактные.методы.в.UML-диаграммах.выделены. курсивом. Таким образом, у нас есть фреймворк, который является по-настоящему по- лиморфным. Метод Draw может вызываться для рисования любой фигуры, предусмотренной в системе, однако его вызов приводит к разным результатам. Вызов метода Draw из объекта Circle приводит к рисованию круга, а вызов ме- тода Draw из объекта Rectangle — к рисованию прямоугольника. В сущности, отправка сообщения объекту вызывает разную ответную реакцию в зависимости от того, каким именно является этот объект. В этом и заключается суть поли- морфизма: circle.draw(); // рисует круг rectangle.draw(); // рисует прямоугольник Взглянем на код, который показывает, как Rectangle и Circle соблюдают кон- тракт с Shape . Вот код для класса Shape : public abstract class Shape { public abstract void draw(); // реализация отсутствует } Обратите внимание, что класс не обеспечивает какой-либо реализации для draw() ; по сути, код отсутствует, что делает метод draw() абстрактным (обеспе- чение кода сделало бы этот метод конкретным). Отсутствие реализации объ- ясняется двумя причинами. Во-первых, Shape не знает, что рисовать, в силу чего мы не смогли бы реализовать метод draw() , даже если бы захотели. СТРУКТУРНАЯ АНАЛОГИЯ _____________________________________________________________ Это.интересный.момент..Если.бы.мы.пожелали,.чтобы.класс. Shape .содержал.код.для. рисования.всевозможных.фигур,.как.нынешних,.так.и.будущих,.то.нам.потребовал- ся.бы.условный.оператор.(вроде. case )..Тогда.сопровождение.всего.кода.оказалось. бы.очень.запутанным.и.сложным..Это.лишь.один.пример.того,.где.будут.кстати.пре- имущества.объектно-ориентированного.проектирования. Во-вторых, нам нужно, чтобы подклассы обеспечивали реализацию. Взглянем на классы Circle и Rectangle : public class Circle extends Shape { public void Draw() {System.out.println ("Рисование круга")}; } 169 Что.такое.контракт?. . public class Rectangle extends Shape { public void Draw() {System.out.println ("Рисование прямоугольни- ка")}; } Обратите внимание, что оба класса Circle и Rectangle расширяют (то есть на- следуют от) Shape . Заметьте также, что они обеспечивают фактическую реали- зацию. Именно здесь в дело вступает контракт. Если Circle будет наследовать от Shape , однако в итоге в нем не окажется метода draw() , то Circle не пройдет даже компиляцию. Таким образом, Circle не сможет выполнить контракт с Shape . Менеджер проекта может потребовать, чтобы программисты, создающие для приложения классы, которые представляют фигуры, обеспечили наследо- вание от Shape . Благодаря этому все такие классы в приложении будут содержать метод draw() , который станет работать ожидаемым образом. CIRCLE ________________________________________________________________________________ Если.у. Circle .действительно.не.получится.реализовать.метод. draw() ,.то.этот.класс. будет.считаться.абстрактным..Таким.образом,.еще.один.подкласс.должен.наследо- вать.от. Circle .и.реализовать.метод. draw() ..Тогда.этот.подкласс.станет.конкретной. реализацией.обоих.—. Shape .и. Circle Хотя концепция абстрактных классов «вращается» вокруг абстрактных методов, ничто не мешает Shape обеспечить реализацию (помните, что определение аб- страктного класса заключается в том, что он содержит один или несколько аб- страктных методов, — это означает, что абстрактный класс также может включать конкретные методы). Например, несмотря на то что Circle и Rectangle по-разному реализуют метод draw() , они совместно используют один и тот же механизм для задания цвета фигуры. Таким образом, класс Shape может содержать атрибут color и метод для задания цвета. Метод setColor() является конкретной реализацией и был бы унаследован обоими — Circle и Rectangle . Единственными методами, которые подкласс должен реализовать, являются те, что объявлены в суперклас- се абстрактными. Эти абстрактные методы представляют собой контракт. ПРЕДОСТЕРЕЖЕНИЕ __________________________________________________________________ Знайте,.что.в.случае.с.Shape,.Circle.и.Rectangle.мы.имеем.дело.с.отношением.стро- гого.наследования,.а.не.с.отношением.интерфейса,.о.котором.пойдет.речь.в.следу- ющем.разделе..Circle.является.экземпляром.Shape.так.же,.как.и.Rectangle. В некоторых языках программирования, например C++, для реализации кон- трактов используются только абстрактные классы; вместе с тем Java и .NET располагают другим механизмом, который реализует контракт, называемый интерфейсом. В прочих языках программирования, например Objective-C |