Теперь от универсальности в чистом виде можно перейти к наследованию. Для сравнения его с универсальностью рассмотрим пример из стандартной библиотеки для работы с файлами. Начнем с эскиза реализации "специальных файлов" в смысле Unix, то есть файлов, связанных с устройствами:
class DEVICE feature open (file_descriptor: INTEGER) is do ... end close is do ... end opened: BOOLEAN endПример использования этого класса:
d1: DEVICE; f1: INTEGER; ... create d1.make; d1.open (f1); if d1.opened then ...Теперь рассмотрим понятие ленточного накопителя. Это устройство обладает всеми свойствами, представленными в классе DEVICE, плюс способность перематывать ленту. Вместо формирования нового класса на пустом месте можно использовать наследование и объявить класс TAPE, расширяя и модифицируя DEVICE. Новый класс расширяет DEVICE, добавляя новую процедуру rewind в соответствии с особенностями именно ленточных устройств. Кроме того необходима новая версия open, модифицированная с учетом специфики накопителей на магнитной ленте.
Объекты типа TAPE автоматически обладают всеми свойствами объектов DEVICE плюс их собственные (перемотка). Придется модифицировать ряд компонентов DEVICE в классе TAPE (open) и добавить новые (rewind). Класс DEVICE может иметь и других потомков, например класс DISK с его собственными специфическими особенностями прямого доступа.
Наследованию сопутствует полиморфизм, разрешая присваивания x:= y, если тип x - предок типа y. Следующая особенность - динамическое связывание: если x устройство, то вызов x.open (f1) будет выполнен различным образом в зависимости от значения присвоенного x перед вызовом. После присваивания x := y, где y - лента, будет выполнена версия открытия для ленты.
Как уже указывалось, преимуществами наследования являются возможность повторного использования и расширяемость. Ключевым является принцип Открыт-Закрыт: программный элемент типа DEVICE пригоден как для непосредственного использования, так и в качестве предка новых классов.