204
Глава.6 .Основные.сведения.о.членах.и.типах
Безопасность и предсказуемость.
Состояние класса должно быть надежно
защищено. Если класс не запечатан, производный
класс может изменить его
состояние, воспользовавшись незащищенными полями или методами базового
класса, изменяющими его доступные незакрытые поля. Кроме того, в произво-
дном классе можно переопределить виртуальные методы и не вызывать реали-
зацию соответствующих методов базового класса. Назначая метод, свойство
и событие виртуальным, базовый класс уступает
некоторую степень контроля
над его поведением и состоянием производному
классу, что при неумелом обра-
щении может вызвать непредсказуемое поведение и проблемы с безопасностью.
Беда в том, что запечатанные классы могут создать изрядные неудобства для
пользователей типа. Разработчику приложения может понадобиться производный
тип, в котором будут добавлены дополнительные поля или другая информация
о состоянии. Они даже могут попытаться добавить в производном типе дополни-
тельные методы для работы с этими полями. Хотя CLR не предоставляет механизма
расширения уже построенных типов вспомогательными методами или полями,
вспомогательные методы можно имитировать при
помощи методов расширения
C# (см. главу 8), а для расширения состояния объекта может использоваться класс
ConditionalWeakTable
(см. главу 21).
Вот несколько правил, которым я следую при проектировании классов:
Если класс не предназначен для наследования, я всегда явно объявляю его
запечатанным. Как уже отмечалось, C# и многие современные компиляторы
поступают иначе. Если нет необходимости в предоставлении другим сборкам
доступа к классу, он объявляется внутренним. К счастью, именно так ведет себя
по умолчанию компилятор C#. Если я хочу определить класс, предназначенный
для создания производных классов, одновременно запретив его специализацию,
я должен переопределить и запечатать
все виртуальные методы, которые на-
следует мой класс.
Все поля данных класса всегда объявляются закрытыми, и в этом я никогда не
уступлю. К счастью, по умолчанию C# поступает именно так. Вообще говоря,
я бы предпочел, чтобы в C# остались только закрытые поля, а объявлять их
со спецификаторами
protected
,
internal
,
public
и т. д. было бы запрещено.
Доступ к состоянию объекта — верный путь к непредсказуемому поведению
и проблемам с безопасностью. При объявлении полей внутренними (
internal
)
также могут возникнуть проблемы, поскольку даже внутри одной сборки очень
трудно отследить все обращения к полям, особенно когда над ней работает не-
сколько разработчиков.
Методы, свойства и события класса я всегда объявляю
закрытыми и невирту-
альными. К счастью, C# по умолчанию делает именно так. Разумеется, чтобы
типом можно было воспользоваться, некоторые методы, свойства и события
должны быть открытыми, но лучше не делать их защищенными или внутрен-
ними, поскольку это может сделать тип уязвимым. Впрочем, защищенный или
205
Компоненты,.полиморфизм.и.версии
внутренний член все-таки лучше виртуального, поскольку последний предо-
ставляет производному классу большие возможности
и всецело зависит от
корректности его поведения.
В ООП есть проверенный временем принцип: «лучший метод борьбы со слож-
ностью — добавление новых типов». Если реализация алгоритма чрезмерно
усложняется, следует определить вспомогательные типы, инкапсулирующие
Do'stlaringiz bilan baham: