Как я могу добиться полиморфизма, если логика MVC находится на контроллере?

Как я могу добиться полиморфизма, если логика MVC находится на контроллере?
Как я могу добиться полиморфизма, если логика MVC находится на контроллере? - pepegombos @ Unsplash

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

Во время обучения я наткнулся на MVC, и сейчас большинство проектов, которые я делаю, реализованы по этому паттерну. Даже если иногда это чересчур, я все равно делаю это, чтобы изучить лучшие практики.

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

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

Мой вопрос таков:

Я видел в разных местах, что «если вы не можете поместить свои модели в другое приложение, а контроллер этого приложения вызывает методы из этой модели внутри другого контроллера, то вы, вероятно, делаете это неправильно». Об этом говорят опытные и уважаемые разработчики.

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

Например.:

У меня есть игра в монополию, когда текущий игрок приземляется на квадрат, я могу сделать одну из двух вещей -

  1. получить тип квадрата приземлился и запустить некоторую логику.

    If(square.type === “property”) {
        //check if owned
        //if it’s not owned call a view method giving us the option to buy
    }
    

    Затем мне пришлось бы повторить это if, если квадратный тип - это шанс, сундук сообщества, штраф и т. д. Это явно не чистый код.

  2. Вместо этого мой квадратный объект использует полиморфизм, который принимает зависимости при построении. У каждого квадрата есть метод activateLogic(). Это означает, что та же самая логика моего контроллера теперь находится в моей модели. Тем не менее, некоторые говорят, что это неправильно, логика приложения должна быть в контроллере, мои модели теперь подходят только для монопольной игры, потому что я передал ей различные зависимости, например. текущий игрок и кости (чтобы проверить, есть ли дубль). Но затем другие говорят, что мой код чище в модели, меньше операторов переключения и т. д., например. Мне просто нужно получить квадрат в моем контроллере и вызвать activateLogic() на квадрате, а полиморфизм позаботится обо всем остальном.

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

Контекст: Судя по некоторым вашим комментариям, вы имеете дело с настольным приложением, работающим на JavaScript, так что это в основном написано в этом свете (то есть сценарий не является Web-MVC).

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

[...]

«Это означает, что та же логика [которая была ранее] в моем контроллере [теперь находится] в моей модели. Тем не менее, некоторые говорят, что это неправильно, логика приложения должна быть в контроллере, мой модели теперь подходят только для монопольной игры" [выделено ответившим]

Таким образом, сделанное замечание не следует воспринимать слишком буквально, потому что то, что подразумевается под «другим приложением», является немного более техническим, чем кажется.

Совершенно нормально, что ваша доменная модель подходит только для создания монопольных игр. Помните, вы не создаете какую-то программируемую среду общего назначения (для этого существуют языки программирования), вы делаете монопольную игру.

Возможность использовать это в «другом приложении» означает, что вы организовали вещи таким образом, чтобы вы могли использовать этот код, не перетаскивая вместе с ним элементы контроллера / пользовательского интерфейса (и, возможно, любые другие внешние факторы, такие как база данных) - и сделать еще одно монопольное игровое приложение (возможно, с лучшей графикой). Таким образом, это возможность просто извлечь то самое ядро, которое кодирует логику игры в монополию (примечание: это не обязательно легко сделать, вы должны все обдумать, и это может занять несколько итераций). Это ядро ​​не обязательно должно быть функциональным само по себе (его не нужно запускать из коробки), ему просто нужно зафиксировать в себе правила монополистической игры и позволить вам «подключаться» (или создавать вокруг него) различные компоненты (такие как графический интерфейс), которые вместе составляют законченное приложение.

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

«Так что, если это так, это будет означать, что вся моя логика выполняется в контроллер"

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

Один из способов думать об этом заключается в том, что теоретически вы должны быть в состоянии взять это и прикрепить к нему другой концептуальный пользовательский интерфейс (скажем, на основе терминала, а не графический интерфейс) — думайте об этом как о «другом приложении», как начальное приближение.

«Например, если недвижимость продается, создайте всплывающее окно с вопросом, хочет купить недвижимость? Добиться этого сложно, если моя логика модель!"

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

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

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

Также помните, что вы можете (вручную или через DI-контейнер) внедрять полиморфные объекты и/или лямбда-выражения в некоторые из ваших доменных объектов. Например. доменный объект уровня приложения может принимать в качестве параметра метода объект или лямбда-выражение, которое позволяет ему отправлять результат некоторого вычисления, когда метод его вычисляет. Вы можете передать ему (или внедрить) код, который принимает некоторые данные и обновляет что-то в пользовательском интерфейсе — объект ничего не знает о пользовательском интерфейсе, потому что он просто передает результат вычисления, а обработчик исходит из внешнего источника.


LetsCodeIt, 18 января 2023 г., 03:05