Как лучше всего реорганизовать метод, у которого слишком много (6+) параметров?

103

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

return new Shniz(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)

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

ShnizArgs args = new ShnizArgs(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)
return new Shniz(args);

Так что это не похоже на улучшение. Итак, каков лучший подход?

рекурсивный
источник
Вы сказали «структура». Этот термин имеет разные значения в разных языках программирования. Что ты имеешь в виду?
Джей Базузи,
1
Если вы ищете определенный язык для устранения неоднозначности, используйте C #. Но, по сути, это всего лишь простой мешок с недвижимостью. Он имеет разные именованные свойства с разными типами. Может быть определен как класс, хеш-таблица, структура или что-то еще.
рекурсивный
В этой статье есть хорошее понимание темы. Специфично для Javascript, но принципы могут быть повторно применены к другим языкам.
lala

Ответы:

93

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

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

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

Мэтью Брубейкер
источник
5
Хорошая идея, но плохой пример; конструктор для Rectangle должен иметь 4 аргумента. Это имело бы больший смысл, если бы метод ожидал 2 набора координат / размеров прямоугольника. Тогда вы могли бы передать 2 прямоугольника вместо x1, x2, y1, y2 ...
Outlaw Programmer
2
Справедливо. Как я уже сказал, это действительно имеет смысл делать только в том случае, если в результате получается несколько логических группировок.
Мэтью Брубейкер,
23
+1: «К единой ответственности», это один из немногих комментариев во всех ответах, который действительно касается истинной проблемы. Какому объекту действительно нужны 7 независимых значений, чтобы сформировать его идентичность.
AnthonyWJones,
6
@AnthonyWJones Я не согласен. Данные о текущих погодных условиях могут иметь гораздо больше независимых значений, чтобы сформировать свою идентичность.
funct7
109

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

У вас есть несколько вариантов:

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

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

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

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

Строитель шаблон .

Подумайте об отношениях между 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);
}

И вот мы кое-что достигли: реализация SpeedIsAcceptable()now говорит о том, что вы имеете в виду, и у вас есть полезный, многоразовый класс. (Следующий очевидный шаг должен сделать , SpeedRangeчтобы Range<Speed>.)

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

Джей Базузи
источник
4
Я бы посоветовал сначала попробовать «Ввести объект параметра» и только откатиться к другим параметрам, если вы не можете найти хороший объект параметра для создания.
Дуглас Лидер,
4
отличный ответ. Если бы вы упомянули объяснение рефакторинга до синтаксических сахаров С #, это было бы оценено выше IMHO.
rpattabi
10
Ой! +1 за «Вы поймете, что сделали все правильно, когда название нового типа станет очевидным».
Шон Макмиллан
20

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

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

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

kdgregory
источник
Отличный ответ. Возможно, даже более актуальным, чем ответ «поместите параметры в класс», который все (включая меня) дали.
Воутер Ливенс,
1
Вероятно, делать свой класс изменяемым - плохая идея, чтобы не передавать конструктору слишком много параметров.
Outlaw Programmer
@outlaw - если изменчивость вызывает беспокойство, вы можете легко реализовать семантику «запускать один раз». Однако большое количество параметров ctor часто указывает на необходимость настройки (или, как отмечали другие, на класс, пытающийся сделать слишком много вещей). (продолжение)
kdgregory
Хотя вы можете использовать внешнюю конфигурацию, во многих случаях в этом нет необходимости, особенно если она управляется состоянием программы или является стандартной для данной программы (подумайте о синтаксических анализаторах XML, которые могут быть осведомлены о пространстве имен, проверять с помощью различных инструментов и т. Д.).
kdgregory
Мне нравится шаблон построителя, но я разделяю свои неизменяемые и изменяемые типы построителей, такие как string / StringBuilder, но использую вложенные классы: Foo / Foo.Builder. У меня есть сценарий PowerShell для создания кода для простых классов данных.
Джей Базузи,
10

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

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

driver.connect(host, user, pass)

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

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

YMMV

Воутер Ливенс
источник
5
Мне определенно больше понравится первый фрагмент кода. Я согласен, что есть некий предел, выше которого число параметров становится некрасивым, но на мой вкус 3 было бы приемлемо.
blabla999
10

Это цитата из книги Фаулера и Бека: «Рефакторинг».

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

В первые дни программирования нас учили передавать в качестве параметров все, что нужно программе. Это было понятно, потому что альтернативой были глобальные данные, а глобальные данные - зло и обычно болезненно. Объекты меняют эту ситуацию, потому что, если у вас нет чего-то, что вам нужно, вы всегда можете попросить другой объект получить это для вас. Таким образом, с объектами вы не передаете все, что нужно методу; вместо этого вы передаете достаточно, чтобы метод мог получить все, что ему нужно. Многое из того, что нужно методу, доступно в классе хоста метода. В объектно-ориентированных программах списки параметров обычно намного меньше, чем в традиционных программах. Это хорошо, потому что длинные списки параметров трудно понять, потому что они становятся непоследовательными и трудными в использовании. и потому что вы постоянно меняете их, поскольку вам нужно больше данных. Большинство изменений удаляются путем передачи объектов, потому что вам, скорее всего, потребуется сделать всего пару запросов, чтобы получить новый фрагмент данных. Используйте Replace Parameter with Method, когда вы можете получить данные в одном параметре, сделав запрос объекта, о котором вы уже знаете. Этот объект может быть полем или другим параметром. Используйте «Сохранить весь объект», чтобы взять набор данных, полученных от объекта, и заменить их самим объектом. Если у вас есть несколько элементов данных без логического объекта, используйте Introduce Parameter Object. Есть одно важное исключение из внесения этих изменений. Это когда вы явно не хотите создавать зависимость от вызываемого объекта к более крупному объекту. В таких случаях распаковка данных и отправка их в качестве параметров разумны, но обратите внимание на сопутствующие проблемы. Если список параметров слишком длинный или изменяется слишком часто, вам необходимо переосмыслить структуру зависимостей.

Юсеф
источник
7

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

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

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

Дарен Томас
источник
7

Когда я вижу длинные списки параметров, мой первый вопрос: не слишком ли много делает эта функция или объект. Рассматривать:

EverythingInTheWorld earth=new EverythingInTheWorld(firstCustomerId,
  lastCustomerId,
  orderNumber, productCode, lastFileUpdateDate,
  employeeOfTheMonthWinnerForLastMarch,
  yearMyHometownWasIncorporated, greatGrandmothersBloodType,
  planetName, planetSize, percentWater, ... etc ...);

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

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

 Order order=new Order(customerName, customerAddress, customerCity,
   customerState, customerZip,
   orderNumber, orderType, orderDate, deliveryDate);

У нас могло быть:

Customer customer=new Customer(customerName, customerAddress,
  customerCity, customerState, customerZip);
Order order=new Order(customer, orderNumber, orderType, orderDate, deliveryDate);

Хотя, конечно, я предпочитаю функции, которые принимают всего 1, 2 или 3 параметра, иногда мы должны признать, что на самом деле эта функция занимает много, и что количество само по себе не создает сложности. Например:

Employee employee=new Employee(employeeId, firstName, lastName,
  socialSecurityNumber,
  address, city, state, zip);

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

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

void updateCustomer(String type, String status,
  int lastOrderNumber, int pastDue, int deliveryCode, int birthYear,
  int addressCode,
  boolean newCustomer, boolean taxExempt, boolean creditWatch,
  boolean foo, boolean bar);

И затем я вижу, что он вызван:

updateCustomer("A", "M", 42, 3, 1492, 1969, -7, true, false, false, true, false);

Я забеспокоился. Глядя на звонок, совсем не понятно, что означают все эти загадочные числа, коды и флаги. Это просто просьба об ошибках. Программист может легко запутаться в порядке следования параметров и случайно переключить два, и если они одного типа данных, компилятор просто примет его. Я бы предпочел иметь подпись, в которой все эти вещи являются перечислениями, поэтому вызов передает такие вещи, как Type.ACTIVE вместо «A» и CreditWatch.NO вместо «false» и т. Д.

Джей
источник
5

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

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

Подробности этого описаны в Эффективной Java, 2-е изд., Стр. 11. Что касается аргументов метода, в той же книге (стр. 189) описаны три подхода к сокращению списков параметров:

  • Разбейте метод на несколько методов, которые принимают меньше аргументов
  • Создайте статические вспомогательные классы-члены для представления групп параметров, т.е. передавайте DinoDonkeyвместо dinoиdonkey
  • Если параметры являются необязательными, приведенный выше построитель может быть адаптирован для методов, определяя объект для всех параметров, устанавливая необходимые, а затем вызывая для него некоторый метод выполнения.
Фабиан Штег
источник
4

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

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

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

tvanfosson
источник
2
Это позволило бы существовать объекту t new Shniz (). Хорошая реализация объектно-ориентированного программирования должна стремиться минимизировать возможность существования объектов в неполном состоянии.
AnthonyWJones,
В общем, любой язык с собственным синтаксисом хэша / словаря поставляется с адекватной заменой именованных параметров (которые великолепны и часто требуются в этих ситуациях, но по какой-то причине единственный популярный язык, поддерживающий их, является худшим на планете) .
хаос
4

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

Шниз (фу, бар, баз, куукс, фред, вильма, барни, дино, осел)

можно интерпретировать как:

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) { ...

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

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

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

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

Роберт Полсон
источник
3

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

Жюльен Хорау
источник
2

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

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

Анджей Дойл
источник
2

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

Карл
источник
2

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

user54650
источник
1

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

Мгновенный суп
источник
1

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

        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();
Гант
источник
3
В C # 3 на самом деле есть синтаксис, позволяющий легко это сделать: инициализаторы объектов.
Дарен Томас
1

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

Франк Швитерман
источник
1

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

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

Брайан Расмуссен
источник
Проблема с членами и свойствами экземпляра заключается в том, что они 1) должны быть доступны для записи, 2) могут быть не установлены. В случае конструктора есть определенные поля, которые я хочу обеспечить, чтобы они были заполнены до того, как экземпляру будет разрешено существовать.
рекурсивный
@recursive - Я не согласен с тем, что поля / свойства всегда должны быть доступны для записи. Для небольших классов есть много случаев, когда члены только для чтения имеют смысл.
Брайан Расмуссен,
1

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

Другой вариант, который я бы искал при выполнении такого рода рефакторинга, - это группы связанных параметров, которые лучше обрабатывать как независимый объект. Используя класс Rectangle из более раннего ответа в качестве примера, конструктор, который принимает параметры для x, y, высоты и ширины, может вынести x и y в объект Point, что позволит вам передать три параметра конструктору Rectangle. Или пойти немного дальше и сделать его двумя параметрами (UpperLeftPoint, LowerRightPoint), но это будет более радикальный рефакторинг.

Дэйв Шерохман
источник
0

Это зависит от того, какие аргументы у вас есть, но если они содержат много логических значений / параметров, возможно, вы могли бы использовать Flag Enum?

Скотт
источник
0

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

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

axel_c
источник
0

Одно из соображений заключается в том, какое из значений будет доступно только для чтения после создания объекта?

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

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

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

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

ЭнтониУ Джонс
источник
0

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

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

Увидеть ниже :

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();
    }

}
Гийом
источник