Ох, эта статья будет ещё более сумбурная, чем они бывают обычно у меня. Просто есть какие-то мысли и надо их немного устаканить.
Есть такой человечище, зовут Мартином. Когда я ещё только играл в 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. И покуда перенёс в блог, наткнулся ещё на одну интересную статью про проектирование Проектирование по вытягивающему принципу. Моя практика это только подтверждает, вначале надо что-то реализовать попроще, напрямую, перед тем как понять, как из этого сделать универсальное решение и потом зарефакторить в удобный салюшен.