Продолжаем тему различных принципов проектирования классов и в целом про объектно-ориентированное программирование на ПЛК в среде Codesys. Сегодня на повестке дня принцип подстановки Лисков. Мы рассмотрели принцип единственной ответственности, а также принцип открытости/закрытости
Что же такого интересного сформулировала Бараба Лисков?
Здесь требуется что-то вроде следующего свойства подстановки: если для
каждого объекта o1 типа S существует такой объект o2 типа T, что для
всех программ P, определенных в терминах T, поведение P не изменяется при
подстановке o1 вместо o2, то S является подтипом T1.
Barbara Liskov Data Abstraction and Hierarchy, SIGPLAN Notices 23, 5 (May 1988)
Фактически изначально это можно рассматривать как руководство к наследованию. Ну и очень грубо можно сказать, что при замене родительского объекта на дочерний общее поведение программы не должно измениться.
Как это работает в Codesys?
В данном примере мы рассмотрим вариант с реализацией интерфейсf. Задача будет не вакуумная на этот раз, но код синтетическим. Нам надо обрабатывать на ПЛК различные действия при нажатии кнопок на панели.
Сразу разрешите доложить, что способ решения данной задачи несколько, но я вот хочу такой.
Интерфейс
Логика рассуждения в следующем, нам надо создать один функциональный блок «Кнопка», который будет обрабатывать нажатия. Потом мы создаем для каждой кнопки свой экземпляр функционального блока и при нажатии различных кнопок должна выполняться разная логика.

Вот основа. Переменная xState будет условно означать пришел ли сигнал с панели или нет. Дальше небольшой обработчик в котором необходимо вызывать какие-то действия и скинуть сигнал, чтобы действия не повторялись повторно.
Результат имеет ссылочный тип, так как мы будем в последствии ссылаться на одно единственное значение и на мне нужны его переписывания в цикле тем значения, которое будет храниться в экземпляре функционального блока
Теперь чтобы все заработало вспоминаем про наследование. Все потомки могут встать на место родителей. И про принцип подстановки, что при такой замене у нас не должно изменится поведение.
Создаем интерфейс

В нем один метод, который мы называем Act и который будет нам возвращать значение типа String.

Теперь мы добавляем поле ButtonAct, которое будет принимать объект, соответствующий интерфейсу ButtonAction и после, в процессе обработки, будет вызывать метод Act()
Теперь обозначим два действия, которые будут выводить разные сообщения. чисто для наглядности.

Второе будет выводить немного иначе — с цифрой 2. И соберем все это.

Получается что при замене одного класса другим, которые будут наследоваться от одного интерфейса логика программы не нарушается, а значит LSP не нарушен.
Вопросы, комментарии и предложения на почту — info@engcore.ru
Также есть канал с интересными мировыми новостями в области промышленной автоматизации
