Страницы

Поиск по вопросам

пятница, 20 декабря 2019 г.

Как правильно проектировать классы с большим количеством обязательных параметров?

#c_sharp #классы #проектирование


Мне требуется создать достаточно массивный класс, координирующий работу ряда объектов.
Для его работы необходимо отправить ему около пятнадцати объектов. Без них его работа
не возможна, по этому было принято решение отправлять все объекты через конструктор.
Естественно, что конструктор на 10+ аргументов выглядит просто ужасно! Есть альтернатива
отправлять все объекты посредством свойств, но этот способ имеет свои недостатки. Придётся
лепить дополнительные проверки на null и выкидывать исключения. Да и стороннему разработчику,
если потребуется работать с таким классом придётся потратить дополнительный усилия,
что бы не пропустить никакого важного свойства.

Как быть? Множество аргументов в конструкторе? Или множество свойств обязательных
к заполнению?
    


Ответы

Ответ 1



Есть такой анти-паттерн God он же Божественный объект. Его главная особенность — он может всё. Такой объект нарушает принцип единственной ответственности, и, скорее всего, его можно разбить на три или четыре несвязанных класса. Если посмотреть на ту же ситуацию под другим углом зрения, можно говорить о ней, как об ошибке декомпозиции: перескакивании на несколько уровней вниз. Например, вещество состоит из молекул, молекулы из атомов, атомы из протонов, нейтронов и электронов, протоны и электроны — из кварков. Естественно, можно сказать, что молекулы состоят из протонов и электронов, и это правда, но, пропустив атомы, вы получаете весьма запутанную картину молекулы. Атомы позволяют значительно упростить модель. Боб Мартин в книге Чистый код описывает один из запахов кода: вызов методов разного уровня детализации. Этот тот случай, когда декомпозируя молекулы, программист иногда работает с ними как с набором атомов, а иногда, как с набором элементарных частиц. Скорее всего в данном случае можно отказаться от пятнадцати классов, в пользу трёх-четырёх, каждый из которых состоит из трёх-четырёх более мелких классов. Количество классов у вас увеличится, поскольку появятся промежуточные; зато каждый из них теперь будет простым для понимания. С другой стороны, бывают ситуации, когда большое количество параметров вполне допустимо. Мартин Фаулер описал паттерн Registry (Реестр), который предоставляет доступ к набору однотипных объектов. Скажем, вам нужно 5 или 6 репозиториев и, вместо того, чтобы передавать их по одному, вы заводите реестр репозиториев и передаёте только один этот объект. При этом у самого реестра в качестве параметров конструктора теперь передаются все репозитории, которые есть в вашей программе. Поскольку все репозитории относятся к одной категории объектов, код реестра получается простым и понятным. При этом конструктор реестра имеет 15-20 параметров и это всё равно выглядят ужасно. Я бы назвал этот случай неизбежным злом, и отнёс бы его причину к ограниченным выразительным средствам языка. В общем, иногда мы просто вынуждены писать так, потому что по другому язык не позволяет. Но будьте осторожны — прежде чем решить, что всё с вашим кодом в порядке, проверьте, что речь не идёт о первом случае: божественном объекте и смешении уровней детализации.

Ответ 2



Если вы хотите сократить запись при создании экземпляров вашего управляющего класса, то вам в первую очередь следует задуматься о значениях по умолчанию. Например, у вас есть нужные вам свойства, которые вы в конструкторе без аргументов инициализируете корректными значениями, как-то так: public class SomeManager { public object Property01 {get; set; } public object Property02 {get; set; } public int Property03 {get; set; } public double Property04 {get; set; } public string Property05 {get; set; } public int Property06 {get; set; } public DateTime Property07 {get; set; } public object Property08 {get; set; } public object Property09 {get; set; } public object Property10 {get; set; } public object Property11 {get; set; } public SomeManager() { Property01 = null; Property02 = null; Property03 = 100; Property04 = 0.33; Property05 = "Здесь был Вася"; Property06 = 8; Property07 = DateTime.UtcNow; Property08 = null; Property09 = null; Property10 = null; Property11 = null; } } Соответственно, при работе с экземплярами этого класса, вы сможете создавать их в нужной конфигурации, не расписывая портянку в пол-экрана. Вот так: var man1 = new SomeManager() { Property01 = SomeList, Property05 = "Никаких Вась здесь не было" } Или так: var man2 = new SomeManager() { Property03 = 42, Property04 = 0.5, Property07 = DateTime.Now.AddDays(-1) } Так же можно перегрузить конструктор столько раз, сколько вам надо, но если вариантов много - решение так себе. С другой стороны - в большинстве случаев использования классов-менеджеров желательно избегать (upd - я имею в виду God-объекты из другого ответа). Может быть, вам пересмотреть архитектуру?

Комментариев нет:

Отправить комментарий