Что лучший способ состоит в том, чтобы осуществить рефакторинг метод, который имеет слишком многих (6 +) параметры?

У меня есть еще одно быстрое решение, реализуя это (что на самом деле является нечистым, но вы получаете идею):

public static void main(String[] args) {
    System.out.println(StreamUtil.iterate(1, o -> o + 1).terminateOn(15)
            .map(o -> o.toString()).collect(Collectors.joining(", ")));
}

static interface TerminatedStream<T> {
    Stream<T> terminateOn(T e);
}

static class StreamUtil {
    static <T> TerminatedStream<T> iterate(T seed, UnaryOperator<T> op) {
        return new TerminatedStream<T>() {
            public Stream<T> terminateOn(T e) {
                Builder<T> builder = Stream.<T> builder().add(seed);
                T current = seed;
                while (!current.equals(e)) {
                    current = op.apply(current);
                    builder.add(current);
                }
                return builder.build();
            }
        };
    }
}
92
задан recursive 13 January 2009 в 16:07
поделиться

22 ответа

Лучший способ состоял бы в том, чтобы найти способы собрать в группу аргументы. Это принимает, и действительно только работает, если, Вы закончили бы с несколькими "группировками" аргументов.

, Например, при передаче спецификации для прямоугольника можно передать x, y, ширину и высоту, или Вы могли просто передать прямоугольный объект, который содержит x, y, ширину и высоту.

Ищут вещи как это при рефакторинге для чистки его несколько. Если аргументы действительно не могут быть объединены, начните смотреть на то, есть ли у Вас нарушение Единственного Принципа Ответственности.

93
ответ дан Matthew Brubaker 5 November 2019 в 13:38
поделиться

Одно соображение - какое из значений было бы только для чтения, как только объект создается?

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

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

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

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

0
ответ дан AnthonyWJones 5 November 2019 в 13:38
поделиться

Это зависит от того, какие аргументы Вы имеете, но если они - много булевых значений/опций, возможно, Вы могли бы использовать Перечисление Флага?

0
ответ дан scottm 5 November 2019 в 13:38
поделиться

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

другая опция я искал бы в выполнении этого вида, осуществляют рефакторинг, были бы группы связанных параметров, которые могли бы быть лучше обработаны как независимый объект. Используя Прямоугольный класс из более раннего ответа как пример, конструктор, который берет параметры для x, y, высоты и ширины, мог факторизовать X и Y в Точечный объект, позволив Вам передать три параметра конструктору Rectangle. Или пойдите немного далее и сделайте его двумя параметрами (UpperLeftPoint, LowerRightPoint), но это было бы более радикальным рефакторингом.

1
ответ дан Dave Sherohman 5 November 2019 в 13:38
поделиться

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

В некоторых случаях, конструктор с 7 параметрами может указать на плохую иерархию классов: в этом случае структура/класс помощника, предложенная выше, обычно является хорошим подходом, но тогда Вы также склонны заканчивать с загрузками структур, которые являются просто наборами свойств и не делают ничего полезного. Конструктор с 8 аргументами мог бы также указать, что Ваш класс слишком универсален / слишком универсальный, таким образом, требуется много опций быть действительно полезным. В этом случае можно или осуществить рефакторинг класс или реализовать статических конструкторов, которые скрывают настоящих сложных конструкторов: например, Shniz. NewBaz (нечто, панель) мог на самом деле вызвать настоящего конструктора, передающего правильные параметры.

0
ответ дан axel_c 5 November 2019 в 13:38
поделиться

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

Предпочитают маленькие классы/методы по большому. Помните единственный принцип ответственности.

1
ответ дан Brian Rasmussen 5 November 2019 в 13:38
поделиться

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

1
ответ дан Frank Schwieterman 5 November 2019 в 13:38
поделиться

Можно обменять сложность на строки исходного кода. Если сам метод делает слишком много (швейцарский нож) пытаются разделить на два его задачи путем создания другого метода. Если метод прост, только требуется слишком много параметров тогда, так называемые объекты параметра являются способом пойти.

1
ответ дан Karl 5 November 2019 в 13:38
поделиться

Как насчет того, чтобы не установить его во внезапно в конструкторах, но сделать его через свойства/методы set ? Я видел некоторые классы.NET, которые используют этот подход такой как Process класс:

        Process p = new Process();

        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.FileName = "cmd";
        p.StartInfo.Arguments = "/c dir";
        p.Start();
1
ответ дан Gant 5 November 2019 в 13:38
поделиться

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

1
ответ дан Instantsoup 5 November 2019 в 13:38
поделиться

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

2
ответ дан user54650 5 November 2019 в 13:38
поделиться

Можно попытаться сгруппировать параметр в кратные числа значимая структура/класс (если возможный).

3
ответ дан Julien Hoarau 5 November 2019 в 13:38
поделиться

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

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

2
ответ дан Andrzej Doyle 5 November 2019 в 13:38
поделиться

Я использовал бы методы set свойства и конструктор по умолчанию. C# 3.0 имеет некоторый хороший синтаксис, чтобы сделать это автоволшебно.

return new Shniz { Foo = foo,
                   Bar = bar,
                   Baz = baz,
                   Quuz = quux,
                   Fred = fred,
                   Wilma = wilma,
                   Barney = barney,
                   Dino = dino,
                   Donkey = donkey
                 };

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

4
ответ дан tvanfosson 5 November 2019 в 13:38
поделиться

Вы не предоставили достаточно информации для гарантирования хорошего ответа. Длинный список параметров не по сути плох.

Shniz (нечто, панель, baz, quux, fred, wilma, barney, динозавр, осел)

мог быть интерпретирован как:

void Shniz(int foo, int bar, int baz, int quux, int fred, 
           int wilma, int barney, int dino, int donkey) { ...

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

// old way
Shniz(1,2,3,2,3,2,1,2);
Shniz(1,2,2,3,3,2,1,2); 

//versus
ShnizParam p = new ShnizParam { Foo = 1, Bar = 2, Baz = 3 };
Shniz(p);

, Кроме того, если Вы имели:

void Shniz(Foo foo, Bar bar, Baz baz, Quux quux, Fred fred, 
           Wilma wilma, Barney barney, Dino dino, Donkey donkey) { ...

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

Кроме того, некоторые дополнительные параметры? Есть ли переопределение метода (то же имя метода, но различные сигнатуры методов?) Эти виды деталей весь вопрос относительно того, каков лучший ответ.

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

, Как Вы видите, существует больше чем 1 корректный ответ на этот вопрос. Выбрать.

4
ответ дан Robert Paulson 5 November 2019 в 13:38
поделиться

Если бы некоторые параметры конструктора являются дополнительными, имеет смысл использовать разработчика, который получил бы обязательные параметры в конструкторе и имел бы методы для дополнительных, возвращая разработчика, чтобы использоваться как это:

return new Shniz.Builder(foo, bar).baz(baz).quux(quux).build();

детали этого описаны в Эффективном Java, 2-м Ed., p. 11. Для аргументов метода, та же книга (p. 189), описывает три подхода для сокращения списков параметров:

  • Повреждение метод в несколько методов, которые берут меньше аргументов
  • , Создает статические членские классы помощника, чтобы представить группы параметров, т.е. передать DinoDonkey вместо dino и donkey
  • , Если параметры являются дополнительными, разработчик выше может быть принят для методов, определив объект для всех параметров, устанавливание необходимых и затем называя некоторых выполняет метод на нем
5
ответ дан Fabian Steeg 5 November 2019 в 13:38
поделиться

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

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

Этот вид запаха (так как мы говорим о рефакторинге, это ужасное слово кажется соответствующим...) мог бы также быть обнаружен для объектов, которые имеют много (чтение: любой) свойства или методы get/методы set.

7
ответ дан Daren Thomas 5 November 2019 в 13:38
поделиться

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

, Например, вместо:

driver.connect(host, user, pass)

Вы могли использовать

config = new Configuration()
config.setHost(host)
config.setUser(user)
config.setPass(pass)
driver.connect(config)

YMMV

9
ответ дан Wouter Lievens 5 November 2019 в 13:38
поделиться

Это заключается в кавычки из книги Fowler и Beck: "Рефакторинг"

Длинный Список параметров

В наши ранние дни программирования нам преподавали передать в как параметры все необходимое стандартной программой. Это было понятно, потому что альтернатива была глобальными данными, и глобальные данные являются злыми и обычно болезненными. Объекты изменяют эту ситуацию, потому что, если у Вас нет чего-то, которое Вам нужно, можно всегда спрашивать другой объект получить ее для Вас. Таким образом с объектами Вы не передаете во всем потребности метода; вместо этого Вы передаете достаточно так, чтобы метод мог добраться до всего, в чем он нуждается. Многое из того, в чем нужен метод, доступно на классе хоста метода. В объектно-ориентированных программах списки параметров имеют тенденцию быть значительно уменьшенными, чем в традиционных программах. Это хорошо, потому что длинные списки параметров трудно понять, потому что они становятся непоследовательными и трудными использовать, и потому что Вы навсегда изменяете их, поскольку Вам нужно больше данных. Большинство изменений удалено путем передачи объектов, потому что Вы будете должны, намного более вероятно, выполнить только несколько запросов для достигания новой части данных. Используйте Параметр Замены с Методом, когда можно получить данные в одном параметре путем выполнения запроса объекта, Вы уже знаете о. Этот объект мог бы быть полем, или это мог бы быть другой параметр. Используйте Заповедник Целый Объект взять набор данных, подбираемых из объекта и заменить его самим объектом. Если у Вас есть несколько элементов данных без логического объекта, использование Представляют Объект параметра. Существует одно важное исключение к внесению этих изменений. Это - когда Вы явно не хотите создавать зависимость от названного объекта до большего объекта. В тех случаях, распаковывающих данные и отправляющих его вперед как параметры, разумно, но обратите внимание на включенную боль. Если список параметров является слишком длинным или изменяется слишком часто, необходимо заново продумать структуру зависимостей.

10
ответ дан Youssef 5 November 2019 в 13:38
поделиться

Если это - конструктор, особенно если существует несколько перегруженных вариантов, необходимо посмотреть на шаблон Разработчика:

Foo foo = new Foo()
          .configBar(anything)
          .configBaz(something, somethingElse)
          // and so on

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

20
ответ дан kdgregory 5 November 2019 в 13:38
поделиться

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

у Вас есть несколько опций:

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

class C
{
    public string S { get; set; }
    public int I { get; set; }
}

new C { S = "hi", I = 3 };

Однако Вы теряете неизменность, и Вы теряете способность гарантировать, что необходимые значения установлены перед использованием объекта во время компиляции.

разработчик Pattern .

Думают об отношениях между string и StringBuilder. Можно получить это для собственных классов. Мне нравится реализовывать его как вложенный класс, таким образом, класс C связал класс C.Builder. Мне также нравится быстрый интерфейс на разработчике. Сделанный правильно, можно получить синтаксис как это:

C c = new C.Builder()
    .SetX(4)    // SetX is the fluent equivalent to a property setter
    .SetY("hello")
    .ToC();     // ToC is the builder pattern analog to ToString()

// Modify without breaking immutability
c = c.ToBuilder().SetX(2).ToC();

// Still useful to have a traditional ctor:
c = new C(1, "...");

// And object initializer syntax is still available:
c = new C.Builder { X = 4, Y = "boing" }.ToC();

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

class C {
    field I X
    field string Y
}

, Таким образом, я могу генерировать во время компиляции. partial классы позволяют мне расширить и основной класс и разработчика, не изменяя сгенерированный код.

"Представляют Объект параметра" рефакторинг . Посмотрите Осуществлять рефакторинг Каталог . Идея состоит в том, что Вы берете некоторые параметры, Вы являетесь передающими и вставляете их к новому типу, и затем передаете экземпляр того типа вместо этого. Если Вы сделаете это без взглядов, Вы закончите назад, где Вы запустили:

new C(a, b, c, d);

становится

new C(new D(a, b, c, d));

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

  1. Ищут подмножества из параметров, которые имеют смысл вместе. Просто бессмысленно собирание в группу всех параметров функции не получает Вас очень; цель состоит в том, чтобы иметь группировки, которые имеют смысл. Вы будете знать, что разобрались в нем, когда название нового типа очевидно.

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

  3. Ищут функциональность, которая находится в существующем коде, но принадлежит на новом типе.

, Например, возможно, Вы видите некоторый код, который похож:

bool SpeedIsAcceptable(int minSpeed, int maxSpeed, int currentSpeed)
{
    return currentSpeed >= minSpeed & currentSpeed < maxSpeed;
}

Вы могли взять minSpeed и maxSpeed параметры и поместить их в новый тип:

class SpeedRange
{
   public int Min;
   public int Max;
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return currentSpeed >= sr.Min & currentSpeed < sr.Max;
}

Это лучше, но действительно использовать в своих интересах новый тип, перемещать сравнения в новый тип:

class SpeedRange
{
   public int Min;
   public int Max;

   bool Contains(int speed)
   {
       return speed >= min & speed < Max;
   }
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return sr.Contains(currentSpeed);
}

И теперь мы добираемся где-нибудь: реализация [1 115] теперь говорит, что Вы имеете в виду, и у Вас есть полезный, допускающий повторное использование класс. (Следующий очевидный шаг должен сделать SpeedRange в к [1 117].)

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

101
ответ дан Jay Bazuzi 5 November 2019 в 13:38
поделиться

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

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

Посмотрите ниже:

public class Toto {
    private final String state0;
    private final String state1;
    private final String state2;
    private final String state3;

    public Toto(String arg0, String arg1, String arg2, String arg3) {
        this.state0 = arg0;
        this.state1 = arg1;
        this.state2 = arg2;
        this.state3 = arg3;
    }

    public static class TotoBuilder {
        private String arg0;
        private String arg1;
        private String arg2;
        private String arg3;

        public TotoBuilder addArg0(String arg) {
            this.arg0 = arg;
            return this;
        }
        public TotoBuilder addArg1(String arg) {
            this.arg1 = arg;
            return this;
        }
        public TotoBuilder addArg2(String arg) {
            this.arg2 = arg;
            return this;
        }
        public TotoBuilder addArg3(String arg) {
            this.arg3 = arg;
            return this;
        }

        public Toto newInstance() {
            // maybe add some validation ...
            return new Toto(this.arg0, this.arg1, this.arg2, this.arg3);
        }
    }

    public static void main(String[] args) {
        Toto toto = new TotoBuilder()
            .addArg0("0")
            .addArg1("1")
            .addArg2("2")
            .addArg3("3")
            .newInstance();
    }

}
0
ответ дан Guillaume 5 November 2019 в 13:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: