Каковы плюсы и минусы того, что «менеджер» делает что-то с объектом или когда объект делает что-то сам?

Я изучал удобство добавления функциональности к объектам, которые раньше обрабатывались «менеджерами» или «контроллерами». Я просто волновался, есть ли какие-либо проблемы, работающие таким образом. Основная проблема, которую я вижу, это связь.

В качестве примера возьмем увольнение сотрудника из компании.

Раньше я бы работал так:

class Employee
{
    // Does employee stuff
}

class Business
{
    public List<Employee> employees
}

class BusinessManagement
{
    Business buisness
    public void FireEmployee(Employee employee)
    {
        business.employees.Remove(employee);
    }
}

Теперь я пробовал это:

class Employee
{
    Business business
    Fire()
    {
        business.employees.Remove(this);
    }
}

class Business
{
    public List<Employee> employees
}

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

По вашему описанию сложно понять, какие шаблоны и для каких целей вы собираетесь использовать. Но business.employees.Remove(...); определенно пахнет кодом, поэтому обе части выглядят не очень хорошо.

Dmytro Mukalov 13.02.2019 06:38
class RogueEmployee : Employee { override void Fire() { /*Do nothing*/ } }. Вы хотите, чтобы экземпляры Employee могли контроль запускать процесс?
Damien_The_Unbeliever 13.02.2019 08:03

Взгляните на богатую модель данных и анемичную модель данных.

Johnny 13.02.2019 09:38
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
48
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

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

Бизнес увольняет сотрудника, и сотрудник также должен знать, что он уволен. Если бы я собирался это сделать, я бы поместил метод Fire(Employee e) в класс Business — внутри этого метода я, скорее всего, хотел бы вызвать e.GetFired(), а затем удалить сотрудника из коллекции сотрудников. Затем Employee.GetFired() установит для собственного свойства EmployeeBusiness значение null.

Это может работать в обоих направлениях, сохраняя при этом разделение между этими классами. Сотрудник, предположительно, может уйти с работы. Таким образом, Employee.Quit() может вызывать Business.EmployeeHasQuit(Employee e), передавая себя в качестве ссылки, позволяя экземпляру Business взять на себя ответственность за удаление экземпляра Employee из коллекции.

Ваш пример является примером связи, потому что метод GetFired() класса Employee использовал ссылку на бизнес-объект для доступа к общедоступной коллекции и удаления себя. Это также было бы сцеплением, если бы метод Business.Fire(Employee e) установил e.Business = null. Разумное эмпирическое правило заключается в том, что классы не должны напрямую изменять членов других классов; позволяя каждому классу управлять своим собственным состоянием, вы можете реализовать поведение, чтобы гарантировать, что вещи всегда находятся в допустимом состоянии, и запускать другие необходимые поведения (например, мы хотим убедиться, что, когда Сотрудник увольняется, его пропуск безопасности отзывается, и это произойдет внутри EmployeeHasQuit() - если сотрудник просто удалил себя из коллекции Business, это не так. Бизнес также может захотеть иметь возможность поместить сотрудника, который уволился или был уволен, в коллекцию exEmployees, чтобы отслеживать из них позже.

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

Это помогло мне понять обязанности объекта. Посмотрите Агрегаты и сущности. Как объяснил @toadfish, у вас может быть много возможных решений, и я согласен с тем, что главное — избегать связывания. Если вы читали о DDD, там будет сказано, что выберите основной класс (Aggregate), который будет содержать ссылки на объекты одного домена. Когда вы обрабатываете/управляете вещами, всегда используйте совокупность для доступа к дочерним объектам. Например, Business может быть основным классом со ссылкой на Employee. Тогда Fire() внутри Business.

T.Nylund 13.02.2019 22:33

Другие вопросы по теме