ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание
Шрифт:
Удобство определения защищенных членов в базовом классе заключается в том, что производные типы теперь могут иметь доступ к данным не только через открытые методы и свойства. Недостатком, конечно, является то, что при прямом доступе производного типа к внутренним данным родителя вполне вероятно случайное нарушение существующих правил, установленных в рамках общедоступных свойств (например, превышение допустимого числа страниц для "мини-романа"). Создавая защищенные члены, вы задаете определенный уровень доверия
Наконец, следует понимать, что с точки зрения пользователя объекта защищенные данные воспринимаются, как приватные (поскольку пользователь находится "за пределами семейства"). Так, следующий вариант программного кода некорректен.
Запрет наследования: изолированные классы
Создавая отношения базовых классов и подклассов, вы получаете возможность использовать поведение существующих типов. Но что делать, когда нужно определить класс, не позволяющий получение подклассов? Например, предположим, что в наше пространство имен добавлен еще один класс, расширяющий уже существующий тип SalesPerson. На рис. 4.8 показана соответствующая схема.
< image l:href="#"/>Рис. 4.8. Расширенная иерархия служащих
Класс PTSalesPerson является классом, представляющим продавца, работающего на неполную ставку, и предположим, например, что вы хотите, чтобы никакой другой разработчик не мог создавать подклассы из PTSalesPerson. (В конце концов, какую еще неполную ставку можно получить на основе неполной ставки?) Чтобы не допустить возможности расширения класса, используйте ключевое слово C# sealed.
Поскольку класс PTSalesPerson изолирован, он не может служить базовым Классам никакому другому типу. Поэтому при попытке расширить PTSalesPersоn вы получите сообщение об ошибке компиляции.
Наиболее полезным ключевое слово sealed оказывается при создании автономных классов утилит. Класс String, определённый в пространстве имен Sуstem, например, явно изолирован.
Поэтому вы не сможете создать новый класс, производный от System.String:
Если вы хотите создать новый класс, использующий функциональные возможности изолированного класса, единственным вариантом будет отказ от классического наследования и использование модели локализации/делегирования (известной еще как отношение локализации, "has-a").
Модель локализации/делегирования
Как уже отмечалось в этой главе, наследование можно реализовать двумя способами. Только что мы исследовали классическое отношение подчиненности ("is-a"). Чтобы завершить обсуждение второго принципа ООП, давайте рассмотрим отношение локализации (отношение "has-a", также известное под названием модели локализации/делегирования). Предположим, что мы создали новый класс, моделирующий пакет льгот работника.
Ясно, что отношение подчиненности ("is-a") между типами BenefitPackage (пакет льгот) и Employee (работник) выглядело бы достаточно странно. (Является ли менеджер пакетом льгот? Вряд ли.) Но должно быть ясно и то, что какая-то связь между этими типами необходима. Короче, вы должны выразить ту идею, что каждый работник имеет ("has-a") пакет льгот. Для этого определение класса Employee следует обновить так", как показано ниже.
Здесь вы успешно создали вложенный объект. Но чтобы открыть функциональные возможности вложенного объекта внешнему миру, требуется делегирование. Делегирование означает добавление в класс-контейнер таких членов, которые будут использовать функциональные возможности содержащегося в классе объекта. Например, можно изменить класс Employee так, чтобы он открывал содержащийся в нем объект empBenefits с помощью некоторого свойства, а также позволял использовать функциональные возможности этого объекта с помощью нового метода GetBenefitCost.