Я изучал удобство добавления функциональности к объектам, которые раньше обрабатывались «менеджерами» или «контроллерами». Я просто волновался, есть ли какие-либо проблемы, работающие таким образом. Основная проблема, которую я вижу, это связь.
В качестве примера возьмем увольнение сотрудника из компании.
Раньше я бы работал так:
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
}
Последний способ хорош и удобен, потому что вам просто нужна ссылка на сотрудника, и вы можете уволить его, где хотите. Вам также не нужен класс управления.
class RogueEmployee : Employee { override void Fire() { /*Do nothing*/ } }. Вы хотите, чтобы экземпляры Employee могли контроль запускать процесс?
Взгляните на богатую модель данных и анемичную модель данных.





Я думаю, что вы правы, беспокоясь о том, чтобы избежать связи здесь - я бы посчитал ваш пример плохой практикой. Но давайте раскроем и покажем способ, которым мы можем добиться такого же поведения с более понятным, предсказуемым и поддерживаемым контрактом между классами.
В бизнесе много сотрудников, он знает, кто у него работает, поэтому ему имеет смысл владеть коллекцией ссылок на 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.
По вашему описанию сложно понять, какие шаблоны и для каких целей вы собираетесь использовать. Но
business.employees.Remove(...);определенно пахнет кодом, поэтому обе части выглядят не очень хорошо.