Объектно-ориентированное программирование (ООП) – концепция, которая призвана облегчить разработку сложных систем, за счет введения новых понятий, более приближенных к реальному миру, чем функциональные и процедурные языки программирования. Как пишет википедия, «Обычный человеческий язык в целом отражает идеологию ООП, начиная с инкапсуляции представления о предмете в виде его имени и заканчивая полиморфизмом использования слова в переносном смысле, что в итоге развивает выражение представления через имя предмета до полноценного понятия – класса.»
Но с точки зрения всех, кто впервые сталкивался эти этим абстракциями, после классических процедурных языков понятнее не становилось, кажется наоборот все еще больше запутывалось.
С другой стороны, есть графические нотации работы программы, которые не приближены к человеческому языку, но гораздо понятнее, чем даже любой код, не то что ООП. Вполне возможно, что это более понятно мне, испорченному инженерным образованием, но таких как я много и этот текст для таких же испорченных физиков, не понимающих высокие абстракции.
Вот, например, реальное описание в графической нотации алгоритма управления задвижками АЭС.:
Рисунок 1. Пример программы управления АЭС в графической нотации
Слева входные сигналы, справа команды.
Мне кажется, что такой алгоритм прочитать может даже ребенок:
- Если насос включен в течении 60 секунд и расход меньше 10, то задвижку на рециркуляции открыть.
- Если насос включен, то подавать в течении 5 секунд на задвижки 001 и 002 команду открыть.
- Если расход больше 20 и насос включен, то в течении 5 секунд на задвижку 003 подавать команду закрыть.
В бытность мою студентом я подрабатывал, создавая библиотеку компонентов для Delphi и был знаком с ООП не понаслышке. Потом, когда столкнулся с реальными программами управления АЭС, очень удивился что нет никакого абстрагирования, инкапсуляции и, прости господи полиморфизма, только чистый Си, и еще желательно урезанный правилами и рекомендация MISRA C, чтобы все было надёжно, переносимо, безопасно.
Вершиной обрезания Си в моей практике был язык FIL, для систем управления реакторами РБМК. В нем функции заранее писались на Си, компилировались, а потом вызывались на основе текстового файла, где они были описаны на языке FIL. В итоге, можно было вызвать только ограниченный, но тщательно проверенный и отлаженный набор функций. И все это – во имя безопасности и надежности.
Но при этом система управления реактором и в целом система управления АЭС – это как раз тот случай, где принципы ООП должны применятся в полный рост. В само деле, есть множество однотипного оборудования – задвижки, насосы, датчики, всё легко классифицируется, есть готовые объекты, соответствующие реальному оборудованию. Казалось бы, вот оно – применяй ООП, классы, наследование, абстрагирование и полиморфизм. Но нет, нужен чистый Си и это требования безопасности.
А дальше – еще интереснее. На самом деле программу для управления АЭС пишет не программист, а технолог – только он знает, что и когда закрывать, открывать, включать, а главное – он знает, когда всю это байду выключать, чтобы не долбануло. А программист должен аккуратно всё это реализовать на коде Си. А еще лучше, что бы программиста вообще бы не было, а технолог сам рисовал технологические алгоритмы управляющих программ в графическом виде, автоматически генерировал код Си и загружал его в аппаратуру управления. Это рекомендуют международные стандарты по безопасности, в этом случае программист – как скрипач – не нужен. Он вносит только дополнительные ошибки и искажения в реализацию мыслей технолога.
Каково же было мое изумление, когда я узнал, что технологи и проектанты АЭС, сами независимо от программистов разработали и успешно применяют объектно-ориентированное программирование, да еще в графических нотациях, но при этом результирующий код полностью удовлетворяет требованиям безопасности и не содержит артефактов методологии ООП.
В самом деле, если посмотреть на код, который сгенерирован из схемы на рисунке 1 мы увидим чистый Си без всяких там классов.
Например таблица входа в алгоритм:
/* Index=0
UID=0
GeneratorClassName=TSignalReader
Name=KBA__AA.KBA31EY001.alg_inp
Type=Вход алгоритма */
state_vars->kbaalgsv0_out_1_ = kba31ap001_xb01;
state_vars->kbaalgsv0_out_4_ = kba31cf001_xq01;
Просто присвоение переменных.
Любой блок описывается как вычисление выхода по входу, с учетом параметров, заданных в списке констант. Например блок «Больше» выглядит в коде так:
/* Index=5
UID=5
GeneratorClassName=TLogBlock
Name=KBA__AA.KBA31EY001.smu.GT2
Type=Операция БОЛЬШЕ */
locals->v5_out_0_ = state_vars->kbaalgsv0_out_4_ > consts->kbaalgsv3_a_;
Выход блока это результат сравнение сигнала входа со значением в константе.
Таким образом, и в других блоках происходит последовательное вычисление локальных переменных из входных, и в конце цикла программы осуществляется запись в выходные переменные.
/* Index=14
UID=14
GeneratorClassName=TSignalWriter
Name=KBA__AA.KBA31EY001.alg_out
Type=Выход алгоритма */
if((action==f_InitState)||(action==f_GoodStep)||(action==f_RestoreOuts)){
kba31ey001_yb01 = locals->v8_out_0_;
kba31ey001_yb11 = state_vars->kbaalgsv9_out_0_;
kba31ey001_yb12 = state_vars->kbaalgsv12_out_0_;
kba31ey001_yb02 = locals->v13_out_0_;
};
А где здесь классы, спросите вы?
Вся методология, связанная с ООП, находится в именах переменных. Казалось бы, что такого может быть в имени переменной? А там может быть цела бездна. Например имя переменной kba31ap001_xb01, просто переменная в коде Си отвечающая требованием по наименованию переменных. Однако для технолога проектанта она выглядит примерно так: «Реакторное отделение, система промышленного водоснабжения, первый насос, пуск». Все это волшебство преобразования происходит благодаря замечательной немецкой системе кодирования (Kraftwerk-Kennzeichensystem) KKS, цитата:
“Данная система классификации кодирования предназначена для электростанций и обладает большими возможностями, а так же, учитывает особенности свободно-программируемых микропроцессорных технических средств.
Наряду с маркировкой технологического оборудования, исполнительных органов (запорно-регулирующей, предохранительной, отсечной и т.п. арматуры, механизмов собственных нужд), точек измерения, монтажных единиц, устройств автоматизации, зданий и сооружений, система KKS позволяет маркировать алгоритмы и программы различного вида и назначения (алгоритмы обработки измеряемых технологических параметров, сигнализации, автоматического регулирования, технологических защит, логического управления: блокировок, АВР, пошаговых программ, — расчета технико-экономических показателей и диагностики состояния технологического оборудования), входные, выходные и промежуточные сигналы этих алгоритмов и программ, видеограммы всех уровней, отображаемые на видеотерминалах, кабели и пр.».
Но самое интересное в последней части имени — _xb01, то что задается через знак подчеркивания. Если посмотреть на базу сигналов для проекта управления, то мы увидим там классы, понятные и знакомые всем, кто когда-то, как-то и где-то интересовался ООП (см. Рис. 2).
Рисунок 2. Пример структуры базы сигналов для системы управления АЭС.
У нас есть классы, или таблицы, на рисунке это столбец «Категории». Например, «KD1» у которых есть таблица шаблонных сигналов, полей класса Верхний предел измерения, нижний предел измерения, показание датчика и т.д. — это абстракция.
А так же есть реализация данного класса — конкретный датчик, например ТК21F02B1, расположенный в контуре, как вы уже догадались по его названию, в «Реакторном отделении, системе промышленного водоснабжения, у первого насоса», да и то, что это датчик расхода, тоже есть в этом названии, но это не точно.
И у этого экземпляра данного класса есть конкретные сигналы и их значения, в процессе работы программы, и к ним можно получить доступ по именам полей класса. Например, показание датчика рабочее обозначается переменной ТК21F02B1_XQ04.
На этом месте можно сказать, постой это же не совсем ООП, или даже совсем не ООП, тут же просто структуры данных, это есть и в стандартном Си. А где инкапсуляция методов в состав класса? Обработка данных должна быть в классе, тогда это и будет настоящий кошерный метод ООП.
Посмотрим, как выглядит в графическом виде подпрограмма контроля достоверности датчика. На рисунке 3 часть схемы обработки сигналов:
Рисунок 3. Пример программы обработки сигнала.
Видно, что в подпрограмме обработки используются имена переменных ТК21F02B1_XQ04, сформированные по правилам ККS и на основании таблицы полей класса. В приведенном примере происходит вычисление показания датчика в процентах ТК21F02B1_XQ03 по заданным значениям полей экземпляра класса таким, как ТК21F02B1_Xmin и ТК21F02B1_Xmax.
Если обратится к коду, сгенерированному из этой схемы, то мы увидим простое присвоение значение переменной, чистый Си и никаких плюсов и ООП.
/* Index=12
UID=12
GeneratorClassName=TSignalReader
Name=KD1.kd3_45.SR6
Type=Чтение из списка сигналов */
state_vars->su100v12_out_0_ = tk21f02b1_ai;
И присвоение результата расчета, тоже как простое присвоение переменной (с проверкой на действительность числа, что бы не уронить систему если в результате обработки сигналов мы получили ошибку)
/* Index=100
UID=100
GeneratorClassName=TSignalWriter
Name=KD1.kd3_45.SW3
Type=Запись в список сигналов */
if(isfinite(locals->v63_out_0_)){
tk21f02b1_xq04 = locals->v63_out_0_;
};
А в какой же момент появляется объединение данных полей класса методов обработки? На самом деле я знаком с двумя вариантами этого фокуса. Сейчас разберем один из них.
Посмотрим, как на схеме настраивается блок в котором расположена схема программы обработки (см. рис. 4).
У нас есть схема, на которую мы размещаем блоки субмодели графического языка программирования, внутри этих блоков находится графическая схема, часть которой приведена на рисунке 3, — программа обработки сигналов с датчиков.
В свойствах данного блока мы видим поля базы данных сигналов и выпадающий список, в котором находятся уже существующих в базе данных сигналов, экземпляры класса, конкретные датчики данного типа. Достаточно выбрать нужный датчик, экземпляр класса по имени и происходит чудо. В схеме все блоки чтение и записи получают имена типа ТК21F02B1_XQ03, (имя датчика экземпляра класса + имя поля).
Теперь при генерации кода Си все переменные получат значения нужного датчика. И программист не нужен, технолог все сделал сам когда разрабатывал схему в графическом языке програмирования для алгоритма управления АЭС.
Рисунок 4. Пример настройки схемы обработки датчика.
Для присвоения имен служит специальный скрипт автоматики в среде проектирования систем управления, примерно такой, как на рисунке 5. Всем блокам чтения на схеме присваиваются имена, состоящие из имени объекта и имени поля в классе (см. рис. 5).
Рисунок 5. Настройка имени переменных в блоках чтения.
Ясно, что аналогичным образом может быть создано неограниченное количество вариантов обработки сигналов, по сути методов для класса в методологии ООП. Точно так же могут быть сформированы для датчика, его подведение при отображении на видеокадрах SCADA системы, или например обработка процедур изменения уставок. Создается схема в графическом виде, сохраняется виде блока и используется при необходимости.
Подведу итог: в графических языках программирования методы ООП так же применяются и приносят пользу. А после генерации исходного кода управляющих программ, все артефакты методологии ООП, исчезают и остается, чистый С, безопасный, надежный, верифицируемый.
Понятно, что такое применение средств автоматизации кроме ускорения разработки, позволяет так же значительно сокращать время разработки количество ошибок в управляющих программах.
Комментарии (27)
EvgeniiR
13.05.2019 18:40+1В Си есть и инкапсуляция, и наследование, и полиморфизм.
petuhoff Автор
13.05.2019 18:49+1Язык Си не является объектно-ориентированным языком. И значит все что будет описано ниже это костыли и велосипеды.
ООП включает в себя три столпа: инкапсуляция, наследование, полиморфизм. Ниже я покажу как этих вещей можно добиться в С.
habr.com/ru/post/263547Ryppka
13.05.2019 21:17+1C++ начинался с препроцессора CFront, который из кода достандартного тогда еще C++ герерировал код на C. А тот уже компилировался в исполнимый код, как обычно.
Разница между современным C++ и C кажется огромной, но различия между этими языками не стоит преувеличивать. Приуменьшать, впрочем, тоже необязательно...)
EvgeniiR
13.05.2019 22:06+1Так как он не объектно-ориентированный, если он соответствует вашему же определению?)
Тем более, четкого и однозначно принятого определения ООП до сих пор нет.petuhoff Автор
13.05.2019 22:53+1Я так полагаю, что раз встроенных механизьмов реализции методоллогии ООП нет в С, а нужно делать из костылей и велосипедов, то это не ООП. А если стандратные средства поддерживают методологию ООП из коробки, то ООП. Наверное так как то, но это не точно ;)
С точки зрения пользователя СFront это ООП, а С — нет.EvgeniiR
13.05.2019 23:42+1Что вы называете встроенными механизмами реализации методологии ООП и каких механизмов не хватает в Си?
Интересное, кстати разделение, если делать X — удобно, то это парадигма ООП, а если неудобно — не ООП.
По поводу механизмов — наследование структур есть, структуры могут содержать указатели на функции, вспомним, например, STDIN / STDOUT. инкапсуляция в Си из коробки получше чем в современных языках — всё инкапсулировано и скрыто в одном файле, пока вы сами, явно, не опишите, что из этого файла можно будет использовать снаружи. *
С одним фактором по поводу удобства, конечно, соглашусь — полиморфизм стал действительно удобнее с развитием парадигмы. Но разве этим теперь определяется ключевое развитие между парадигмами, удобством использования одного подхода?
* Хоть четкого определения данной парадигмы нет, если полиморфизм и инкапсуляция довольно хорошие механизмы, и с развитием ООП/ООД они стали популярнее и удобнее, наследование отличительной чертой парадигмы я никак не могу назвать
evocatus
14.05.2019 01:50Создатель ООП Алан Кей с вами бы не согласился:
«Я считал объекты чем-то вроде биологических клеток, и/или отдельных компьютеров в сети, которые могут общаться только через сообщения.»
«Мой опыт в математике заставил меня понять, что каждый объект может иметь несколько алгебр, они могут объединяться в семейства, и это может быть очень полезным.»
«Одна из ключевых идей: система должна продолжать работу во время тестирования о особенно во время произведения изменений. Даже крупные изменения должны быть поэтапными и занимать не больше доли секунды.»
«Позднее связывание позволяет с меньшими усилиями встраивать в проект идеи, которые возникли позже в процессе разработки (по сравнению с системами с более ранним связыванием вроде C, C++, Java, и пр.)»
«Я не против типов, но мне не знакома ни одна система типов, которая не вызывала бы боли. Так что мне все еще нравится динамическая типизация.» — Алан Кей за динамическую типизацию.
«ООП для меня это сообщения, локальное удержание и защита, скрытие состояния и позднее связывание всего. Это можно сделать в Smalltalk и в LISP.» — а наследования в Smalltalk не было вовсе.petuhoff Автор
14.05.2019 10:18В системах управления как правило все занчительно проще, с точки зрения типов:
На вход идет число — значение с датчика, на выход тоже число — команда упрвления, все число, даже если это логическое да или нет 0,1. Для упрощения можно считать что все числа — реальные двойной точности.
А «идеи возникшие позже», это разные схемы обработки этих чисел.evocatus
14.05.2019 12:43+2Я отвечал с чисто теоретической точки зрения на вашу реплику про «инкапсуляцию, наследование, полиморфизм». Я не знаю кто это придумал, но в большинстве российских (и, кажется, не только российских) вузов с этих слов начинается разговор про ООП и я не уверен, что это правильное начало разговора. По сути большинство вузов обучает не ООП и не программированию, а C++, Java или C#.
Ярко показывает степень разрыва в мышлении тот факт, что Алан Кей считает LISP «главной идеей в Computer Science», тогда как большинство преподавателей в вузах если и слышали это название, то ничего не понимают в функциональном программировании. И уж совершенно точно в вузах студенты не слышат ни слова про SOLID, про то, что наследование — очень опасная штука и её нужно использовать очень осторожно (о чём написано даже в «банде четырёх»). Там, блин, задают домашние задания на придумывание идиотских иерархий классов. Даже полиморфизм они зачастую неспособны объяснить без наследования.
Когда-то я жалел, что не закончил вуз по программистской специальности. Сейчас — не жалею. Я закончил лингвистический класс гимназии и (спасибо Наталье Михайловне) знаю английский достаточно хорошо, чтобы слушать и читать Роберта Мартина, Мартина Фаулера и пр. в оригинале, а не преподавателей российских вузов, многие из которых вообще ни разу в жизни не занимались профессиональным программированием и переписывают друг у друга из учебников и методичек одну и ту же ересь десятилетиями.petuhoff Автор
14.05.2019 14:00Меня вообще не учили ООП, программированию меня учили в рамках базового первого курса, чисто решение расчетных задач, циклы, интерполяция, интегрирование. Я по образованию инженер конструктор ядерных реакторов, поэтому не могу иметь притензии к своему ВУЗу. ООП я изучал сам, разрабатывая нативные компоненты к Delphi, в качестве источника данных был пользовательсткий мануал на английском языке.
evocatus
14.05.2019 14:05Главное чтобы работало. Но ООП очень большая тема, чтобы её можно было раскрыть в рамках мануала, это одна из трёх главных парадигм программирования на данный момент и было написано уже штук 10 книг, которые можно назвать классическими, притом развитие не стоит на месте, те же принципы SOLID были предложены в начале 2000-х. Да, это просто формальное проговаривание лучших практик уже сложившихся к тому времени, но до этого проговаривания большинство им не следовало (боюсь, сейчас ситуация не сильно поменялась) и в большинстве материалов они не были изложены.
petuhoff Автор
13.05.2019 18:47Пример определения из ООП
Инкапсуляция – это свойство системы, позволяющее объединить данные и методы, работающие с ними, в классе и скрыть детали реализации от пользователя.
Когда я ставлю на схему блок, в примере КД, я как раз получаю классический пример инкапсуляции. Пользователь указывает сигнал с какого датчика он хочет получить в миливольтах, и задает параметры для пересчета и получает значения в единицах измерения, например в град Цельсия.
Классическое из ООП объединение данных и методов их обработки.
third112
13.05.2019 19:52+2Спасибо за статью — очень интересно.
Но с точки зрения всех, кто впервые сталкивался эти этим абстракциями, после классических процедурных языков понятнее не становилось, кажется наоборот все еще больше запутывалось.
Тут похоже опечатка. Но по сути ИМХО от процедурного Паскаля перйти к ОО Паскалю Delphi ( упомянутому в статье) очень просто даже на формальном уровне.
Простейшая инкапсуляция уже есть в записях (record). Далее понятие о наследовании приходит в таких простых примерах:
type TCoord = record // координаты точки x, y : integer end; TRect = record // прямоугольник leftTop, RBot : TCoord; end;
Остается заменить слово «record» на слово «class» (с указанием имени предка в скобках), разрешить записывать заголовки методов внутри таких «записей» и оговорить несложные правила полиморфизма классов.
только чистый Си, и еще желательно урезанный правилами и рекомендация MISRA C, чтобы все было надёжно, переносимо, безопасно.
Тут мне видится историческая несправедливость и непоследовательность. Еще во времена виртовского Паскаля критики указывали на слишком сильную (по их мнению) типизацию, отмечая, что Си — гораздо более гибкий язык. Следовательно на Си возможно больше трюков и, как оборотная сторона монеты, больше ошибок, более трудная читаемость исходного кода. Паскаль был сделан для обучения, Си — для разработки. Логичнее было выбрать для АЭС Паскаль, как потенциально более простой и надежный.petuhoff Автор
13.05.2019 20:24+1Мне тоже типизированный Паскаль нравится больше.
Но с Си на контроллерах управления, мне кажется это исторически сложилась такая практика, контроллеры более слабые, чем десктопные процессоры, поэтому гибкость Си позволяла реализовать «всякие трюки», а потом в критических системах это осталось, типа работает — не трогай. Вчера сделали на Си работало значит и сегодня будем продолжать тоже.third112
13.05.2019 20:40+1Со времен PDP-11 (а м.б. раньше) в Паскале был один супертрюк, который для любой самой глупой железки позволял записать любую инструкцию — встроенный ассемблер :) И со времен PDP-11 (и управляющих различной аппаратурой LSI-11) этот подход себя зарекомендовал: программа была написана в основном на Паскале, и только небольшие куски на ассемблере. ИМХО это надежнее, т.к. наглядно видно какие регистры для чего использованы, какие вектора прерываний и т.д.
third112
13.05.2019 20:25+1PS В этой статье ИМХО точнее сказать не графический язык, а визуальное средство разработки. Аналогично: ОО Паскаль не графический ЯП, но IDE Delphi — визуальное средство разработки.
petuhoff Автор
13.05.2019 20:33+1В стандарте MЭК 60880 «Атомные станции. Системы контроля и управления, важные для безопасности. Программное обеспечение компьютерных систем, выполняющих функции категории А». Используется понятие
Проблемно-ориентированный язык (application oriented language): компьютерный язык, специально разработанный для определенного типа применений и используемый лицами, являющимися специалистами в данном типе применений. [МЭК 62138, 3.3]
В этом же стандарте есть понятие
Автоматизированная генерация кода (automated code generation): функция автоматизированных инструментов, позволяющая преобразовывать проблемно-ориентированный язык в форму, пригодную для компиляции или выполнения.
Таким образом схему графического языка SimInTech вполне можно рассматривать как программу на проблемно ориентированном языке. (графическом языке программирования)third112
13.05.2019 20:43+1Со стандартом не поспорить :) Раз так написано — так тому и быть.
petuhoff Автор
13.05.2019 22:57+1Тут дело в том, что схему алгоритма можно запустить на исполнение без генерации года Си. Графический язык SimInTech может выполнять расчеты, сам по себе. А IDE Delphi все переводит в ОО Паскаль, а потом только компилирует и выполняет. Как то так.
И еще, в IDE Delphi функции все таки нужно писать на Паскале. А в графическом языке — не надо, можно все нарисовать.
lingvo
14.05.2019 10:17Логичнее было выбрать для АЭС Паскаль, как потенциально более простой и надежный.
Не для АЭС, но для автоматизации производственных процессов так и сделали. Для программирования ПЛК часто используют ST (IEC 61313-3), который очень похож на Паскаль.
Но при этом этот же ST компилируется перед выполнением и возможно одной из промежуточных форм опять таки является Си.petuhoff Автор
14.05.2019 10:45Из приведенной схемы управления SimInTech, можно сгенерировать и ST. ООП в графическом языке SimInTech, а код сгенерируем ST.
funca
14.05.2019 16:04+1Обычно интересен язык на самом высоком уровне. На котором будет думать программист. Дальше программа интерпретируется, транспилируется, компилируется, линкуется, проходя множество представлений и в конце превращаясь в наборы нулей и единиц.
petuhoff Автор
14.05.2019 17:05Вот программист и думает в виде расчетных схемы SimInTech и объектов проекта. Хотя в случае АЭС это даже не програмист, а технолог. А программист вообще не нужен.
lingvo
Я бы не назвал это ООП.
Это просто типичный способ упрощения работы дизайнеру алгоритма за счет того, что создается база данных сигналов под конкретный объект управления. Раньше это все писалось ручками и можно было легко сделать неуникальную переменную и получить проблемы.
Объекты — это часто физические компоненты — моторы, кнопки, задвижки и т.д и они импортируются из принципиальной схемы. Чтобы не возникало путаницы с сигналами, все эти объекты автоматически получают набор входных и выходных сигналов и параметров, которые имеют уникальные имена и которые потом кодогенератор может преобразовать в глобальные переменные. В этом и есть смысл базы данных сигналов. Никаких инкапсуляций, никаких классов, все направлено на то, чтобы скрипт, указанный в конце вашей статьи, сгенерировал нужный файл, в котором будут представлены все десятки тысяч сигналов, используемые в реально исполняемом коде.
Как верх стандартизации вы можете глянуть на IEC 61850, где четко прописано как все это дело должно работать.
petuhoff Автор
Да принцип работы именно такой.
Но то что это совсем не ООП не соглашусь. Например:
Инкапсуляция – это свойство системы, позволяющее объединить данные и методы, работающие с ними, в классе и скрыть детали реализации от пользователя.
Когда я ставлю на схему блок, в примере статьи КД1, я как раз получаю классический пример инкапсуляции. Пользователь указывает сигнал с какого датчика он хочет получить в миливольтах, и задает параметры для пересчета и получает значения в единицах измерения, например в град Цельсия.
Классическое из ООП объединение данных и методов их обработки.
lingvo
А где здесь методы?
Насколько я знаю эти вещи, кроме пересчета — т.е. умножения/деления на константу + оффсет + ограничение по максимуму/минимум, эта база данных не делает. Т.е. любой сигнал, по умолчанию проходит через этот пересчет и вы, если хотите, можете привести милливольты в Цельсии или напряжение в Per Unit. Но это все!
petuhoff Автор
Здесь «метод класса» в терминах ООП, это схема внутри блока. В этой схеме можно реализовать любую логику обработки. Сама схема привязана к конкретному наименованному набору данных. Инкапсуляция — блок содержит схемы и набор данных.