Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекция 2. Наследование.doc
Скачиваний:
5
Добавлен:
15.11.2019
Размер:
273.92 Кб
Скачать

Столпы объектно-ориентированного программирования

С# можно считать новым членом сообщества объектно-ориентированных языков программирования, к самым распространенным из которых относятся Java, C++, Object Pascal и (с некоторыми допущениями) Visual Basic 6.0. В любом объектно-ориентированном языке программирования обязательно реализованы три важнейших принципа — «столпа» объектно-ориентированного программирования:

  • инкапсуляция: как объекты прячут свое внутреннее устройство;

  • наследование: как в этом языке поддерживается повторное использование кода;

  • полиморфизм: как в этом языке реализована поддержка выполнения нужного действия в зависимости от типа передаваемого объекта?

Конечно же, все три этих принципа реализованы и в С#. Однако перед тем как начать рассматривать синтаксические особенности реализации этих принципов, мы постараемся убедиться, что в них не осталось абсолютно никаких неясностей.

Инкапсуляция

Первый «столп» объектно-ориентированного программирования — это инкапсуляция. Так называется способность прятать детали реализации объектов от пользователей этих объектов. Например, предположим, что вы создали класс с именем DBReader (для работы с базой данных), в котором определено два главных метода: Open( ) и Close( ).

// Класс DBReader скрывает за счет инкапсуляции подробности открытия

// и закрытия баз данных

DBReader f = new DBReader( );

f.Open(@"C:\foo.mdf");

// Выполняем с базой данных нужные нам действия

f.Close( );

Наш вымышленный класс DBReader инкапсулирует внутренние подробности того, как именно он обнаруживает, загружает, выполняет операции и в конце концов закрывает файл данных. За счет инкапсуляции программирование становится проще: вам нет необходимости беспокоиться об огромном количестве строк кода, которые выполняют свою задачу скрыто от вас. Все, что от вас требуется — создать экземпляр нужного класса и передать ему необходимые сообщения (типа «открыть файл с именем foo.mdf»).

С философией инкапсуляции тесно связан еще один принцип — сокрытия всех внутренних данных (то есть переменных-членов) класса. Как мы уже говорили, в идеале все внутренние переменные члены класса должны быть определены как private. В результате обращение к ним из внешнего мира будет возможно только при помощи открытых функций — членов. Такое решение, помимо всего прочего, защищает вас от возможных ошибок, поскольку открытые данные очень просто повредить.

Наследование: отношения «быть» и «иметь»

Следующий столп объектно-ориентированного программирования — наследование. Под ним понимается возможность создавать новые определения классов на основе существующих. В сущности, наследование позволяет вам расширять возможности, унаследованные от базового класса, в собственном производном классе. Простой пример реализации такого подхода представлен на рис. 3.3.

Рис. 3.3. Отношение «быть»

Как мы помним, на вершине любой иерархии в .NET всегда находится базовый класс Object. В нашей ситуации возможности этого класса вначале дополняются возможностями, привнесенными классом Shape. Речь идет о свойствах, полях, методах и событиях, которые являются общими для всех геометрических фигур (shapes). Класс Hexagon (шестиугольник), производный от Shape, дополняет возможности предыдущих двух базовых классов своими собственными уникальными свойствами.

Диаграмму, представленную на рис. 3.3, можно прочесть следующим образом-«Шестиугольник есть геометрическая фигура, которая есть объект». Когда ваши •лассы Связываются друг с другом отношениями наследования, это означает, что зы устанавливаете между ними отношения типа «быть» (is-a). Такой тип отношений называется также классическим наследованием.

В мире объектно-ориентированного программирования используется еще одна эорма повторного использования кода. Эта форма называется включением-деле-"ированием (или отношением «иметь» — has-a). При ее использовании один класс включает в свой состав другой и открывает внешнему миру часть возможностей этого внутреннего класса.

Например, если вы создаете программную модель автомобиля, у вас может появиться идея включить внутрь объекта «автомобиль» объект «радио» с помощью отношения «иметь». Это вполне разумный подход, поскольку вряд ли возможно произвести как радио от автомобиля, так и автомобиль от радио, используя отношения наследования. Вместо наследования вы создаете два независимых класса, работающих совместно, где внешний (контейнерный) класс создает внутренний класс и открывает внешнему миру его возможности (рис. 3.4).

Рис. 3.4. Отношения между средой выполнения и библиотекой базовых классов .NET

Внешний объект Саг (автомобиль) ответственен за создание внутреннего объекта Radio (радио). Если вы хотите, чтобы через объект Саг можно было бы обращаться ко внутреннему объекту Radio, в классе Саг необходимо предусмотреть специальный открытый интерфейс для этой цели. Обратите внимание, что пользователь объекта Саг и не узнает, что тот передает обращения какому-то внутреннему объекту:

// Внутренний класс Radio инкапсулирован внешним классом Саг

Саг viper = new Car( );

Viper.TurnOnRadio(false); // Вызов будет передан внутреннему объекту Radio