Який найкращий спосіб переробити метод, який має занадто багато параметрів (6+)?


102

Іноді я стикаюся з методами з незручною кількістю параметрів. Частіше за все вони здаються конструкторами. Здається, що має бути кращий спосіб, але я не бачу, що це таке.

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

Відповіді:


94

Найкращим способом було б знайти способи групування аргументів разом. Це передбачає, і справді працює лише в тому випадку, якщо ви закінчилися безліччю "групувань" аргументів.

Наприклад, якщо ви передаєте специфікацію для прямокутника, ви можете передавати х, у, ширину і висоту, або ви можете просто передати об'єкт прямокутника, що містить х, у, ширину і висоту.

Шукайте подібні речі під час рефакторингу, щоб дещо почистити. Якщо аргументи справді неможливо поєднати, почніть розбиратись у тому, чи є у вас порушення Принципу єдиної відповідальності.


4
Гарна ідея, але поганий приклад; конструктор для Прямокутника повинен був мати 4 аргументи. Це мало б більше сенсу, якби метод очікував 2 набору координат / розмірів прямокутника. Тоді ви могли пропустити 2 прямокутника замість x1, x2, y1, y2 ...
Програматор поза законом

2
Досить справедливо. Як я вже сказав, це дійсно має сенс зробити, якщо ви закінчитеся з кількома логічними угрупованнями.
Метью Брубакер

23
+1: До єдиної відповідальності - це один з небагатьох коментарів до всіх відповідей, який справді стосується справжнього питання. Який об’єкт справді потребує 7 незалежних значень для формування своєї ідентичності.
AnthonyWJones

6
@AnthonyWJones Я не згоден. Дані для поточного погодного стану можуть мати набагато більше незалежних значень для формування його ідентичності.
funct7

107

Я припускаю, що ви маєте на увазі 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()зараз говорить про те, що ви маєте на увазі, і у вас є корисний, багаторазовий клас. (Наступний очевидний крок повинен зробити , SpeedRangeщоб Range<Speed>.)

Як бачите, Введення параметра «Об’єкт» було гарним початком, але його справжня цінність полягала в тому, що він допоміг нам виявити корисний тип, який відсутній у нашій моделі.


4
Я б запропонував спершу спробувати "Ввести об'єкт параметру", а лише інші варіанти, якщо ви не можете знайти хороший об'єкт параметрів для створення.
Дуглас Лідер

4
відмінна відповідь. якщо ви згадали пояснення рефакторингу до синтаксичних цукрів c #, це було б визнано вищим IMHO.
rpattabi

10
О-о! +1 для "Ви дізнаєтесь, що ви зрозуміли це правильно, коли назва нового типу очевидна".
Шон Макміллан

20

Якщо це конструктор, особливо якщо є кілька перевантажених варіантів, слід переглянути шаблон Builder:

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

Якщо це звичайний метод, слід подумати про зв’язки між переданими значеннями та, можливо, створити об’єкт передачі.


Відмінна відповідь. Можливо, навіть більш релевантна, ніж відповідь "поставити параметри в клас", яку дали всі (включаючи мене).
Wouter Lievens

1
Можливо, погана ідея зробити свій клас змінним просто, щоб уникнути занадто великої кількості параметрів для конструктора.
Програматор поза законом

@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
Я, безумовно, хотів би перший фрагмент коду більше. Я згоден, що існує певна межа, над якою параметри numbe rof стають некрасивими, але на мій смак, 3 було б прийнятним.
blabla999

10

Про це цитує книга Фаулера і Бека: "Рефакторинг"

Довгий список параметрів

У наші дні раннього програмування нас вчили передавати як параметри все необхідне для розпорядку. Це було зрозуміло, оскільки альтернативою були глобальні дані, а глобальні дані є злими і зазвичай болючими. Об'єкти змінюють цю ситуацію, оскільки якщо у вас немає чогось необхідного, ви завжди можете попросити інший об’єкт, щоб отримати його за вас. Таким чином, з об'єктами ви не передаєте все необхідне методу; натомість ви проходите достатньо, щоб метод міг дістатись до всього необхідного. Багато речей, що потрібні методу, доступні в хостовому класі методу. У об'єктно-орієнтованих програмах списки параметрів, як правило, значно менші, ніж у традиційних програмах. Це добре, тому що довгі списки параметрів важко зрозуміти, оскільки вони стають непослідовними і важкими у використанні, і тому що ви назавжди їх змінюєте, оскільки вам потрібно більше даних. Більшість змін видаляються, передаючи об'єкти, тому що вам набагато більше шансів зробити лише кілька запитів, щоб отримати новий фрагмент даних. Використовуйте Замінити параметр методом, коли ви можете отримати дані в одному параметрі, зробивши запит об'єкта, про який ви вже знаєте. Цей об'єкт може бути полем, або це може бути інший параметр. Використовуйте Зберегти цілий об’єкт, щоб взяти купу даних, зібраних з об'єкта, і замінити їх самим об'єктом. Якщо у вас є декілька елементів даних, що не мають логічного об'єкта, використовуйте Введення об'єкта параметра. Є один важливий виняток із внесення цих змін. Це коли ви явно не хочете створювати залежність від викликаного об'єкта до більшого об'єкта. У цих випадках розумне розпакування даних та надсилання їх як параметрів, але зверніть увагу на біль, що виникає. Якщо список параметрів занадто довгий або змінюється занадто часто, вам потрібно переглянути свою структуру залежності.


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

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


2
Це дозволило б існувати об'єкту t новий Shniz (). Хороша реалізація ОО передбачає мінімізацію можливості об'єктів, що існують у неповному стані.
AnthonyWJones

Взагалі, будь-яка мова з синтаксисом вродженого хешу / словника має адекватну заміну названих параметрів (що чудово і часто вимагає цих ситуацій, але чомусь єдиною популярною мовою, яка їх підтримує, є найгірша на планеті) .
хаос

4

Ви не надали достатньо інформації, щоб гарантувати хорошу відповідь. Довгий список параметрів не є по суті поганим.

Шніз (foo, bar, baz, quux, fred, wilma, barney, dino, oskey)

можна інтерпретувати як:

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 правильної відповіді. Візьміть свій вибір.


3

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


2

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

Якщо набір параметрів неможливо перетворити на значущий об'єкт, це, мабуть, знак, який Shnizробить занадто багато, і рефакторинг повинен включати розбиття методу на окремі проблеми.


2

Ви можете торгувати складністю рядків вихідного коду. Якщо сам метод робить занадто багато (швейцарський ніж), спробуйте вдвічі зменшити його завдання, створивши інший метод. Якщо метод простий, йому потрібно занадто багато параметрів, тоді так звані об’єкти параметрів - це шлях.


2

Якщо ваша мова підтримує це, використовуйте названі параметри та робіть якомога більше необов'язкових (з розумними значеннями за замовчуванням).


1

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


1

Як щодо того, щоб не встановити його відразу на конструкторах, а зробити це за допомогою властивостей / сетерів ? Я бачив кілька класів .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();

3
C # 3 насправді має синтаксис для цього легко: ініціалізатори об'єктів.
Дарен Томас

1

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


1

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

Віддавайте перевагу малим класам / методам над великими. Пам’ятайте про принцип єдиної відповідальності.


Проблема з членами і властивостями екземпляра полягає в тому, що вони 1) повинні бути записані, 2) не можуть бути встановлені. У випадку конструктора є певні поля, які я хочу переконатись, що вони заповнені, перш ніж екземпляр буде дозволений.
рекурсивна

@recursive - я не погоджуюся, що поля / властивості завжди мають бути записані. Для невеликих занять є багато випадків, коли члени, що читають, мають сенс.
Брайан Расмуссен

1

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

Інший варіант, на який я хотів би звернути увагу при виконанні такого роду рефактора, - це групи пов'язаних параметрів, які можуть бути краще оброблені як самостійний об'єкт. Використовуючи клас Прямокутник з попередньої відповіді в якості прикладу, конструктор, який приймає параметри для x, y, висоти та ширини, може розподілити x і y в об'єкт Point, що дозволяє передавати три параметри конструктору прямокутника. Або йдіть трохи далі і зробіть для нього два параметри (UpperLeftPoint, LowerRightPoint), але це було б більш радикальним рефакторингом.


0

Це залежить від того, які аргументи у вас є, але якщо вони є великими булевими значеннями / параметрами, можливо, ви могли б використати прапор Енума?


0

Я думаю, що ця проблема глибоко пов'язана з областю проблеми, яку ви намагаєтеся вирішити разом із класом.

У деяких випадках 7-параметричний конструктор може вказувати на погану ієрархію класів: у цьому випадку допоміжна структура / клас, запропонована вище, як правило, є хорошим підходом, але тоді ви також схильні закінчуватись навантаженнями структур, які є лише пакетами властивостей і не робіть нічого корисного. 8-аргументальний конструктор також може вказувати на те, що ваш клас занадто загальний / занадто універсальний, тому йому потрібно багато варіантів, щоб бути дійсно корисними. У цьому випадку ви можете або рефакторировать клас, або застосувати статичні конструктори, які приховують реальні складні конструктори: наприклад. Shniz.NewBaz (foo, bar) насправді може викликати реального конструктора, передаючи потрібні параметри.


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

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.