January 8th, 2012

flow

Многоликие данные

Программисты (особенно начинающие) любят с размахом писать код. А я люблю этому препятствовать. Ибо каждая тысяча строк ведёт к дополнительным расходам на отладку, поддержку и рефакторинг, особенно если может быть выкинута или заменена чем-то более простым и компактным. Основными источниками мусорного кода являются конвертеры между разными моделями данных и адаптеры между разными API. Очевидное решение этой проблемы - обеспечить общую модель данных и общие средства доступа из разных подсистем. Но делать это надо хорошо, иначе получится трэш вроде windows registry.

Делать общие модели данных умеют разработчики САПРов и таких милых сердцу продуктов как 3DS Max и Maya. Именно оттуда можно взять атрибуты (обычно "attributes" или "custom attributes") в качестве основного механизма работы с произвольной информацией. Технически похожее можно встретить и в других местах (аннотационные атрибуты в XML/DOM, "attached properties" в WPF, ...), но основная идеология там другая.

От обычных "свойств объекта" такие атрибуты отличаются глобальностью. Т.е. вот в списке деталей есть entry123, где entry123.label хранит информацию о том, какой ярлык печатать/клеить, а label это свойство, которое описано в классе Entry со своим хитрым типом. А у кнопочки в пользовательском интерфейсе есть mybutton.label="OK", где label это свойство строкового типа в классе Button. Одинаковые названия, разный смысл, разное применение. А для тех мест, где смысл одинаковый, приходится подбирать иерархии наследования, где-то выносить label в UIElement, а где-то не выносить, потому что вроде label такой-же, а другие критерии UIElement не соблюдаются. Постепенно приходя к оверинжинирингу и ужасам вроде INamed, ILabelled, etc. Потому что свойства малопригодны для таких задач. Надо использовать атрибуты.

Тогда вместо локального свойства label вводится глобальный атрибут vis_label. Тип - строка. В языках со статической типизацией прописывается явно, в динамических подразумевается. Правила использования: если требуется визуализация названия какой-либо сущности пользователю, то для этого запрашивается атрибут vis_label, и визуализируется его содержимое; в случае отсутствия используются иные правила, специфичные для подсистемы. В какой-то мере duck typing. Для документирования атрибута может оказаться достаточно одной строки таблицы, а его использование в коде функций и методов легко отслеживается автоматикой.

В коде проекта .15926 Editor (на данный момент) vis_label распознаётся для:
команд
меню
окон редактирования
элементов деревьев
диалогов
и даже для самого приложения

Причём, часть является экземплярами, а часть просто классами, благо в систему типов Python это прекрасно помещается.
в классах пишем vis_label = 'Save as...'
в экземплярах self.vis_label = label
получаем как getattr(obj, 'vis_label')
или obj.vis_label (в этом случае не забывая про исключение при отсутствии)

В языках со статической типизацией пришлось бы чуть иначе:
obj.set(vis_label, "value")
или vis_label.Bind(obj, "value")
...
С потерей некоторых фишек, но приобретением других полезных качеств за счёт систем типов.

У атрибута всегда должен быть тип, который указывается в коде и/или документации разработчика. Строка, целое число, "значение в килограммах от 0 до 150 в виде числа с плавающей точкой" или расплывчатое "любая другая сущность с атрибутами". Но нет смысла ограничивать сферу применения атрибута, заявляя что physical_mass может быть только у сущностей типа SomethingPhysical и используя его как обычное свойство. Потому что тогда сразу теряется возможность гибкой работы с идентичностями. Об этом далее.

В .15926 Editor кроме vis_label используются и другие атрибуты, например, vis_icon (тоже текстовая строка) - идентификатор иконки для команды меню, элемента дерева, etc. Можно продолжить и ввести какой-нибудь vis_color, если вдруг на дереве понадобятся разноцветные элементы.

И вот на базе этого кода появляется... например, какая-то презентация по крупным городам России. Справочные данные, статистика, динамика населённости, динамика застройки.

spb.vis_label = "Санкт-Петербург" # имя
spb.vis_icon = "saint_petersburg_flag" # однако, у города есть иконка... на самом деле флаг
spb.vis_color = "#FF00FF" # и цвет
В этот момент появляется стадо нервных онтологов с возгласом что "у города не может быть цвета, иначе у них религиозный траур и Аристотель с Абсолютом не сходятся". На что отвечается, что идентичность, выражаемая в указанном фрагменте как spb, используется и как "город Санкт-Петербург в его географическом значении", и как "город Санкт-Петербург в его административном значении", и как "город Санкт-Петербург для рисования его статистики на графиках фиолетовым цветом", и ещё много в каких других. Ведь в жизни, в текстах и прямой речи происходит то же самое. А если дробить идентичности без веской причины, то выползают неисчислимые MyFactoryAdapterInterfaceContextProxyLauncher.

Разумеется, тут возникает вопрос, что делать, если для визуализации трендов хочется один vis_color, а для отрисовки имени на дереве другой. Или разные vis_label для разных языков (i18n). Другая важная тема - как выбирать между простыми структурами, свойствами и глобальными атрибутами в разных случаях.

Об этом, способах сочетания разных контекстов/реификаций внутри системы, а также информационном обмене и интеграции данных между разными системами - читайте в следующих выпусках. Будет рассматриваться и архитектура программного обеспечения, и особенности ISO 15926 и Gellish, и традиционные факапы W3C.