воскресенье, 29 мая 2011 г.

DCI: Data-Context-Interaction

Введение
Пару-тройку недель назад попалась ссылка на интересный подход к проектированию называющийся DCI, сформулированный создателем  MVC.

В кратце его можно передать так: все, что сейчас разрабатывается не ООП, а Class Orientied Programming.
И на самом деле должно быть разделение не на объекты, которые умеют как-то менять свое состояние, а на данные + роли, т.е. то, как разные классы данных взаимодействуют.
Ведь в сущности объект человек это набор ролей: отец, брат, продавец, муж и т.д.

Описание
Во время выполнения программы или любого алгоритма 1 объект может выступать в разных ролях и попытка впихнуть весь этот функционал в класс приводит к жуткой мешанине кода, тесной связность и вообще страданиям, страху и ужасу).

Вместо этого классы доменной логики, сами по себе должны быть эдакими "тупыми" объектами.
Например, объект Банковский Счет должен знать, что у него есть баланс, и он может его повышать и понижать, но только в рамках определенной роли и определенного контекста, к нему добавляется логика, например
1)возможность или невозможность залезть в долг при выдаче наличных,
2)возможность перевода денег на другой счет, при наличии достаточного количества на счету
3)возможность оплатить деньги всем кредиторам.

Все 3 предыдущих пункта - роли, играемые данным счетом в определенном контексте, и например во 2м варианте , этот же счет может играть роль "принимающего" счета.

Фактически любая программная система это динамический набор объектов, которые принимают то одну то другую роль за время своей жизни в рантайме и концпеции типа MVC, позволяют через сеть слоев соединить ментальную модель пользователя с настоящей vjltkm. работы системы.
Но во время разработки программист работает, с ментальной моделью статичной программы, в которой есть классы А И Б, которые умеют действий В и Г, а не с динамической моделью, что вот в этом месте класс А впитывает в себя роль принимающего счета, или брата, или чего-либо еще.
Подход к разработке с использованием парадигм DCI, пытается смягчить "impedance mismatch" (как это по русски-то называется ?)) между моделью системы в работе(runtime), и моделью ее во время разработки(compiletime),  что должно послужить повышению читаемости, понятности и предсказуемости поведения программ.

Итого

Чем-то этот подход и сама идея незримо напоминает AOP (который слишком сложен и неудобен и потому не получил особого распространения) и очень даже напоминает идеи mixin-нов, trait-ов и соответственно аспектов (которые мало-помалу вкрадываются во все новомодные языки и предают им синтаксической и концептуальной силы).

С одной стороны вроде получается боян, по причине "вот же оно - уже было".
С другой стороны если посмотреть, как обычно о них рассказывают, то нет именно этого акцента на участие роли в определенном контексте взаимодействия, и отсутствие четких стратегий смешивания этих самых миксинов и ролей.

В общем-то видно, что интуитивно идея постепенно просачивается в программерское сообщество с тенденциями типа DDD, mixin`ов и trait`ов, но возможно с другим именем, и пока только кусками.

Источники
Тексты/Статьи:
1)http://en.wikipedia.org/wiki/Data,_Context_and_Interaction - статья в википедии
2)http://heim.ifi.uio.no/~trygver/themes/babyide/baby-documents.html - набор ссылок от автора
3)гугл =)

Видео:
1) http://oredev.org/videos/dci-in-practice(качественная речь, на слух воспринимается до скорости 1.3-1.4х)
2) James Coplien - The DCI Architecture: Supporting the Agile Agenda (качественная речь, до 1.6х скорости воспринимается)
3) Trygve Reenskaug - DCI: Re-thinking the foundations of object orientation and of programming
(у него жуткий акцент, очень сложно воспринимаемое на слух видео)

11 комментариев:

  1. Как я понимаю, Rails 3 насквозь пропитаны этой идеологией, и код при разработке на них модно писать именно так. Т.е. идея уже просочилась, и, вроде даже, стреляет :)

    ОтветитьУдалить
  2. Ну в динамических языках такое полегче будет провернуть, в сях придеться через шаблоны что есть ужас)

    Т.е. уже щас пишут вот в таком стиле ?

    class AddToCartContext
    def initialize(user, shop, product_id, quantity)
    //do smtng
    end

    def execute()
    @shop.extend CatalogRole
    @user.extend BuyerRole
    product = shop.find_product(@product_id)
    user.add_to_cart(product, @quantity)
    end
    end

    + Для первых-вторых рельс вроде было нормально, что логика валидации (и бизнес логика и логика корректности сущности/данных) вносилась в модель ?

    ОтветитьУдалить
  3. скорей так https://gist.github.com/997823
    Cart это типа и есть context.

    На счет валидации: сам процесс валидации вынесен в модуль ActiveModel::Validations (т.е. это роль объекта -- быть валидируемым).

    В том, что сама модель знает, как быть валидируемой -- тоже вроде бы нет ничего страшного: модель это данные в роли валидных данных. Хотя по идее, такая отговорка не особо нужна, вряд ли имеет смысл писать что-то, что соответствовало бы DCI на всех уровнях абстракции.

    Большей частью все происходит мягко и неявно, потому что если явно биндить везде данные с их ролями, выйдет совсем петрушка.

    Главный профит здесь -- это имхо разнос логики таким образом, что классы(модули) и методы знают и реализуют только то, что необходимо.

    ОтветитьУдалить
  4. impedance mismatch - дословно "несогласованность по сопротивлению", термин из электротехники.
    В software engineering это значит несогласованность моделей (например объектной модели и реляционной при рассмотрении ORM-ов)

    ОтветитьУдалить
  5. А в целом по предлагаемой идее (судя по твоему посту, сейчас гляну первоисточник) я вижу ООП со строгой динамической типизацией.
    Мы настолько привыкли к ООП со строгой статической типизацией (иногда встречаем нестрогую статическую), что считаем, что это и есть "настоящее" ООП :) А подстилей ООП много :)

    ОтветитьУдалить
  6. Ага только получается, что User во все возможные места все равно таскает с собой методы от Buyer`а, даже там где методы должны быть от другой роли.

    Вот такой пример надумал, корявый конечно, но показывает, что статический линкинг(include Smtng) этих методов может привести к не совсем хорошим последствиям:

    https://gist.github.com/997931

    ОтветитьУдалить
  7. Дочитал. Да, элегантная идея.
    Но только это не классический ООП, т.к. мы разделяем данные и поведение.
    Надо будет попробовать :)

    Кстати, предтечей можно считать DDD в варианте anemic domain model.

    ОтветитьУдалить
  8. Суть коллизии понятна, но я привел пример наиболее употребительного способа применения идей, не претендуя на универсальность и чистоту.

    ОтветитьУдалить
  9. кстати, применительно к руби, для чистоты все можно подсластить и сделать, чтобы было что-то вроде

    user.as(Buyer).can_pay

    но так никто не делает :)

    ОтветитьУдалить
  10. @ekr, ага отдаленно похоже, и избавляется от недоcтатков, типа:
    невозможности коде-реюз,
    невозможности проверить корректность домаин обьекта,
    нарушения энкапсуляции и т.д.

    Это получается DDD поделенное на две абстракции: модели(доменные состояния) + взаимодействия этих состояний и изменения(что собственно автор где-то в одной из статей там подметил).

    И нет такой проблемы как в DDD:
    к какой модели принадлежит use case "покупка товара" к магазину или пользователю ?.

    ОтветитьУдалить
  11. 2Илья:

    DDD в варианте с анемичной моделью как раз разделяет сервисы (процессы, поведение) и доменные объекты (данные). Т.е. формально это тоже не ООП в классическом виде :)

    Если замеппить DCI на anemic DDD, то получается, что DDD-сервис это С+I, а DDD-domain model это D.

    ОтветитьУдалить