Наследование и полиморфизм
Цель лекции: Показать возможности технологий наследования и полиморфизма при проектировании классов. Научится применять эти технологии при программировании на языке C#.
Чтобы продемонстрировать механизм наследования классов рассмотрим несколько нарочито простых классов, которые будут описывать транспортные средства: автобус, такси, троллейбус и трамвай. Базовым классом у нас будет класс "траспортное средство", который мы определим следующим образом:
Смысл этого класса хранить и распечатывать информации о транспортном средстве. Любое транспортное средство имеет такие характеристики как скорость, масса и грузоподъемность. Однако для автотранспорта есть еще дополнительные характеристики такие, как расход топлива, а у электротранспорта есть такая дополнительная характеристика как напряжение. Поэтому мы создадим еще два класса. С использованием технологии наследования. Наследование - это технология проектирования классов, позволяющая создавать новые классы на базе предыдущих классов. При этом классы наследники будут иметь все поля и методы родительского класса. А к тем полям и методам, которые имеют спецификатор доступа или , можно будет обращаться из наследного класса. Приведем определения этих классов:
Разберем, как работает наследование класса. Чтобы указать, что класс является наследником класса , следует написать:
Теперь класс имеет все поля и методы класса . Однако поля и методы класса , имеющие классификатор доступа , будут недоступны для класса . Если в классе объявлены поля и методы, совпадающие с полями и методами класса , то они будут заменены, или как говорят - сокрыты соответствующими полями и методами класса . В нашем примере таким методом является метод . Если нам нужно из класса обратиться к полям или методам родительского класса , то для этого нужно использовать ключевое слово , аналогичное ключевому слову .
Если родительский класс имеет заданный конструктор, то он должен быть вызван из конструктора наследного класса. Обратите внимание, как можно вызывать конструктор родительского класса:
Попрактикуемся в создании классов наследников. Создадим два класса (автомобиль и такси) наследников от класса и два класса (трамвай и троллейбус) наследников от класса . У каждого нового класса могут быть дополнительные поля и методы, но мы для простоты не будем добавлять новых полей и методов.
Предположим, что у нас будет список различных транспортных средств, для которых нам нужно вычислить отношение стоимости единицы полезной массы к расходу топлива (бензина или электроэнергии). Создадим массив транспортных средств:
Заметим, что у нас в массиве все элементы из разных классов, хотя и родственники класса . Конечно, мы могли бы, перебирая массив , для каждого экземпляра класса подсчитать нужное соотношение. Но тут возникает проблема - ведь у половины классов нужно делить значение на значение , а у половины нужно делить значение на значение . Тем более, что идеология объектно-ориентированного программирования подразумевает, что классы сами должны обрабатывать собственные данные. Поэтому мы поступим следующим образом - мы добавим в наши классы метод . При этом мы воспользуемся технологией полиморфизма. Полиморфизм - это возможность объектов с одинаковой спецификацией иметь различную реализацию. Мы создадим у класса TTransport виртуальный метод:
Ключевое слово означает, что наследники данного класса смогут изменить код данного метода. Сейчас мы еще модифицируем у родительского класса метод следующим образом:
Разумеется, везде мы видим , потому что мы еще нигде не реализовали метод вычисления нужного нам коэффициента. Но вспомним, что мы пометили метод как виртуальный, и теперь мы можем заменить этот метод у классов потомков. Сделаем следующие модификации наших классов:
Теперь наша программа выдаст нужный результат:
Чтобы оценить всю мощь полиморфизма, нужно обратить внимание, что метод вызывается в методе класса ! Ведь при наследовании мы можем и не иметь исходного текста класса , этот класс может быть доступен только в виде откомпилированной библиотекой. Тем не менее механизм полиморфизма позволяет нам модифицировать виртуальные методы. Мы будем использовать систематически полиморфизм при реализации вычислительных процедур.
Сделаем еще одно замечание. В родительском классе нам пришлось фиктивно реализовать метод , хотя с точки зрения проектирования это не очень правильно. Во-первых, плохо писать код только для того, чтобы он был, а, во-вторых, плохо писать неверный код: возвращать 0. К счастью, развитое объектно-ориентированное программирование позволяет создавать так называемые абстрактные классы. Абстрактные классы могут содержать абстрактные виртуальные методы. Абстрактный метод содержит только объявление, но не имеет реализации. Разумеется, абстрактный метод является виртуальным, но ключевое слово не пишется, чтобы в наследных классах можно было перекрыть этот метод. Более того, невозможно создать экземпляр абстрактного класса, а наследник абстрактного класса или сам должен быть абстрактным или обязан реализовать все абстрактные методы. Последний штрих к нашей программе:
Еще раз подчеркнем, что мы не привели в этом листинге реализацию метода не потому, чтобы сократить письмо, а потом что для абстрактного метода не бывает реализации.
Ключевые термины
Абстрактный класс - класс, содержащий нереализованные методы, которые необходимо реализовать в классах наследниках.
Наследование - технология проектирования классов, позволяющая создавать новые классы на базе предыдущих классов.
Полиморфизм - возможность объектов с одинаковой спецификацией иметь различную реализацию.
Краткие итоги: На примерах проектирования классов продемонстрированы технологии наследования и полиморфизма в языке C#. Показано, как работать с абстрактными классами.