Ох, эта статья будет ещё более сумбурная, чем они бывают обычно у меня. Просто есть какие-то мысли и надо их немного устаканить.
Есть такой человечище, зовут Мартином. Когда я ещё только играл в HoMM 3, он уже написал статью про анти-паттерн AnemicDomainModel. И не смотря на то, что я запоем читал некоторые его книги и статьи, с этим я совершенно не согласен.
Начнём, пожалуй с главного с ООП, оно же OOD. Вся проблема в том, что в последнее время слишком много читаю статей/книг, слушаю подкастов, о функциональном программировании. И это стало сказываться. Всё больше хочется писать не в описанном Мартином ООП стиле. Но, для начала в целом откажемся от использования этого трёхбуквенного сочетания далее по тексту. Почему? Всё очень просто. Agile :) Да именно он мне в области управления проектами напоминает мне то, что твориться в программировании с этим словосочетанием. Проблема в том, что есть какие-то гуру/консультанты, которые начинают рассказывать магические правила и все типа начинают им следовать и наступает всемирное счастье. Но счастье так и не наступает... Вот именно поэтому мне не нравиться это словосочетание. Это больше напоминает религию/секту/культ, чем действительно что-то полезное. Кстати ругать и писать статьи на тему провала ООП (так же как и Agile), так же невероятно популярно. Но есть и хорошие вещи. Например вот эта беседа с разработчиком Erlang'а (он кстати считает, что сделал ООП язык :))) или более броская заметка Кризис объектно-ориентированного программирования.
Поэтому буду говорить просто о подходах к дизайну программ, как я себе это вижу. Сразу предупреждаю, необходимо делать скидку, что, как работой, программированием я занимаюсь всего три года. Так что никаких откровений и скорее всего много глупостей, может лет через 10 соглашусь с Мартином, но пока нет.
Вырвем одну фразу из контекста
The basic idea of object-oriented design; which is to combine data and process together.
Трактовать можно по разному, тем более у Алана Кея, такого не было. Фразу похожую этой я слышал много раз. Что, методы обрабатывающие какие-то данные должны быть в том же классе, что и данные. Часто любят вспомнить инкапсуляцию. Это всё конечно было бы хорошо, если бы всё время я не натыкался на проблему, где бы разместить тот или иной метод, потому что он вроде бы и должен быть тут, но ему ещё нужны и те данные. Ох, запутано получилось, поэтому просто украду пример.
ручка.писать(бумажка, текст) VS бумажка.писать(текст, ручка)
Я не знаю ответа на этот вопрос.
Но я прекрасно понимаю, что в конце концов ручка.писать(бумажка, текст) например в терминах той же объектной Java, это просто раскрывается в писать(ручка, бумажка, текст), где вместо ручка, просто this.
В этом по моему главная проблема, что просто внести метод писать в класс ручка, не решит никаких проблем, в том числе и инкапсуляции.
Ещё один пример из мира Java (в C# с этим получше и я поддерживаю их идею). В Java все методы виртуальные, поэтому я могу в наследнике переопределить любой метод, за исключением тех случаев, когда на него будет навешан final (или вообще на весь класс). Кто-нибудь использовал Sun'овскую конфигурацию для Check style? Там есть одно правило, в разделе проектирование. Что все не абстрактные методы должны быть финализированы. И ведь действительно, кто-нибудь переопределяя ваш метод, может и не вызвать super.писать и что-то отвалиться. Вообщем первая банальность на сегодня, надо думать и много проектировать, рефакторить, чтобы работала инкапсуляция и c помощью наследования не прострелить ногу. Как тут не вспомнить букву O из S.O.L.I.D. Но то что написал Мартин не имеет к этому никакого отношения. На всякий случай ещё упомяну одну вещь, которую мне пытались вдолбить в университете как часть ООП. Полиморфизм в большинстве языков, которые себя причисляют к ООП языкам смешон, посмотрите на Haskell, чтобы узнать что такое настоящий полиморфизм.
Ох.. Куда-то меня понесло, но в этом был смысл. Так как о Анемичная модели/архитектуре особо писать не имеет смысла, со времён статьи Мартина написано предостаточно. Один пример из жизни, который работает. Есть концепция интеграции данных ETL. Она достаточно простая и поэтому предостаточно разных тулов, которые её реализуют, я тоже один такой пытался написать на 4 курсе и ещё один сейчас пытаюсь сделать. Так вот. Например если посмотреть на большинство из них, это просто pipeline. Т.е. есть понятие анемичного Документа и набора компонент объединёных в общий flow, которые между собой его гоняют и трансформируют. И это работает! Причём компоненты можно в отдельных потоках запускать и это будет работать! Но это анемичная модель. А как насчёт Linq в C#? Надеюсь уже не трудно догадаться к какому банальному совету я веду. Хватить быть религиозными фанатиками, если работает анемичная модель, пусть работает. Но и так же с другой стороны не надо её всюду пихать. Например посмотрим на функциональные языки, большинство из них не такие упёптые и позволяют использовать императивный подход, потому что прекрасно понимают, что надо дать человеку возможность что-то сделать ещё и немного по другому. И пример C# думаю должен остальных вдохновить на переосмысление своих религиозных взглядов. Практика, лучший критерий истины.
Ах.. да. Эта заметка в виде набора ссылок и мыслей долго валялась в Evernote. И покуда перенёс в блог, наткнулся ещё на одну интересную статью про проектирование Проектирование по вытягивающему принципу. Моя практика это только подтверждает, вначале надо что-то реализовать попроще, напрямую, перед тем как понять, как из этого сделать универсальное решение и потом зарефакторить в удобный салюшен.
Отличненько, отличненько. Пиши ещё, Паша.
ОтветитьУдалитьОтличненько, отличненько. Пиши ещё, Паша.
ОтветитьУдалитьАнемичная модель не так уж сильно противоречит ООП, что бы по этому поводу не говорил уважаемый дядька Фаулер. Если ранее не читали, то советую: http://www.gotdotnet.ru/blogs/bezzus/1224/
ОтветитьУдалитьДа, хорошая статья. И комментарии интересные.
ОтветитьУдалитьВообщем, я всё таки согласен с тем, что анемичная модель не соответствует каким-то абстрактным представлениям о ООП. Но во многих случая она великолепно работает и не стоит от этого отказываться.
Я недавно листал Clean Code от Robert Martin. И там по моему в 6той главе о данных высказываются аналогичные мысли.
Суть такова. Если у нас часто меняются данные, именно структура данных, то конечно богатая модель рулит. Там был как раз простой понятный пример с геометрическими фигурами. Если надо постоянно добавлять новые фигуры, то проще, когда все нужные методы: периметр, площадь находятся в них. Новую фигуру легко добавить, определив лишь соответствующий класс.
Но очень часто данные у нас постоянные, зато меняется логика, появляются новые алгоритмы и тогда анемичная модель выходит на первый план. Так как, чтобы добавить новый алгоритм не нужно обновлять стопитсот уже написаных фигур, а нужно добавить/обновить лишь один метод (или перегруженный набор методов) реализующий этот алгоритм.
Так что в свой статье я просто хотел сказать, что пора переставать действовать шаблонно. Если какой-то подход отлично работает, то не стоит отказываться от него, только потому что он якобы не кошерен :)
Этот как с Agile, большинство команд, которые пытаются в тупую строго следовать каким-то заповедям agile-консультанта, ничего хорошего не добиваются. Надо быть гибче и подстраивать инструмент по себя :)