Базовый класс
Построим первый вариант описания класса Логический элемент (TLogElement). Обозначим его входы как In1 и In2, а выход назовем Res (от англ. result — результат) (рис. 7.11). Здесь состояние логического элемента определяется тремя величинами (In1, In2 и Res), это позволяет на основе того же самого базового класса моделировать и элементы с памятью (например, триггеры), а не только статические элементы (как НЕ, И, ИЛИ и т. п.).
Рис. 7.11
Любой логический элемент должен уметь вычислять значение выхода по известным входам, для этого введём в класс метод calc:
В таком варианте все данные открытые (общедоступные). Чтобы защитить внутреннее устройство объекта, скроем внутренние поля (добавим при этом в их названия первую букву «F») и введём свойства: type
Обратите внимание, что свойство Res — это свойство только для чтения, и другие объекты не могут его менять. Кроме того, мы поместили процедуру calc в скрытый раздел (private), потому что пересчёт результата должен выполняться автоматически при изменении любого входного сигнала (другие объекты не должны об этом беспокоиться).
Несложно написать процедуру setln1 (и аналогичную ей процедуру setln2), в ней новое входное значение присваивается полю и сразу пересчитывается результат:
Если внимательно проанализировать построенное описание класса, можно выявить несколько проблем. Во-первых, элемент НЕ имеет только один вход, поэтому не хотелось бы для него открывать доступ к свойству In2 (это не нужно и может привести к ошибкам).
Во-вторых, процедуру calc невозможно написать, пока мы не знаем, какой именно логический элемент моделируется. Вместе с тем мы знаем, что такую процедуру имеет любой логический элемент, т. е. она должна принадлежать именно классу TLogElement. Здесь можно написать процедуру-«заглушку» (которая ничего не делает):
Но нужно как-то дать возможность классам-наследникам изменить этот метод так, чтобы он выполнял нужную операцию. Такой метод называется виртуальным. Более точное определение этого понятия мы дадим несколько позже.
Классы-наследники могут по-разному реализовывать один и тот же метод. Такая возможность называется полиморфизмом.
Полиморфизм — это возможность классов-наследников по-разному реализовывать метод, описанный для класса-предка.
Мы уже говорили о том, что метод calc не нужно делать общедоступным (public). В то же время его нельзя делать закрытым (private), потому что в этом случае он не будет доступен классам-наследникам. В таких случаях в описании класса используется третий блок (кроме private и public), который называется protected (защищённый). Данные и методы в этом блоке доступны для классов-наследников, но недоступны для других классов.
В этот же блок protected мы переместим объявление поля FRes (его будут менять наследники в процедуре Calc) и объявление свойства In2 — оно будет скрыто для элемента «НЕ», а элементы с двумя входами его «откроют» (чуть позже).
Обратите внимание на объявление метода calc: после него стоят слова virtual (виртуальный) и abstract (в переводе с англ. — абстрактный). Описатель virtual говорит о том, что метод calc — виртуальный, и классы-наследники могут его переопределять. Как уже отмечалось, мы должны объявить этот метод (ввести его в описание класса), поскольку он должен быть у любого логического элемента. В то же время невозможно написать процедуру calc, пока неизвестен тип логического элемента. Такой метод называется абстрактным и обозначается описателем abstract. Для абстрактного метода не нужно ставить «заглушку».
Абстрактный метод — это метод класса, который объявляется, но не реализуется в классе.
Более того, не существует логического элемента «вообще», как не существует «просто фрукта», не относящегося к какому-то виду. Такой класс в ООП называется абстрактным. Его отличительная черта — хотя бы один абстрактный (нереализованный) метод.
Абстрактный класс — это класс, содержащий хотя бы один абстрактный метод.
Итак, полученный класс TLogElement — это абстрактный класс (компилятор определит это автоматически). Его можно использовать только для разработки классов-наследников, создать в программе объект этого класса нельзя.
Чтобы класс-наследник не был абстрактным, он должен переопределить все абстрактные методы предка, в данном случае метод calc. Как это сделать, вы увидите в следующем пункте.
Следующая страница Классы-наследники