Рефакторинг крупных конструкторов - PullRequest
7 голосов
/ 31 августа 2011

В нашей модели предметной области есть несколько объектов, которые вы бы назвали оскорбительно большими конструкторами, такими большими, что IntelliSense перестает пытаться показать вам все это ...

Cue тип с 50 или около того аргументами, в основном типами значений, несколько ссылочных типов:

public class MyLegacyType
{
    public MyLegacyType(int a1, int a2, int a3, ... int a50) // etc
    {
    }
}

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

Однако, что может измениться, так это то, как создается тип. В настоящее время у нас есть разделы кода, которые страдают от:

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

Однако создается впечатление, что это на полпути к полному рефакторингу.

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

Существует также концепция конструкторов Fluent ... как для каждого свойства (WithIntA, WithIntB), так и для типа контейнера (WithTheseInts(IntContainer c)). Лично мне нравится этот подход от вызывающей стороны, но опять же для большого типа он становится многословным и кажется, что я только что переместил проблему вместо ее решения.

Мой вопрос, если кто-то похоронен в этом беспорядке, таков: Являются ли эти жизнеспособные тактики рефакторинга для проблемы? Пожалуйста, дополните любой ответ некоторым опытом, подводными камнями или критикой. Я склоняюсь к вещам Fluent, потому что я думаю, что это выглядит круто, вполне читабельно и удобно для слияния.

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

Ответы [ 3 ]

12 голосов
/ 31 августа 2011

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

public MyLegacyType(SomeConcept foo, AnotherConcept bar, ...)
{
}

, где только логика, необходимая для организации между концепциями, остается в MyLegacyType (любая логика, специфичная для SomeConcept, идет туда и т. Д.).

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

4 голосов
/ 31 августа 2011

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

Например:

public class MyLegacyType
{
    public MyLegacyType(MyConfiguration configuration) // etc
    {
      // ...
    }
}

public class MyConfiguration
{
   public int Value1 { get; set; }
   public int Value2 { get; set; }
   // ...
}

А потом:

var myInstance = new MyLegacyType(new MyConfiguration
{
  Value1 = 123,
  Value2 = 456
});
1 голос
/ 31 августа 2011

Есть одна вещь, в которой я не уверен в вашем вопросе, и именно поэтому вам нужны все такие параметры в конструкторе?Используете ли вы все параметры в коде конструктора?Ваша проблема с intellisense, вероятно, связана с наличием слишком большого количества параметров в одном методе.Наличие большого количества полей / свойств для одного типа не вызовет никаких проблем.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...