Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 40
Текст из файла (страница 40)
работы лишь ненамного больше. Но плюс состоит в том,что ыетнаг)с Саплпап1сагог можно повторно использовать, как если бы он был "черным ящиком". Разработчики ыесиогксапппапусасаг могли сделать его зеа1еб. а вы все равно смогли бы повторно его использовать. Будь он зеа1еб, вы по определению не смогли бы наследовать от него. Используя ыесног)ссошшпп1сасаг через включение, можно даже предоставить общедоступный контракт на контейнере, который будет выглядеть несколько иначе, чем тат,что реализует сам Магнат)гСошшапгсагаг. Такой подход известен как шаблон проектирования РЬсабе (Фасад). Еще один недостаток применения наследования заключается в том, что оно не динамично.
Наследование статично в силу того факта. что определяется на этапе компиляции. Такое положение дел может оказаться, как минимум, весьма ограничивающим. Применяя включение, вы устраняете это ограничение. Однако чтобы сделать это, также следует заручиться поддержкой со стороны хорошего друга — полиморфизма. При этом включающий тип может быть. скажем, интерфейсным типом. Тогда включаемый 148 Глава 4 объект просто должен поддерживать контракт этого интерфейса, чтобы повторно использоваться контейнером.
Более того, этот объект можно изменить во время выполнения. Задумайтесь об этом на минутку. Предположим, что есть объект, представляющий контейнер сортируемых объектов. В этом контейнерном типе предусмотрен алгоритм сортировки по умолчанию. Если вы реализуете этот алгоритм по умолчанию как включаемый тип, который можно подменить во время выполнения, тогда, если того требует предметная область, вы всегда сможете заменить его собственным специальным алгоритмом сортировки — до тех пор, пока объект нового алгоритма сортировки реализует необходимый интерфейс, ожидаемый контейнерным типом. Такая техника известна как шаблон проектирования З1гаге))у (Стратегия]. В итоге дизайн с динамическими конструкциями является намного более гибким, чем при использовании конструкций статических.
Сюда относится и предпочтение включения наследованию во многих случаях повторного использования. Такой тип повторного использования известен как делегирование, поскольку работа делегируется включаемому типу. Включение также предохраняет инкапсуляцию, в то время как наследование ее разрушает. Тем не менее, важно сделать одно предупреждение. Как почти с любым техническим приемом, существует риск переусердствовать с включением. Для небольших служебных классов, возможно, и не стоит предпринимать слишком много усилий по внедрению включения.
И в некоторых случаях необходимо использовать наследование для реализации специализации. Но если говорить в целом, то дизайн, предпочитающий наследованию включение в качестве механизма повторного использования, обеспечивает намного большую гибкость и гораздо лучше выдерживает испытание временем. Всегда учитывайте мощь наследования, включая возможные неприятные последствия от злоупотребления им. Резюме В этой очень длинной главе были освещены важные моменты, относящиеся к системе типов С№, которая позволяет создавать новые типы, оснащенные всеми возможностями встроенных типов, определенных в исполняющей системе.
Вначале рассматривались определения классов, применяемых для определения новых ссылочных типов, затем описывались определения структур, используемых для создания экземпляров новых типов значений внутри С1Л, и указывались основные отличия между этими двумя категориями типов. В непосредственной связи с темой типов значений находится тема упаковки и распаковки — операций, которые могут привести к нежелательным последствиям, если вы не знаете, где именно упаковка может быть вставлена компилятором.
(В главе 11, посвященной обобщениям, будет показано, как в некоторых случаях можно вообще избежать упаковки и распаковки.) Затем начала раскрываться сложная тема создания и инициализации объектов, а также их уничтожения. Уничтожение — довольно сложная тема в рамках СЬВ, поскольку ссылочные типы могут поддерживать как детерминированное, так и недетерминированное уничтожение. 1Более подробно уничтожение рассматривается в главе 13, где приводится множество примеров.) Далее кратко рассказывалось о перегрузке методов в С№ и различных модификаторах, которыми можно снабдить методы, чтобы управлять ихизменением;г№ггаа1, озеггаое или зеа1ео. И.наконец, некоторое время было уделено обсуждению наследования, полиморфизма и включения, а также приведено несколько советов относительно выбора между ними.
В следующей главе раскрывается важнейшая тема интерфейсно-ориентированного (или контрактно-ориентированного) программирования. а также его использования в среде СЕВ. глдвд 5 Интерфейсы и контракты о времена, когда вы начинали заниматься разработкой программного обеспечения, наверняка вы встречали упоминание июлер4ебсно-ориенглироэанного программпроэания.
Если вы читали фундаментальную книгу Эриха 1Иммы (Ег!сИ Оапппа), Ричарда Хелма (В1сИагб Не1ш), Ральфа Джонсона (Ка1РИ доЬпэоп) и Джона Влиссидеса (доЬп ЪЧ!ээ!Иеэ) (известных, как "банда четырех") 13еэгдп Ранетпз: Е(степ!э о('КеиэпЫе ОЬ)есг-Олен!ес( ЭоЯшаге (Абб!эоп-У!Геэ!еу Рго!еээ!опа(, 1995 г.), то знаете, что многие шаблоны проектирования используют "контракты" в стиле интерфейсов. Если вы еще не знакомы с этой книгой и изложенных в ней концепциях, рекомендуется прочесть ее. Цель настоящей главы — показать, как моделировать хорошо определенные, поддерживающие множество версий контракты с использованием интерфейсов. В этом контексте контракт — это соглашение типа на поддержку набора функциональности.
Если в течение последних лет приходилось заниматься разработкой с использованием СОМ и СОВВА, то наверняка это была интерфейсно-ориентированная разработка. Фактически интерфейс является единственной формой взаимодействия меж!р компонентами в СОМ. Поэтому большая часть сложности дизайна заключается в разработке надежных интерфейсов, прежде чем приступать к написанию хоть одной строки кода реализации.
Отказ от следования этой парадигме становится источником многих проблем. Например, Иэпз) 8!псйо 2003 предоставляет удобную среду для создания вебслужб. Всего лишь аннотируя методы класса определенным образом, эти методы можно представить как методы веб-службы. Однако при этом 1ОЕ-среда воспитывает подход, в котором подразумевается, что интерфейс — это результат аннотирования методов класса, а не наоборот. Таким образом, телега ставится впереди лошади.
На самом деле вместо этого необходимо четко определить интерфейс веб-службы перед тем, как начать какое-либо кодирование,а только потом кодировать реализацию этого интерфейса. Единственное преимуществотакого подхода состоит в возможности параллельного кодирования серверной и клиентской частей. Другая сторона проблемы связана стем, что, однажды опубликовав интерфейс для всего мира, его нельзя изменять. Любое изменение немедленно разрушит работу всех реализаций, основанных на нем. К сожалению, среда Ч!эпа) Э!псйо поощряет нарушение этого правила, облегчая возможность добавления новых методов в класс и аннотирования их как методов веб-службы.
В хорошо спроектированной интерфейсно-ориентированной системе, такой как система с архитектурой, ориентированной на службы (эеппсе-онеп!ес1 агсИ!!ес!пге — БОА), сначала должен разрабатываться интерфейс в качестве контракта между компонентами. Контракт управляет реализацией.
а не наоборот. К сожалению, слишком много инструментов в прошлом и даже в настоящем стимулируют такую разработку "задом 150 Глава 5 наперед". Но это не значит, что вы должны безропотно следовать их ошибочному пути. В конце концов, контракт, примененный к типу, определяет набор требований к этому типу.
Не имеет смысла, чтобы сами типы определяли требования, предъявляемые к ним. В среде .НЕТ интерфейсы являются типами. Интерфейсы определяют типы Объявление интерфейса определяет ссылочный тип. В переменных этого типа можно хранить ссылки на объект, располагающийся в куче и удаляемый сборщиком мусора, который реализует контракт типа интерфейса. Каждая переменная в СЬК хранится в определенном месте памяти, будь то куча или стек. Каждое место хранения имеет ассоциированный с ним тип. Когда переменная, скажем, ссылка на объект, находится в этом месте, она должна быть того же типа, что и это местоположение, либо допускать преобразование к типу, ассоциированному с местоположением.
Если она может быть преобразована автоматически в тип местоположения, тогда она является неявно преобразуемой к типу этого места хранения. Во многих примерах в качестве основы для демонстрации используется воображаемый каркас графического интерфейса (ЖЛ); то же самое будет и здесь. Взгляните на следующий фрагмент кода: рпЬ11с Тпкеггасе 101Соппго1 ( нозс Ра1пг () 1 ) риЬ11с с1аяя Впгооп: 1О1Сопгго1 ( риЬ11с уо1О Рауля() ( IГ Нарисовать кнопку ) РпЬ11с с1аяэ 'Тяъвох: 101Соппго1 рпЪ11с уо1О Ра1пя() ГГ Нарисовать окно списка ) ) В этом примере объявлен интерфейс 1С1Сопгго1, который просто предоставляет один метод — Раапя.












