Евгений Каратаев
Иногда так бывает - соберутся люди и начинают обсуждать серьезные проблемы. И устают уже, а все равно продолжают. Остановиться уже не могут, потому как несерьезно получится. И мысли уже кончаются. И тут кто-то говорит шутку и все смеются и ситуация разряжается. Как такая шутливая разрядка и появилась эта работа. Несмотря на шутливость и кажущуюся несерьезность, доля интересного и полезного в ней есть.
Как проектировать классы?
Классы есть определения понятий и отношений между ними, выраженные на каком - либо объектном языке. Когда говорят, что объекты - это ново, модно и все такое, мне это трудно понять, поскольку ничего кроме объектов никогда не писал. Скорее уж программирование без объектов - вот это действительно новость.
Чтобы спроектировать классы, следует выполнить по шагам следующий рецепт. Описание рецепта будет иллюстрироваться на настолько банальном примере, чтобы его нельзя было использовать в реальной работе. Как это и принято в настоящих компьютерных публикациях.
Шаг 1
Следует взять и потрясти заказчика (или самого себя, если сам все придумал) на тему получить побольше описания темы на русском языке. Надо именно на русском. Пусть пишет. Или даст статью в журнале. Тоже бывает. Увидит шеф статью и загорится на тему "а мне тоже надо". После этого следует просмотреть текст и выбросить из него все, что к делу не относится. После чего дописать к нему то, что не упомянуто, а по идее должно быть. После таких исправлений русскоязычного текста его следует перечитать и немножко погордиться, поскольку первый этап выполнен.
Пример, что может получиться в результате:
В лесу родилась елочка, в лесу она росла.
Зимой и летом стройная, зеленая была.
Вот так вот, грубо и зримо, будем выделять то, что относится к разбираемому примеру.
Шаг 2
Второй шаг состоит из того, что выписываем отдельно слова, являющиеся существительными, прилагательными, глаголами, союзами и другими частями речи. Кто давно ничего не писал на других языках кроме C++, Object Pascal, Java или других, тот может проконсультироваться у своих младших товарищей, какое слово чем является. В нашем примере получится:
существительные: лес, елочка, зима, лето
глаголы: родиться, рости, быть
прилагательные: стройная, зеленая
союзы: и
местоимение "она" позорно отнесем к существительным, а предлог "в" - к союзам.
Шаг №2 закончен.
Шаг 3
Третий шаг заключается в таком же , как и второй, монотонном переписывании исходного текста. Теперь переписываем по-другому.
Каждому имени нарицательному ставим в соответствии имя класса и называем его наследником некой иерархии.
Каждому имени собственному ставим в соответствие объект как экземпляр некоего нарицательного класса. В нашем примере имени собственного нет, поэтому я, насупив бровь, могу смело заявить, что разбор примера с именем собственным есть домашнее задание и что вообще "легко видеть, что".
Каждому прилагательному ставим в соответствие класс и называем его базовым. Еще лучше - абстрактным. Если язык позволит. Тем, кто пишет на Java, вообще повезло - прилагательным следует сопоставлять интерфейс в том виде, в котором он определен в Java.
Идем дальше - как только видим глагол, смело создаем функцию, поскольку глагол есть действие, переводящее одно в другое либо что-то куда-то передающее.
Союзы есть сигнал о существовании предикативных условий, если участвуют в исходных материалах действия или о существовании наборов данных, если участвуют в выходных материалах действия.
В нашем примере получим:
Базовые классы:
TСтройный, TЗеленый.
Классы - наследники, которые можно завести:
ТЛес, ТЕлочка, ТЗима, ТЛето.
Функции:
Родиться, Рости, Быть.
Союз "и" однозначно указывает на наличие набора.
Этот шаг самый простой в техническом плане и может быть выполнен автоматически программой проектирования классов, если эта программа располагает достаточно большим словарем. Одновременно с этим этот шаг наиболее насыщен идеологически - его выполнение делает процесс проектирования необратимым, перешедшим некий виртуальный рубикон и исключающим существование вероятных реальностей. Во сказал - даже самого себя заинтриговал.
Почему так? По приводимой идее проектирования исходный текст сам в себе содержит некоторое количество определений, если он выражен на неком понятном человеку языке. Русский язык достаточно богат в плане выразительных возможностей и суть методы стоит в переводе уже сформулированных определений и отношений с одного языка на другой, что и есть программирование, если целевым языком является машинный язык.
Прилагательное отображается на абстрактные базовые классы, поскольку прилагательное по своей сути есть признак, характеризующий нечто таким образом, что если прилагательное относится к этому нечту, то это нечто есть реализация этого прилагательного. Например, если есть прилагательное "объемный", то завести такого объекта нельзя. Но при этом "объемным" могут быть другие классы (существительные, имена нарицательные). "Объемным" может быть "ящик", "изображение", "понятие". Любое из приведенных существительных может быть использовано в качестве реализации прилагательного "объемный". Если некоторое действие оперирует понятием "объемность", то в качестве его аргумента могут быть использованы перечисленные существительные. Примерно так поступают механизмы COM - оперируя объектами, механизмы никогда не передают описателя объекта, всегда передается только описатель реализованного интерфейса. Один объект может реализовать несколько интерфейсов одновременно - например, объект может иметь признаки "объемный", "цветной", "прочный" и так далее одновременно.
Существительные, имена нарицательные, отображаются в классы, по которым реально могут быть заведены объекты. Основным отличием существительных есть понятие "существование". Если нечто является существительным, то это нечто может существовать или не существовать. В программировании прямым аналогом является вписывание в архитектуру классов однозначно выделенного класса, отвечающего именно за возможность заведения объекта. В Delphi это класс TObject, в MFC - CObject, в Java - тоже что-то типа Object. Классы, которые могут иметь представителей (по которым можно создать объект), практически всегда есть наследники такого рода базового класса с добавлением собственного поведения и / или с множественным наследованием ранее объявленных интерфейсов. При этом интерфейс базового объекта, скажем TObject, реализует именно и только интерфейс существования. Для некоторых средств программирования такой объект действительно выглядит как некое почти материальное ядро, обрастающее другими интерфейсами. Но это необязательно так. Например, в том же COM функции существования выполняются через интерфейс IUnknown.
Существительные, имена собственные, отображаются на объекты как экземпляры неких классов. Имя как в русском языке, так и в языке программирования играет одну и ту же роль - указать среди набора объектов один из них. Объекты именуются и указываются путем указания имени. Здесь для понимания методы, надеюсь, ничего сложного нет.
Глаголы отображаются на функции. Функции есть реализация алгоритма, последовательности неких действий. Это не данные и не определения. Функции отличаются от иных понятий тем, что они выполнимы и при выполнении воздействуют на что-то. Функция может что-то преобразовать, перевести объект из одного состояния в другое, либо передать информацию без изменений. Прилагательное характеризуется набором глаголов. Верно и обратное - набор глаголов определяет прилагательное, поскольку понятие определяется набором отношений, в котором оно участвует. Например, прилагательное "объемный" может характеризоваться набором глаголов "вмещать", "сравнить". При этом, что интересно, если человеку перечислить набор глаголов, который характеризует некое прилагательное, то он достаточно быстро отгадает это прилагательное. Набор глаголов, входящих в описание прилагательного, есть те ниточки, за которые можно дергать объект, к которому это прилагательное относится. Глагол во многих случаях программистами не относится явно ни к какому прилагательному, и при описании класса программист просто наследует класс TObject и добавляет ему функции. На самом деле это так же есть определение прилагательного, но, если можно так сказать, явно не именованного. При этом программист мог бы существенно выиграть, если бы добавляемые функции явно оформил бы в виде интерфейса. Пригодилось бы на будущее.
Наличие союзов вида "и", "или", "не", "либо" явно свидетельствует о существовании предикативных условий или наборов данных. Если союз связывает аргументы некоего действия, то это формулировка предиката, если результата действия - то формулировка набора.
Как несложно видеть, приводимая метода проектирования классов чрезвычайно похожа на программирование на языке Пролог. Когда есть исходное описание прикладной области и задача программиста состоит только в том, чтобы чуть-чуть переписать исходную задачу, привести текст в соответствие с правилами описания определений и фактов языка программирования, конкретного диалекта.
Шаг 4
Шаг состоит в том, чтобы из набора полученных на третьем шаге отображений составить иерархию классов. Глаголы следует распределить по прилагательным, либо явно добавить к существительным. Существительные следует, если это необходимо, наследовать от прилагательных либо от других существительных. Имена собственные должны получить класс и быть оформлены в виде объектов.
Итак, о глаголах. Чтобы отнести глагол к прилагательному, следует внимательно прочитать исходный текст на русском и определить контекст действия глагола. Выяснить, какое прилагательное он характеризует. В нашем примере глаголы "родиться" и "рости" являются неименованными прилагательными и относятся к существительному "елочка". А глагол "быть" относится к прилагательному "цветной" и "фигурный".