Имена или семантика классов в программировании

    1. При программировании на придумывание имен может уходить ощутимо много времени. Казалось бы, такая маловажная вещь, как имя. И вот только сегодня я осознал, почему я трачу на это время, и почему, в итоге, это не напрасная трата времени.

    Придумывание имени для модели, класса (предметной области) — важно и при проектировании, и при кодировании. Ведь имя несет с собой огромное облако ассоциаций из реальной жизни, из других областей.

    Выбор имени — это один из этапов проектирования, когда какие-то сложные вещи — операции, объекты, отношения, скрываются уровнем абстракции. То есть скрываются за назначенным именем.

    Если имя выбрано неудачно и оно с искажениями отражает то, чем является модель, оно будет мешать не только тем, кто в будущем будет читать код и документацию. Оно мешает непосредственно сейчас при проектировании, сбивает с толку. Неудачное имя мешает детализировать модель, мешает продумывать отношения этой модели с другими моделями или с пользователем.

    Как вы яхту назовете, так она и поплывет.

    2. Свежий личный пример. Пишу код для запуска предустановленных виртуальных машин у различных облачных провайдеров. Сначала у меня получилось, что моделью, то есть объектом, который юзер настраивает и создает пошаговой формой, в которой потом хранятся результаты установки, и с которой пользователь потом может выполнять другие действия, сначала стала Instance — экземпляр виртуальной машины. Такое название тянуло за собой лишние для этой задачи свойства — хранить историю изменения состояний, отслеживать актуальное состояние инстанса у провайдера и показывать его в юзерской панели. Их можно было не реализовывать, но они все-время маячат на фоне, т.к. это общепринятое поведение инстансов. В то время как исходная задача очень простая и минимальная — запустить инстанс, установить и настроить на нем софт, и выдать пользователю результат.

    Название не нравится, модель сужена до LaunchedInstance, то есть «запущенный сейчас экземпляр виртуальной машины». Такое ограничение отсекает лишние понятия, но у этого имени все еще много ассоциаций, отвлекающих от основной короткой задачи.

    В итоге правильное имя для модели стало Launch, т.е. непосредственно операция запуска. Методы и атрибуты класса остались остались прежними, изменилось только название. Но оно теперь содержит в себе только необходимый минимум и не несет избыточной смысловой нагрузки. После этого стало проще развивать логику работы класси и организовывать взаимодействие с другими компонентами проекта.

    3. Продумывание имени — это непосредственная часть проектирования. И если вы не можете подобрать удачное слово и чувствуете неудовлетворенность, то скорее всего вы либо плохо продумали модель и она не реализует то, чем вы ее назвали, либо плохо продумали эту часть проекта и нужна вообще другая модель.

    Время, потраченное на выбор имени, тратится не зря. Его можно воспринимать как самое ранее тестирование модели — некий интеграционный тест, проверяющий, правильно ли разработчик представляет задачу.
    Share post

    Comments 41

      +1
      Если имя выбрано неудачно и оно с искажениями отражает то, чем является модель

      Вы словом "модель" неудачно назвали какую-то сущность (в широком смысле слова, похоже один класс) с которой оперируете. Модель — это совокупность сущностей, моделирующих предметную область. В вашем случае, в моделировании запуска виртуальной машины, похоже, модель заключается в команде/сервисе запуска с параметрами.

        0
        Здесь модель — это термин из MVC, например, при использовании Ruby on Rails. То есть это класс, отражающий какое-нибудь важное понятие или предмет из предметной области.

        Согласен, тоже слово перегруженное ассоциациями. Но в ООП много способов применения классов и не для всех способов применения выбор имени класса может иметь такое существенное значение.
          +1

          В терминах MVC модель — не класс, отражающий какое-нибудь важное понятие или предмет из предметной области, а либо (строго) класс или классы, скрывающий (адаптер, прокси и т. п.) совокупность классов, отражающих понятия или предмет из предметной области, либо (чаще применяется) сама эта совокупность. Выражение "класс модели" нужно понимать чаще всего не как "класс, реализующий модель", а как "класс, принадлежащий модели, реализующий часть модели".


          А то что авторы некоторых фреймворков/библиотек выбирают (обычно при реализации ORM паттерна ActiveRecord) Model для имени базового класса, от которого должны наследоваться персистентные сущности модели — как раз пример неудачного именования, вводящего в заблуждение использующих этот класс :)

            0
            Я в итоге думаю, что модель в том смысле, как она появилась в ActiveRecord, не противоречит понятию модели из системного анализа. Как только у нас появляются разные слои абстракции, так модель предметной области расслаивается на модели отдельных подсистем. Сложное понятие из предметной области практически всегда в силу своей сложности заслуживает того, чтобы его рассматривать как подсистему и строить собственную модель (как термин системного анализа).

            Ну и модель в rails уже достаточно многие используют без привязки именно к ActiveRecord, а именно как четко выделенную группу классов, которые моделируют предметную область, оторванную от представления для пользователя или для API.
              –1
              ActiveRecord это только структуры данных, а модель предметной области включает не только сущности, но и операции с ними.
                0

                ActiveRecord — это именно объединение структур данных, бизнес-операций и операций сохранения и загрузки.

        +3
        Возможно, моё мнение несущественно, т.к. я совсем не профессиональный программист, но все равно выскажусь.
        Я учился на книжках Гради Буча и т.п. древностях, и у меня сложилось представление об ООП принципиально отличающееся от того, что под ООП понимают сейчас. Мне кажется, мир перевернулся… Для меня объекты (сиречь классы) — это отражение сущностей РЕАЛЬНОГО мира в ВИРТУАЛЬНУЮ область. Иными словами для меня выглядит логичной классификация вроде: РЫБЫ-ПТИЦЫ-ЖИВОТНЫЕ, РЫБЫ: КАРАСЬ, ПТИЦЫ: УТКА; ПТИЦЫ: УТКА: ЛЕТАТЬ, ПТИЦЫ: УТКА: ПЛАВАТЬ, РЫБЫ: КАРАСЬ: ПЛАВАТЬ, РЫБЫ: ЛЕТУЧАЯ_РЫБА: ЛЕТАТЬ и т.п.
        Но читая хабр я убеждаюсь, что сейчас принято строить ОО-модели совсем иначе — от ДЕЙСТВИЯ или от ЦЕЛИ (например). То есть вполне допустимой считается объектная модель типа ЛЕТАЛКИ-ХОДИЛКИ-ПЛАВАЛКИ, ЛЕТАЛКИ: УТКА, ЛЕТАЛКИ: ЛЕТУЧАЯ_РЫБА, ХОДИЛКИ: ВОЛК, ХОДИЛКИ: УТКА…
        Я понимаю, что виртуальный мир программирования не обязан подчиняться закономерностям реального мира, но ведь и тем и другим заправляет человек, а уж ему-то изначально ближе классическая структуризация объектов реального мира! Все-таки видовая классификация устроена иначе… Класс бытовых приборов включает в себя телевизор и депилятор, и классификация их по иным признакам (напр., развлекательные приборы или средства удаления волос) по определению вторична, когда ее ставят на первое место — это выворачивание реальности наизнанку…

        Соответственно, проблема именования порождается именно из когнитивного диссонанса между реальностью и виртуальностью. Если подвести второе под гребенку первого — проблема именования исчезнет: главное — это существительное, глаголы обозначают действия существительного над другими объектами, прилагательные — это свойства существительных…

        Откуда пошло это массовое увлечение переиначивание привычного в непривычное? Неужели так легче жить программисту, находясь между реальным и выдуманным и занимаясь сшиванием одного с другим белыми нитками странных интерфейсов?!
          0

          Реальный мир состоит из, грубо говоря, объектов и процессов их взаимодействия. ООП (в мэйнстрим реализациях) в качестве реализаций процессов по умолчанию предлагает методы объектов (с возможными параметрами, в том числе в виде объектов), но всё многообразие отношений реального мира сложно свести к действиям, совершаемых объектами, к чёткой картине "существительное-глагол", поэтому вводят объекты, имитирующие процессы без явных действий какого-то субъекта… Кажется это вы имеете в виду.


          Но примеры вы приводите не об этом :), а о способе построения иерархий объектов. Так вот, при моделировании реального мира в целях решения конкретных задач иерархия строится так, чтобы максимально отражать важные для решаемой задачи отношения, а не какие-то "естественные". Для задачи, например, выбора снаряжения для ловли животных, абсолютно не важно, что утка и летучая мышь принадлежат к далеко отстоящим друг от друга ветвям в общепринятой биологической таксономии, важно что они могут летать, а акулы и дельфины плавать, что для ловли летающих животных нам нужна, например, сеть, выстреливаемая в воздух конкретно по цели, а для плавающих — выставляемая в воде заранее. Причём если мы соберемся ловить квадрокоптеры, которые вообще животными не являются, то выстреливаемая сеть тоже пойдёт, а против торпед — выставляемая. Для подобных задач нам важно летает цель в воздухе или плавает в воде, а насколько цели родственны в биологической таксономии нам не важно. Куда логичнее разделить объекты "цель" на "леталка", "плавалка" и т. д., чем объединять уток и страусов, летучих мышей и дельфинов, квадрокоптеры и торпеды в какие-то "естественные" иерархии, а потом по слабозначащим для этих иерархий признакам выбирать что нам делать.


          Моделирование реального (или воображаемого, например в играх) мира заключается в выкидывании ненужных для решаемой задачи свойств объектов (в широком смысле слова) и отношений между ними и в акцентировании на важных. В рамках ООП это заключается в построении объектов, иерархий наследования и полиморфных свойств в порядке важном для задачи. Утка.лети(), ЛетучаяМышь.лети(), Квадрокоптер.лети(), АбстрактнаяЛеталка.лети(), Рыба.плыви(), Дельфин.плыви(), Торпеда.плыви(). АбстрактнаяПлавалка.плыви(). Какая нам разница, что летучая мышь очень дальний родственник утки, а квадрокоптер вообще не животное, а что утка может плавать нас вообще не интересует?

            0
            Куда логичнее разделить объекты «цель» на «леталка», «плавалка» и т. д., чем объединять уток и страусов, летучих мышей и дельфинов, квадрокоптеры и торпеды в какие-то «естественные» иерархии, а потом по слабозначащим для этих иерархий признакам выбирать что нам делать.
            Почему же «по слабозначащим»? Выбор будет идти по тем «умениям», которые реализует класс.

            То есть — да, всё, что вы сказали правда — но обусловлена она ограничениями языков программирования.
              0

              Причём тут языки программирования? Меня, как разработчика системы защиты какой-то локации или объекта от вторжения, мало интересует, что дельфины и летучие мыши — животные и достаточно близкие родственники, а квадрокоптеры и торпеды — механизмы с электродвигателями. Для меня всё это цели, делящиеся на цели воздушные и цели подводные. Если для системы и имеет значение, является цель живым животным или искусственным механизмом, то это следующий уровень иерархии. Среда в которой придётся противодействовать вторжению имеет большее значение, чем философские категории.

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

                А вот в большой программе — часто оказывается что один компонент хочет работать с квадрокоптерами как с летучими мышами (потому что их нужно ловить), а другой — хочет обьединять их с торпедами (потому как обоим нужны аккумуляторы).

                В наиболее распространённых языках в этом месте как раз и появляются все эти фасилитаторы.
                  0

                  Обычно нет никакой необходимости представлять один физический объект в разных модулях программы одним классом. Нередко даже связь между классами, представляющими разные аспекты одного физического объекта можно не устанавливать.

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

              Куда логичнее разделить объекты «цель» на «леталка», «плавалка» и т. д., чем объединять уток

              Причём тут языки программирования? Меня, как разработчика системы защиты какой-то локации или объекта от вторжения, мало интересует, что дельфины и летучие мыши — животные

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

              Вы не заметили, что в вашем споре вы соглашаетесь и не соглашаетесь одновременно, то есть фактически ваш спор ни о чём?
              Проведя параллель, я бы сказал что вы обсуждаете вкусы и пристрастия, о которых, как известно — не спорят. Я же не соглашусь с вами обоими, особенно вот в этой части:
              иерархий наследования и полиморфных свойств в порядке важном для задачи

              Надеюсь меня не заминусуют, но есть огромная разница между наследованием, которое строго иерархиично и предназначено исключительно для переиспользования кода и задачам классификации, то бишь выделения нужных и общих признаков и работы с объектами на их основе, а именно — «важном для задачи». Так вот, классификация не может иметь порядка, это фильтр, где несколько признаков равнозначны и параллельны, а не наследуются друг от друга. Соответственно одними и теми же признаками могут обладать совершенно разные и чуждые друг другу объекты. Фактически, у нас есть интерфейсы для классификации и иерархия для переиспользования кода. Если код для летучей мыши можно использовать для создания кода для квадракоптера — наследуйтесь не задумываясь. Если некоторые свойства торпеды и рыбы позволяют нам абстрагироваться и работать с ними как с объектом одного типа — объединяем их в рамках классификации интерфейсом и работаем когбуд-то бы с одноим объектом, но именно в рамках конкретного использования, а не вообще.
              Если же следовать вашим рамкам, где классификация должна иметь иерархию, то вы придёте к тому, что с добавлением новых объектов, всё должно быть переписано, потому что новый признак стал главнее (собственно, ваш пустой спор о том, какие признаки главнее. да ещё и в какой ситуации) и теперь он должен быть корневым, о каком переиспользовании кода может тогда идти речь?
                0

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


                А спор не о том, какие признаки главнее, а том, нужно ли определять какие главнее в рамках задачи и "плясать" от них или нужно брать готовые "ественные" квалификации и втискивать в неё задачу.

                  0
                  Если вы внимательно прочли мой пост, «может» — это не просто исключительное явление, а ещё и — временное, что сразу делает такую возможность плохой практикой.
                  Нет никаких проблем с тем, что бы определить важные признаки, если известна конкретная задача.
                  Надо контролировать доступ в помещение: (дверь и окно и угол открытия), пробитие предметами (дверь и стена и их прочность), классификация всегда идёт под задачу, никакая иерархия не нужна, наследовать никакую дверь от стены тоже не надо. Более того, признаки, одних задач никак не должны влиять на признаки других, поэтому я и указал, что такие классификации параллельны.

                  Есть проблемы с тем, что бы определить важные признаки, если нет никакой задачи, а эти признаки, как элемент классификции рассматривать именно на будущее в плане наследования, что является в корне не верно, как я уже написал.
              0
              Системы стали больше и сложнее — поэтому все лишнее безжалостно отсекается. Если главной задачей системы является работа с классификацеий видов животных — то будет структура как в вашем первом примере. Если задача сводится к перемещению животных в пространстве, то и модель очевидно должна отражать в первую очередь эти процессы, абстрагируясь от конкретных видов животных и кто там кому кем приходится. Это все хорошо описано у Эванса на примере грузоперевозок, когда изначальные классы «контейнер» и «корабль» превращаются в итоге в «операция перевозки», потому что в области грузоперевозок именно это ключевой термин.
                0
                Да, хороший пример с грузоперевозками. В результате эволюции отрасли появились процессы и термины, лучше всего приспособленные к задачам, у которых убрано все лишнее. Программистам лучше всего перенести существующую модель в цифру.

                Когда процессы и отношения только зарождаются — готовых моделей нет и их нужно придумывать. Один из первых подходов — использовать физические объекты и процессы, но сразу же упрощать, убирать все неважное, оставлять только минимально необходимое.
                +1
                Казалось бы, РЫБЫ, ПТИЦЫ и ЖИВОТНЫЕ — это абстрактные классы, в то время как ЛЕТАЛКИ, ХОДИЛКИ и ПЛАВАЛКИ — это интерфейсы (возможно, в силу бедности языка, реализованные в виде тех же абстрактных классов, но по изначальной сути это именно интерфейсы).
                Собственно, никто не мешает вам иметь как одно, так и другое (и их одновременно), в зависимости от того, что вам в конкретной задаче удобнее.
                  0

                  Это кажется только с точки зрения научной биологической таксономии :) С точки зрения утиной типизации Леталки вполне имеют право на жизнь даже как полноценные классы.

                    0

                    С точки зрения палеонтолога (и космолога, кстати) — таксономия современных существ есть срез длящегося миллиарды лет процесса.
                    Чтобы это ощутить, понять этих учёных — достаточно увидеть изменения, например, сравнив современных китов и их первопредка. Объекты эволюции существуют внутри процесса.

                      0

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

                        0
                        А что мы делаем в системе, где есть и эволюция и вторжение? Ну там простайшая RPG'шка где персонажи эволюционируют и дерутся?
                          0

                          Думать надо. В случае RPG навскидку проще получится, если основной классификацией будет по возможностям персонажа, а эволюционное дерево — дополнительной, связанной с основной, например, композицией.

                            0

                            Вообще то я хотел сказать кое что попроще — что объекты существуют в некоем процессе или потоке событий и имеют поведение. Это может быть и простейшим течением времени и логическими связями и чем то ещё.
                            Но вот выделение объектов только на основании поведения и приводит к появлению леталок вместо уток.

                  0
                  Думаю, что сериализация и MVC изменили мир. Теперь, в основном, в системах есть модели и сервисы. Так что утки уже без помощи сервисов не летают.
                    0
                    Нет, всё проще, банальнее и грустнее. Мир изменился потому что кто-то в Xerox PARC с дуба рухнул и решил, что метод — есть свойство обьекта. Получили королевство существительных. В языках, где методы (как им и положено!) не принадлежат обьектам, но связывают их — этого всего ужаса, обычно, меньше.
                      +1

                      Ага, имеем кучу методов, висящих в одном пространстве имен. Появляется мысль, не сгруппировать ли их каким-то образом. Ага, вводим понятие "модуль", распихиваем методы по модулям. Теперь метод принадлежит модулю, и вызывается аналогично методу объекта "[module].[method]" с точностью до разделителя. С той только разницей, что модуль у нас один, и получается как бы синглтон. Ага, появляются вопросы, как нам в этом синглтоне чего-нибудь поменять, как переиспользовать код, не копипастя лишнего, как подменить реализацию, например, для работы с другой БД. Получается то же самое, только с другой стороны.

                        0
                        Теперь метод принадлежит модулю, и вызывается аналогично методу объекта "[module].[method]" с точностью до разделителя.
                        Это ещё зачем? Методы, «работающие» с объектами разных классов не могут перепутаться просто потому что у них аргументы разных типов, однако.

                        Пространства имён — да, возможны, но это уж для очень монструозных проектов. И они вообще ничего общего ни с методами, ни с объектами на имеют.

                        Ага, появляются вопросы, как нам в этом синглтоне чего-нибудь поменять, как переиспользовать код, не копипастя лишнего, как подменить реализацию, например, для работы с другой БД.
                        Это, я извиняюсь, называется Java головного мозга. Откуда синглтон? Почему, зачем? Не хотите смотреть как это сделано в LISP'е — посмотрите как перегрузка операторов, функций и пространства имён реализованы в C++. Вот конкретно это место там реализовано почти хорошо — единственная проблема, что всё происходит во время компиляции. Если бы те же алгоритмы применялись в рантайме к реальным, динамическим типам — был бы практически CLOS.

                        Недаром говорят, что тот, кто не понимает LISPа обречён изобрести его кривую копию. C++ к этому уже близок, Java тоже туда же двигается потихоньку…
                          +1
                          Это ещё зачем? Методы, «работающие» с объектами разных классов не могут перепутаться просто потому что у них аргументы разных типов, однако.

                          Затем, чтобы можно было использовать функции с одним названием и одинаковыми типами аргументов, но с разной реализацией.


                          actionEdit(string id)
                          query(string sql)

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


                          Откуда синглтон? Почему, зачем? Не хотите смотреть как это сделано в LISP'е

                          Смотрел я, как это сделано в LISP'е. Похоже на паттерн-матчинг в Erlang'е. Язык тут ни при чем. Модуль с набором функций — это фактически аналог глобального синглтона. Как и статический класс. Мы можем использовать только один экземпляр этого модуля.


                          Сравните:


                          use math;
                          y = math.sin(x);
                          
                          global math = Math::getInstance();
                          y = math.sin(x);
                            0
                            Затем, чтобы можно было использовать функции с одним названием и одинаковыми типами аргументов, но с разной реализацией.
                            Но… зачем?

                            actionEdit(string id)
                            query(string sql)
                            Первого примера я в принципе не понял (какой-то фасилитатор потерялся?), а второй будет
                            query(mysqlink database, string sql)
                            query(htmldom domtree, string xpath)
                            ...
                            Откуда тут путаница возьмётся? Пусть их хоть 100 будет — но они работают с разными обьектами!

                            Все равно без группировки функций в какие-то подмножества не обойтись, надо хотя бы разделять на локальное и глобальное пространство.
                            Это другое — это пакеты. Чтобы скрыть локальные и глобальные обьекты и функции.

                            Смотрел я, как это сделано в LISP'е. Похоже на паттерн-матчинг в Erlang'е. Язык тут ни при чем.
                            Язык формирует мышление. Тот факт, что функции в большинстве языков принадлежат обьектов вызвало кардинальную Java'у головного мозга у большинства разработчиков. В большинстве действий участвуют два (а иногда и более) обьекта — почему оно должно принадлежать одному из них?

                            Причём ладно бы только у «быстрых» языков, где это эффективностью реализации определяется. Так ведь всякие тормозные ruby и python'ы — туда же.
                              0
                              Но… зачем?

                              Я привел примеры ниже в том комменте. Есть много причин, когда нужна другая обработка тех же аргументов. Для оптимизации или тестирования например.


                              Первого примера я в принципе не понял (какой-то фасилитатор потерялся?)

                              Это обработчик запроса на редактирование сущности. В контроллере действий с заказами и в контроллере действий с пользователями есть метод actionEdit(), который принимает строковый id объекта, который передается в get-параметре. Но я уже понял, что вы предлагаете создавать специальный объект, чтобы вызвать нужный метод. Вместо того, чтобы прямо поместить этот метод в объект.


                              query(mysqlink database, string sql)

                              И в чем тогда профит, если нам всегда надо таскать объект определенного типа?


                              query(mysqlink database, string sql)
                              query(postgresqlink database, string sql)


                              а еще


                              open(mysqlink database)
                              close(mysqlink database)
                              open(postgresqlink database)
                              close(postgresqlink database)


                              И тому подобная куча копипасты. А потом мы обнаружили ошибку и решили исправить mysqlink на mysql_link, и пришлось поменять в 2 раза больше строк, чем нужно. А если мы еще и пропустили где-то… Не критично, просто зачем? Если можно сгруппировать связанные методы в класс. Результат ведь будет тот же самый, только первый аргумент с другой стороны от названия функции. Тем более что мультиметоды и в классах могут работать.


                              Это другое — это пакеты. Чтобы скрыть локальные и глобальные обьекты и функции.

                              Модули, пакеты, не суть. Суть в том, что появляется группировка связанных методов.


                              В большинстве действий участвуют два (а иногда и более) обьекта — почему оно должно принадлежать одному из них?

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

                                0
                                Но я уже понял, что вы предлагаете создавать специальный объект, чтобы вызвать нужный метод.
                                Java головного мозга. Поздняя стадия.

                                Методы не должны принадлежать обьектам. Они их связывают. Оптимизация сделанная десятки лет назад настолько вьелась вам в мозг, что вы даже представить не можете, что метод может не быть частью обьекта!

                                Мы знаем, что дверь может открываться и закрываться.
                                Не может она сама по себе открываться и закрываться! Её кто-то открывает и закрывает. Это может быть человек или (если дверь автоматическая) специальный механизм.

                                Потому что это отражает способ мышления в реальности.
                                Серьёзно? Спросите у непрограммиста: «во фразе „Вася открыл дверь“ слово „открыл“ принадлежит Васе или двери?» Он на вас как на идиота посмотрит.

                                Результат ведь будет тот же самый, только первый аргумент с другой стороны от названия функции.
                                Результат не будет тот же самый. Потому что имея методы как отдельные сущности с ними можно и работать как с отдельными сущностями.

                                Можно сделать что-то примерно следующее (примерный псевдокод):
                                isolated_processing(class storage, class text):
                                  open(storage)
                                  query(storage, text)
                                  close(storage)
                                И вы сможете туда передать и mysqlink и html_parser, и строки и какие-нибудь бинарные заранее подготовленные запросы.

                                Копи-паста — это не тут. Копи-паста — это вам в Java. Где из-за необходимости обрабатывать разные обьекты по разному вы эти свои «сгруппированные методы» вынуждены дублировать десятки и сотни раз.

                                Какое уж тут «переиспользование кода»…
                                  +1
                                  Но я уже понял, что вы предлагаете создавать специальный объект, чтобы вызвать нужный метод.

                                  Java головного мозга. Поздняя стадия.

                                  Методы не должны принадлежать обьектам. Они их связывают. Оптимизация сделанная десятки лет назад настолько вьелась вам в мозг, что вы даже представить не можете, что метод может не быть частью обьекта!

                                  Воздержитесь, пожалуйста, от подобных диагнозов. Я вроде вас не оскорблял.
                                  У меня нигде не написано, что они обязательно "должны" принадлежать объектам. С процедурным программированием я знаком, да и с функциональным тоже. И если вы не заметили, в моих примерах есть только один объект Math::getInstance().


                                  Тем не менее, вы все равно, предлагаете создавать специальный объект определенного типа, чтобы вызвать нужный метод из нескольких actionEdit. Принципиально это ничем не отличается от обычного объектного подхода.


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


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


                                  Не может она сама по себе открываться и закрываться! Её кто-то открывает и закрывает. Это может быть человек или (если дверь автоматическая) специальный механизм.

                                  Она не сама открывается и закрывается. Это мы относим к ней такое действие.


                                  Серьёзно? Спросите у непрограммиста: «во фразе „Вася открыл дверь“ слово „открыл“ принадлежит Васе или двери?» Он на вас как на идиота посмотрит.

                                  Серьезно. Спросите у непрограммиста: «во фразе „Вася открыл дверь“ слово „открыл“ относится к действиям с Васей или с дверью?». А заодно спросите, встречал ли он фразу "Дверь неожиданно открылась".


                                  И вы сможете туда передать и mysqlink и html_parser, и строки и какие-нибудь бинарные заранее подготовленные запросы.

                                  Ну и чем это отличается от такого варианта:


                                  isolated_processing(storage, text):
                                    storage.open()
                                    storage.query(text)
                                    storage.close()

                                  И там и там надо делать реализацию метода open, которая будет работать с объектом конкретно этого типа. А еще надо смотреть, а все ли у меня действия с нужными типами определены, а нет ли конфликтов с каким-нибудь "open(string url)" или "open(string filename)". Кстати, что должно означать действие "открыть строку"?


                                  Копи-паста — это вам в Java. Где из-за необходимости обрабатывать разные обьекты по разному вы эти свои «сгруппированные методы» вынуждены дублировать десятки и сотни раз.

                                  Не знаю, как там в Java. "У меня" в PHP трейты есть. А если туда перегрузку функций завезут, будет еще лучше.

                                    0
                                    И там и там надо делать реализацию метода open, которая будет работать с объектом конкретно этого типа. А еще надо смотреть, а все ли у меня действия с нужными типами определены, а нет ли конфликтов с каким-нибудь «open(string url)» или «open(string filename)».
                                    Вообще-то подобными проверками должен заниматься компилятор. И так, как вы сказали, будет в Google Go или в G++ 2.95 (с его сигнатурами). В большинстве же языков вам этого сделать не удастся если разработчики классов storage заранее об этом не позаботятся.

                                    Вы зря к синтаксиса прицепились — дело ведь действительно не в нём. Вопрос прост: должны мы уметь описывать все классы, в которые логически попадает объект — или нет.

                                    Так вот в CLOS/GOOPS/Google Go эта классификация — не «зашивается» в классы! Она может быть расширена потом! Что и делает все споры типа показанных выше гораздо менее острыми. Вам не нужно всё переделывать с одной таксономии на другую при изменении требований, так как этих таксономий может быть много — они друг другу никак не мешают и, главное, создавать новые таксономии можно после создания класса. Это — принципиальное отличие. Всё остальное, действительно, «рюшечки»…

                                    Засовывая же всё на свете в объекты вы порождаете лютый копи-паст на всех уровнях — и даже этого не замечаете.

                                    Кстати, что должно означать действие «открыть строку»?
                                    Вы это странное действие придумали — вы и решайте что оно значит…
                                      0
                                      не удастся если разработчики классов storage заранее об этом не позаботятся
                                      Если не позаботились, может он там и не нужен? Если у меня есть сторонний класс Url, то метод open(Url url) должен быть не в Url, а в другом классе WebBrowser.

                                      создавать новые таксономии можно после создания класса
                                      Новая таксономия — новое название, которое имеет семантику и должно быть отражено в коде. В виде собственно названия, а не безымянного набора дополнительных методов. Впрочем, не буду отрицать, у классов в текущем виде тоже свои недостатки есть.

                                      Засовывая же всё на свете в объекты вы порождаете лютый копи-паст
                                      Не больше, чем засовывая в модули или пакеты. Пакет — это такой же объект, содержащий методы. Другое дело, что иногда лучше поместить метод open не в класс Дверь, а в класс МетодыРаботыСДверью.

                                      Вы это странное действие придумали — вы и решайте что оно значит
                                      Это вы писали, потому и вопрос:
                                      Можно сделать что-то примерно следующее (примерный псевдокод):
                                          isolated_processing(class storage, class text):
                                               open(storage)
                                      

                                      И вы сможете туда передать и mysqlink и html_parser, и строки
                                        0
                                        В виде собственно названия, а не безымянного набора дополнительных методов.
                                        Зачем?

                                        Впрочем если уж есть желание что-то обязательно называть, то это можно сделать: в CLOS/GOOPS можно просто добавить в список предков класса новый на лету, в Google Go — собрать методы в интерфейс.

                                        Если у меня есть сторонний класс Url, то метод open(Url url) должен быть не в Url, а в другом классе WebBrowser.
                                        А что будет есть разработчики WebBrowser и Url — это разные люди? Генераторы фасилитаторов?

                                        И вы сможете туда передать и mysqlink и html_parser, и строки
                                        Хабр опять съел слово. «Тестовые строки». В unit-тестах. open/close — ничего делать не будет, query — будет на равенство сравнивать.

                                        Хотя, может быть, тут уже стоит создать тестовый класс DummyStorage…
                        0

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

                          0
                          Вроде как большинство мэйнстрим ООП языков позволяют совмещать объекты с принадлежащими ими методами с классическими функциями и процедурами.
                          Классические функции не имеют отношения к ООП вообще.

                          ООП — это у нас что? Инкапсуляция, наследование, полиморфизм. Так вот с последним — как раз и возникает беда из-за отсутствия в языках мультиметодов.

                          Возьмите классические геометрические фигуры, с которых обычно начинают «рассказ про ООП». Вот это вот:
                          draw(DirectXContext, circle)
                          draw(PostScriptContext, circle)
                          draw(DirectXContext, rectangle)
                          draw(PostScriptContext, rectangle)
                          Концептуально — должно быть одним мультиметодом. А на практике — это невозможно.

                          Главное — даже не то, что вам приходится создавать drawInDirectXContext и drawInPostScriptContext! Главное — что вы не можете добавить drawInVulcanContext без порождения новых сущностей!

                          Потому и порождается куча вспомогательных обьектов и прочего, чтобы с этим бороться.

                          Вот и всё. Ограничения Smalltalk'а очень сильно продвинули ООП в сторону громоздкости и неестественности.

                          Из распространённых языков, пожалуй только Go делает это «в правильном ключе». Но даже у него это не так элегантно сделано как в оригинале.
                      0
                      Откуда пошло это массовое увлечение переиначивание привычного в непривычное?
                      От Smalltalk'а. Далее Objective C, C++, Java и прочее. Отказ от «сложного» подхода типа CLOS/GOOPS примело нас в королевство существительных.
                      0
                      При выборе имен следует помнить (или принимать как неизбежное) — Мысль изреченная есть ложь
                        0
                        Отличный вопрос на SO, с небольшим количеством хороших ссылок по теме.

                        Only users with full accounts can post comments. Log in, please.