Сколько конструкторов должен иметь класс?

В настоящее время я модифицирую класс с 9 различными конструкторами. Теперь, в целом, я считаю, что этот класс очень плохо разработан... поэтому мне интересно, плохо ли он для класса, у которого так много конструкторов.

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

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

Итак, мой вопрос в том, как я могу исправить эту проблему? Попытка уменьшить количество конструкторов? Если все существующие конструкторы принимают параметр ISomeManager?

Конечно, классу не нужны 9 конструкторов!... oh, и для этого в этом файле есть 6000 строк кода!

Здесь представлено цензованное представление конструкторов, о котором я говорю выше:

public MyManager()
    : this(new SomeManager()){} //this one I added

public MyManager(ISomeManager someManager) //this one I added
{
    this.someManager = someManager;
}

public MyManager(int id)
    : this(GetSomeClass(id)) {}

public MyManager(SomeClass someClass)
    : this(someClass, DateTime.Now){}

public MyManager(SomeClass someClass, DateTime someDate)
{
    if (someClass != null)
       myHelper = new MyHelper(someOtherClass, someDate, "some param");
}

public MyManager(SomeOtherClass someOtherClass)
    : this(someOtherClass, DateTime.Now){}

public MyManager(SomeOtherClass someOtherClass, DateTime someDate)
{
    myHelper = new MyHelper(someOtherClass, someDate, "some param");
}

public MyManager(YetAnotherClass yetAnotherClass)
    : this(yetAnotherClass, DateTime.Now){}

public MyManager(YetAnotherClass yetAnotherClass, DateTime someDate)
{
    myHelper = new MyHelper(yetAnotherClass, someDate, "some param");
}

Update:

Спасибо всем за ваши ответы... они были превосходны!

Просто подумал, что я дам обновление о том, что я закончил делать.

Чтобы устранить проблему с исключением нулевой ссылки, я изменил дополнительные конструкторы, чтобы взять ISomeManager.

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

Когда я перейду к перепроектированию MyManager, я буду искать способ извлечь эту функциональность в два или три разных класса... или сколько угодно, чтобы обеспечить выполнение SRP.

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

Ответ 1

Класс должен делать только одно и только одно. Если у него так много конструкторов, кажется, что это говорит о том, что он делает слишком много вещей.

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

Ответ 2

Как можно меньше,
Сколько необходимо.

Ответ 3

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

Ответ 4

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

Подумайте о наборе конструкторов в классе как математической функции, отображающей M-множества (где каждый набор является единственным списком аргументов конструктора) для N экземпляров данного класса. Теперь скажем, что класс Bar может принимать Foo в одном из своих конструкторов, а класс Foo принимает Baz в качестве аргумента конструктора, как показано здесь:

    Foo --> Bar
    Baz --> Foo

У нас есть возможность добавить в Bar другой конструктор, чтобы:

    Foo --> Bar
    Baz --> Bar
    Baz --> Foo

Это может быть удобно для пользователей класса Bar, но поскольку у нас уже есть путь от Baz до Bar (через Foo), нам не нужен этот дополнительный конструктор. Следовательно, именно здесь вызывается решение.

Но если мы вдруг добавим новый класс под названием Qux, и нам нужно создать экземпляр Bar из него: нам нужно добавить конструктор где-нибудь. Таким образом, это может быть:

    Foo --> Bar
    Baz --> Bar
    Qux --> Bar
    Baz --> Foo

ИЛИ

    Foo --> Bar
    Baz --> Bar
    Baz --> Foo
    Qux --> Foo

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

Ответ 5

Ответ: 1 (в отношении инъекций).

Вот блестящая статья на тему: Зависимость вставки анти-шаблона: несколько конструкторов

Подводя итог, ваш конструктор классов должен быть для инъекций зависимостей, и ваш класс должен быть открыт относительно своих зависимостей. Зависимость - это то, что вам нужно. Не то, что он хочет, или что-то, что он хотел бы, но может обойтись. Это что-то нужно.

Поэтому наличие необязательных параметров конструктора или перегруженных конструкторов не имеет для меня никакого смысла. Ваш единственный публичный конструктор должен определить ваш набор наборов зависимостей. Это контракт, предлагаемый вашим классом, в котором говорится: "Если вы дадите мне IDigitalCamera, ISomethingWorthPhotographing и IBananaForScale, я дам вам лучшую чертову IPhotographWithScale, которую вы можете себе представить. Но если вы скупитесь на любая из этих вещей, вы сами".

Здесь статья Марка Семанна, которая затрагивает некоторые из более тонких причин наличия канонического конструктора: Укажите свой уровень зависимости

Ответ 6

Это не просто этот класс, который вам нужно беспокоиться о повторном факторинге. Это все другие классы. И это, вероятно, всего лишь один поток запутанного мотка, который является вашей базой кода. У вас есть мое сочувствие... Я в одной лодке. Boss хочет, чтобы все было протестировано, не хочет переписывать код, чтобы мы могли unit test. В конце концов, сделайте несколько уродливых хаков, чтобы заставить его работать. Вам придется переписать все, что использует статический класс, чтобы больше не использовать его, и, вероятно, передать его намного больше... или вы можете обернуть его в статический прокси, который обращается к Singleton. Таким образом, вы, по крайней мере, высмеиваете синглтон и проверяете это.

Ответ 7

Ваша проблема заключается не в количестве конструкторов. У 9 конструкторов больше, чем обычно, но я не думаю, что это обязательно неправильно. Это, конечно, не источник вашей проблемы. Реальная проблема заключается в том, что первоначальный дизайн был всеми статическими методами. Это действительно особый случай, когда классы слишком тесно связаны. Нынешние классы связаны с идеей о том, что функции являются статическими. С этим классом вы не можете многое сделать. Если вы хотите сделать этот класс нестатичным, вам придется отменить все, что было записано в коде другими. Измените класс, который будет нестационарным, а затем обновите всех вызывающих лиц, чтобы создать экземпляр класса сначала (или получить один из одноэлементного). Один из способов найти всех вызывающих абонентов - сделать частные функции и дать компилятору рассказать вам.

В 6000 строк класс не очень сплочен. Вероятно, он пытается сделать слишком много. В идеальном мире вы бы реорганизовали класс (и тех, кто его назвал) на несколько меньших классов.

Ответ 8

Достаточно для выполнения своей задачи, но помните принцип единой ответственности, в котором говорится, что класс должен иметь только одну ответственность. Имея это в виду, вероятно, очень мало случаев, когда имеет смысл иметь 9 конструкторов.

Ответ 9

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

Глядя на ваш класс, есть четыре конструктора с телом:

public MyManager(ISomeManager someManager) //this one I added
{
    this.someManager = someManager;
}

public MyManager(SomeClass someClass, DateTime someDate)
{
    if (someClass != null)
       myHelper = new MyHelper(someOtherClass, someDate, "some param");
}

public MyManager(SomeOtherClass someOtherClass, DateTime someDate)
{
    myHelper = new MyHelper(someOtherClass, someDate, "some param");
}

public MyManager(YetAnotherClass yetAnotherClass, DateTime someDate)
{
    myHelper = new MyHelper(yetAnotherClass, someDate, "some param");
}

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

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

Если вас интересует этот подход, попробуйте найти копию книги "Рефакторинг для шаблонов", а затем перейдите на страницу "Конструкторы цепей" .

Ответ 10

Разумеется, класс должен иметь столько конструкторов, сколько требуется классу... это не значит, что плохой дизайн может взять на себя.

Конструкция класса должна заключаться в том, что конструктор создает действительный объект после завершения. Если вы можете сделать это с 1 параметром или 10 параметрами, то пусть будет так!

Ответ 11

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

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

С уважением.

Ответ 12

Единственный "законный" случай, который я вижу из вашего кода, - это то, что половина из них использует устаревший тип, который вы пытаетесь удалить из кода. Когда я работаю так, у меня часто есть двойные наборы конструкторов, где половина из них отмечена @Deprecated или @Obsolete. Но ваш код, похоже, выходит за рамки этого этапа....

Ответ 13

У меня обычно есть один, который может иметь некоторые параметры по умолчанию. Конструктор выполнит минимальную настройку объекта, чтобы он был действителен к моменту его создания. Если мне нужно больше, я создам статические методы factory. Вид вроде этого:

class Example {
public:
  static FromName(String newname) { 
    Example* result = new Example();
    result.name_ = newname;
    return result;
  }
  static NewStarter() { return new Example(); }

private:
  Example();
}

Хорошо, что на самом деле не очень хороший пример, я посмотрю, смогу ли я подумать о лучшем и отредактировать его.

Ответ 14

Тоннель: NONE

Посмотрите на язык Дилан. У него есть другая система.

Instat конструкторов вы добавляете больше значений в свои слоты (участники), а затем на другом языке. Вы можете добавить "ключевое слово init". Затем, если вы создадите экземпляр, вы можете установить для этого слота требуемое значение.

Конечно, вы можете установить 'required-init-keyword:', и есть больше возможностей, которые вы можете использовать.

Это работает, и это легко. Я не пропущу старую систему. Создание конструкторов (и деструкторов).

(кстати, его еще очень быстрый язык)


Ответ 15

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

Ответ 16

Конструктор должен иметь только те аргументы, которые являются обязательными для создания экземпляра этого класса. Все остальные переменные экземпляра должны иметь соответствующие методы getter и setter. Это сделает ваш код гибким, если вы планируете добавлять новые переменные экземпляра в будущем.

Infact, следуя принципу OO -

  • Для каждого проекта дизайна цель для низкой связи и высокой степени сцепления
  • Классы должны быть открыты для расширения, но закрыты для модификации.

    у вас должен быть дизайн вроде -

    import static org.apache.commons.lang3.Validate.*;
    public class Employee
    {
    private String name;
    private Employee() {}
    
    public String getName()
    {
        return name;
    }
    
    public static class EmployeeBuilder
    {
        private final Employee employee;
    
        public EmployeeBuilder()
        {
            employee = new Employee();
        }
    
        public EmployeeBuilder setName(String name)
        {
            employee.name = name;
            return this;
        }
    
        public Employee build()
        {
            validateFields();
            return employee;
        }
    
        private void validateFields()
        {
            notNull(employee.name, "Employee Name cannot be Empty");
        }
    }
    }
    

Java Class Design с помощью Builder