Чи погана практика змусити сетера повернути "це"?


249

Хороша чи погана ідея змусити сетерів у Java повернути "це"?

public Employee setName(String name){
   this.name = name;
   return this;
}

Ця модель може бути корисною, оскільки тоді ви можете зв'язати ланцюги, як це:

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

замість цього:

Employee e = new Employee();
e.setName("Jack Sparrow");
...and so on...
list.add(e);

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

Оновлення:

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


1
Оскільки я бачив цю модель дизайну деякий час тому, я роблю це там, де це можливо. Якщо методу не потрібно явно повертати щось, щоб виконати свою роботу, він тепер повертається this. Іноді я навіть змінюю функцію, щоб замість повернення значення вона працювала на члені об'єкта, просто так я можу це зробити. Це прекрасно. :)
Інверс

4
Для телескопічних сетерів, що повертаються самостійно, а також у будівельників я вважаю за краще використовувати nameName (ім'я рядка), а не setName (ім'я рядка). Як ви зазначали, загальна практика та очікування сетерів - це повернутись нікчемно. "Нестандартні" сетери можуть не добре поводитися з існуючими рамками, наприклад, менеджери організацій JPA, Весна тощо.
1616

Будь ласка, введіть розриви рядків перед кожним викликом :) І налаштуйте свій IDE або отримайте належний, якщо він цього не дотримується.
MauganRa

Широко використовувані рамки (наприклад, весна та сплячка) суворо (принаймні, раніше) дотримувались конвенції про встановлення недійсності
Legna

Дивіться також stackoverflow.com/questions/5741369/…
Піно

Відповіді:


83

Я не думаю, що в цьому щось конкретно не так, це лише питання стилю. Це корисно, коли:

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

Альтернативами цього методу можуть бути:

  1. Один мега конструктор (зворотний бік: ви можете пропустити багато нулів або значень за замовчуванням, і стає важко дізнатися, яке значення відповідає якому)
  2. Кілька перевантажених конструкторів (недолік: стає непростим, коли у вас є більше кількох)
  3. Фабричні / статичні методи (недолік: те саме, що і перевантажені конструктори - стає непростим, коли їх буде більше декількох)

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


1
ну, як правило, ви нічого не повертаєте з сетера за умовою.
Кен Лю

17
Можливо, не для початку, але сетер не обов'язково зберігає своє первісне призначення. Те, що раніше було змінною, може змінитися в стан, який включає декілька змінних або мати інші побічні ефекти. Деякі сетери можуть повернути попереднє значення, інші можуть повернути показник відмови, якщо збій є занадто поширеним для виключення. Це викликає ще один цікавий момент: що робити, якщо інструмент / фреймворк, який ви використовуєте, не розпізнає ваші налаштування, коли вони мають повернені значення?
Том Кліфт

12
@ Добре, що це порушує конвенцію "Java bean" для геттерів та сеттерів.
Енді Уайт

2
@TomClift Чи порушує конвенція "Java Bean" якісь проблеми? Чи є бібліотеки, які використовують умова "Java Bean", дивлячись на тип повернення або просто параметри методу та ім'я методу.
Тео Бріско

3
саме тому шаблон Builder існує, сеттер не повинен щось повертати, натомість створюйте
конструктор,

106

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

Зазвичай це називається модель побудови або вільний інтерфейс .

Він також поширений в Java API:

String s = new StringBuilder().append("testing ").append(1)
  .append(" 2 ").append(3).toString();

26
Його часто використовують у будівельниках, але я б не сказав, що "це ... називається шаблоном Builder".
Лоранс Гонсальвс

10
Мені смішно, що деяке обґрунтування вільних інтерфейсів полягає в тому, що вони полегшують читання коду. Я міг би бачити, що писати зручніше, але мені це ніби важко читати. Це єдина справжня незгода у мене з цим.
Код Брент пише

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

18
@ddimitrov aslong як ви обмежити його повернення цього він ніколи не буде проблемою (тільки перший invokation може кинути NPE)
Stefan

4
простіше писати ТА читати, припускаючи, що ви ставите рядкові рядки та відступи, де читабельність постраждає інакше! (тому що це дозволяє уникнути зайвого безладу повторюваного коду, як, наприклад, bla.foo.setBar1(...) ; bla.foo.setBar2(...)коли ви могли писати bla.foo /* newline indented */.setBar1(...) /* underneath previous setter */ .setBar2(...)(не вдається використовувати рядкові перерви в коментарі SO так :-( ... сподіваюся, ви отримаєте точку, враховуючи 10 таких сетерів або більш складні дзвінки)
Андреас Дітріх

90

Узагальнити:

  • це називається "вільний інтерфейс" або "ланцюжок методів".
  • це не "стандартна" Java, хоча ви більше бачите її в ці дні (чудово працює в jQuery)
  • він порушує специфікацію JavaBean, тому він буде розбиватися з різними інструментами та бібліотеками, особливо JSP-розробниками та Spring.
  • це може завадити деяким оптимізаціям, які зазвичай проводив би JVM
  • деякі думають, що це очищує код, інші вважають, що це "примарно"

Ще кілька моментів, які не згадуються:

  • Це порушує головне, що кожна функція повинна робити одну (і лише одну) річ. Ви можете чи не вірите в це, але в Java я вважаю, що це працює добре.

  • IDE не збираються створювати їх для вас (за замовчуванням).

  • Нарешті, ось точка даних у реальному світі. У мене виникли проблеми з використанням такої бібліотеки. Приклад цього в побутовій бібліотеці - це конструктор запитів Hibernate. Оскільки методи набору Query * повертають запити, неможливо сказати лише підписом, як ним користуватися. Наприклад:

    Query setWhatever(String what);
  • Це вносить неоднозначність: чи метод модифікує поточний об'єкт (ваш шаблон) чи, можливо, Запит дійсно незмінний (дуже популярний і цінний шаблон), і метод повертає новий. Це просто ускладнює використання бібліотеки, і багато програмістів не використовують цю функцію. Якби сеттери були сеттерами, було б зрозуміліше, як ним користуватися.


5
btw, це "вільно", а не "текуче" ... так як в ньому ви можете структурувати ряд викликів методів, як розмовна мова.
Кен Лю

1
Останній пункт про незмінність дуже важливий. Найпростіший приклад - String. Java-розробники очікують, що при використанні методів на String вони отримують абсолютно новий екземпляр і не той самий, але модифікований екземпляр. З вільним інтерфейсом потрібно було б зазначити в документації методу, що повернутий об'єкт "це" замість нового примірника.
MeTTeO

7
Хоча я погоджуюся в цілому, я не згоден, що це порушує принцип "робити лише одне". Повернення thisнавряд чи складна ракетна наука. :-)
user949300

додатковий пункт: він також порушує принцип розділення команд і запитів.
Marton_hun

84

Для цього я вважаю за краще використовувати методи "з":

public String getFoo() { return foo; }
public void setFoo(String foo) { this.foo = foo; }
public Employee withFoo(String foo) {
  setFoo(foo);
  return this;
}

Таким чином:

list.add(new Employee().withName("Jack Sparrow")
                       .withId(1)
                       .withFoo("bacon!"));

Попередження : цей withXсинтаксис зазвичай використовується для надання "сетерів" для незмінних об'єктів, тому абоненти цих методів можуть з розумом очікувати, що вони створять нові об'єкти, а не мутувати існуючий екземпляр. Можливо, більш розумне формулювання буде чимось на зразок:

list.add(new Employee().chainsetName("Jack Sparrow")
                       .chainsetId(1)
                       .chainsetFoo("bacon!"));

З ланцюжкомXyz () умовами іменування практично всі повинні бути задоволені.


18
+1 За цікаву умову. Я не збираюсь його приймати у власному коді, оскільки, здається, зараз вам доведеться мати get, а, setі withдля кожного поля класу. Це все ж цікаве рішення. :)
Пол Манта

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

2
І якщо ви додали це Project Lombok«s @Getter/@Setterанотацій ... що б фантастичним для побудови ланцюжка. Або ви можете використовувати щось багато на зразок Kestrelкомбінатора ( github.com/raganwald/Katy ), який використовують JQuery та Javascript-захисники.
Ехтеш Чудхурі

4
@ AlikElzin-kilaka Насправді я щойно помітив, що незмінні класи java.time в Java 8 використовують цю схему, наприклад, LocalDate.withMonth, withYear тощо.
Кваліфікаційний

4
withпрефікс інший конвенції. як @qualidafial наводив приклад. методи з префіксом withне повинні повертатись, thisа новий екземпляр, як поточний екземпляр, але withце зміна. Це робиться для того, коли ви хочете, щоб ваші об'єкти були незмінні. Тож коли я бачу метод із префіксом, withя припускаю, що отримаю новий об'єкт, не той самий.
tempcke

26

Якщо ви не хочете повертатися 'this'із сетера, але не хочете використовувати другий варіант, ви можете використовувати наступний синтаксис для встановлення властивостей:

list.add(new Employee()
{{
    setName("Jack Sparrow");
    setId(1);
    setFoo("bacon!");
}});

Як осторонь, я думаю, що його трохи чистіше в C #:

list.Add(new Employee() {
    Name = "Jack Sparrow",
    Id = 1,
    Foo = "bacon!"
});

16
подвійна дужка ініціалізації може мати проблеми з рівними, оскільки це створює анонімний внутрішній клас; дивіться c2.com/cgi/wiki?DoubleBraceInitialization
Csaba_H

@Csaba_H Зрозуміло, що в цій проблемі винна людина, яка перешкоджає equalsметоду. Є дуже чисті способи боротьби з анонімними заняттями, equalsякщо ви знаєте, що робите.
AJMansfield

створення нового (анонімного) класу саме для цього? для кожного випадку?
користувач85421

11

Він не тільки порушує конвенцію геттерів / сеттерів, але також порушує довідкові рамки методу Java 8. MyClass::setMyValueє BiConsumer<MyClass,MyValue>, і myInstance::setMyValueє a Consumer<MyValue>. Якщо у вас є повернення setter this, то це вже не дійсний екземпляр Consumer<MyValue>, а, скоріше Function<MyValue,MyClass>, і призведе до порушення будь-якого використання посилань методу на ці налаштування (якщо вважати, що вони є недійсними).


2
Було б дивовижно, якби Java мала певний спосіб перевантажити тип повернення, а не лише JVM. Ви можете легко обійти багато цих порушень.
Adowrath

1
Ви завжди можете визначити функціональний інтерфейс, який розширюється як Consumer<A>і Function<A,B>, забезпечуючи реалізацію за замовчуванням void accept(A a) { apply(a); }. Тоді він легко може бути використаний як один, так і не порушує жодного коду, який вимагає певної форми.
Стів К

Арг! Це просто неправильно! Це називається несумісністю. Замовник, який повертає значення, може виступати споживачем. ideone.com/ZIDy2M
Майкл

8

Я не знаю Java, але я це робив на C ++. Інші люди сказали, що це робить рядки дійсно довгими і дуже важко читати, але я робив це так багато разів:

list.add(new Employee()
    .setName("Jack Sparrow")
    .setId(1)
    .setFoo("bacon!"));

Це ще краще:

list.add(
    new Employee("Jack Sparrow")
    .Id(1)
    .foo("bacon!"));

принаймні, я думаю. Але ти можеш прихильнити мене і зателефонувати мені до жахливого програміста. І я не знаю, чи вам це дозволяють навіть робити на Java.


"Ще краще" не позичає функціональному коду формату вихідного коду, доступному в сучасних IDE. На жаль,
Thorbjørn Ravn Andersen

Ви, мабуть, маєте рацію ... Єдиний автоформатор, який я використав, - це автоматичне відступ від emacs.
Карсон Майєрс

2
Форматори вихідного коду можуть бути примушені до простого // після кожного виклику методу у ланцюжку. Це трохи зменшує ваш код, але не так сильно переробляє вертикальну серію заяв в горизонтальному напрямку.
кваліфікаційний

@qualidafial Вам не потрібно //після кожного методу, якщо ви налаштовуєте свій IDE не приєднуватися до вже обернутих рядків (наприклад, Eclipse> Properties> Java> Style Style> Formatter> Wrapping Line> Ніколи не приєднуйтесь до вже завернутих рядків).
DJDaveMark

6

Ця схема (призначена для каламбура), яка називається «вільний інтерфейс», стає на сьогодні досить популярною. Це прийнятно, але це насправді не моя чашка чаю.


6

Оскільки він не повертає недійсності, він більше не є дійсним параметром властивостей JavaBean. Це може мати значення, якщо ви один із семи людей у ​​світі, який використовує візуальні інструменти "Bean Builder", або один із 17, що використовують елементи JSP-bean-setProperty.


Це також важливо, якщо ви використовуєте рамки, обізнані з бобами, як Spring
ddimitrov

6

Принаймні теоретично це може пошкодити механізми оптимізації JVM, встановивши помилкові залежності між викликами.

Він повинен бути синтаксичним цукром, але насправді може створювати побічні ефекти у суперінтелектуальній віртуальній машині Java 43.

Тому я голосую «ні», не використовуйте.


10
Цікаво ... Ви могли б трохи розширити це?
Кен Лю

3
Подумайте лише, як суперскалярні процесори мають справу з паралельним виконанням. Об'єкт для виконання другого setметоду залежить від першого setметоду, хоча він відомий програмістом.
Мар’ян

2
Я досі не слідую. Якщо ви встановите Foo, а потім Bar з двома окремими твердженнями, об'єкт, для якого ви встановлюєте Bar, має інший стан, ніж об’єкт, для якого ви встановлюєте Foo. Тож компілятор також не міг паралелізувати ці твердження. Принаймні, я не бачу, як це могло б не ввести необґрунтоване припущення. (Оскільки я про це не маю поняття, я не заперечую, що Java 43 насправді робить паралелізацію у випадку одного разу, але не в іншому і вводить необґрунтоване припущення в одному випадку, але не в іншому).
масон

12
Якщо ви не знаєте, тестуйте. -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining Java7 jdk, безумовно, вбудовує ланцюгові методи, і робить приблизно стільки ж ітерацій, які потрібні для позначення недійсних сетерів як гарячих та вбудованих їх також. Зрозуміло, ви недооцінюєте потужність алгоритмів обрізки коду JVM; якщо він знає, що ви повертаєте це, він пропустить код коду jrs (повернення заяви) і просто залишить це на стеці.
Аякс

1
Андреас, я згоден, але проблеми виникають, коли у вас шар на шар неефективного коду. 99% часу слід кодувати для ясності, про що тут багато говориться. Але бувають і випадки, коли вам потрібно бути реалістичними і використовувати свій багаторічний досвід, щоб передчасно оптимізувати в загальному архітектурному сенсі.
LegendLength

6

Це зовсім не погана практика. Але це не сумісно з JavaBeans Spec .

І специфікація залежить від стандартних аксесуарів.

Ви завжди можете змусити їх співіснувати один з одним.

public class Some {
    public String getValue() { // JavaBeans
        return value;
    }
    public void setValue(final String value) { // JavaBeans
        this.value = value;
    }
    public String value() { // simple
        return getValue();
    }
    public Some value(final String value) { // fluent/chaining
        setValue(value);
        return this;
    }
    private String value;
}

Тепер ми можемо використовувати їх разом.

new Some().value("some").getValue();

Ось інша версія для незмінного об'єкта.

public class Some {

    public static class Builder {

        public Some build() { return new Some(value); }

        public Builder value(final String value) {
            this.value = value;
            return this;
        }

        private String value;
    }

    private Some(final String value) {
        super();
        this.value = value;
    }

    public String getValue() { return value; }

    public String value() { return getValue();}

    private final String value;
}

Тепер ми можемо це зробити.

new Some.Builder().value("value").build().getValue();

2
Мою редакцію відхилено, але ваш приклад Builder невірний. По-перше, .value () нічого не повертає і навіть не встановлює someполе. По-друге, вам слід додати захисний захист і встановити someйого nullв build (), так що Someце справді незмінне, інакше ви можете знову зателефонувати builder.value()в той же екземпляр Builder. І нарешті, так, у вас є Someконструктор , але у вас все ще є публічний конструктор, тобто ви не відсторонено виступаєте за використання Builder, тобто користувач не знає про нього, крім спроб або пошуку способу встановити звичай valueзовсім.
Adowrath

@Adowrath, якщо відповідь невірна, слід написати власну відповідь, а не намагатися редагувати чужу форму
CalvT

1
@JinKwon Чудово. Дякую! І шкода, якщо раніше я здавався грубим.
Adowrath

1
@Adowrath Будь ласка, не соромтесь будь-які додаткові коментарі щодо удосконалень. FYI, я не той, хто відхилив твою редакцію. :)
Джин Квон

1
Я знаю, я знаю. ^^ І дякую за нову версію, яка тепер пропонує справжній програвач "Immutable-Some", що змінюється. І це більш розумне рішення, ніж мої спроби редагування, які, порівняно, захаращували код.
Adowrath

4

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

З іншого боку, якщо існуюча частина вашої програми використовує стандартну умову, я б дотримувався її і додав будівельників до складніших класів

public class NutritionalFacts {
    private final int sodium;
    private final int fat;
    private final int carbo;

    public int getSodium(){
        return sodium;
    }

    public int getfat(){
        return fat;
    }

    public int getCarbo(){
        return carbo;
    }

    public static class Builder {
        private int sodium;
        private int fat;
        private int carbo;

        public Builder sodium(int s) {
            this.sodium = s;
            return this;
        }

        public Builder fat(int f) {
            this.fat = f;
            return this;
        }

        public Builder carbo(int c) {
            this.carbo = c;
            return this;
        }

        public NutritionalFacts build() {
            return new NutritionalFacts(this);
        }
    }

    private NutritionalFacts(Builder b) {
        this.sodium = b.sodium;
        this.fat = b.fat;
        this.carbo = b.carbo;
    }
}

1
Саме це і було розроблено для виправлення шаблону Builder. Він не ламає лямбди в Java 8, він не порушує вибагливі інструменти JavaBeans і не викликає жодних проблем з оптимізацією в JVM (оскільки об'єкт існує лише під час інстанції). Він також вирішує проблему "занадто багато конструкторів", яку ви отримуєте, просто не використовуючи будівельників, а також усуваючи забруднення купи від анонімних класів з подвійною дужкою.
ndm13

Цікавий момент - я зауважую, що якщо майже нічого у вашому класі не змінюється, хоча (наприклад, налаштований графічний інтерфейс), то ви, ймовірно, взагалі можете уникнути Builder.
Філіп Гін

4

Paulo Abrantes пропонує ще один спосіб зробити налаштування JavaBean вільним: визначити внутрішній клас будівельника для кожного JavaBean. Якщо ви користуєтеся інструментами, які отримують змішаний сеттер, який повертає значення, шаблон Паулу може допомогти.



3

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


2

Раніше я віддав перевагу такому підходу, але вирішив проти.

Причини:

  • Читабельність. Це робить код більш читаним, щоб кожен setFoo () був окремим рядком. Зазвичай ви читаєте код багато, набагато більше разів, ніж один раз, коли ви його пишете.
  • Побічний ефект: setFoo () повинен встановлювати лише поле foo, нічого іншого. Повернення це додаткове "ЩО було це".

Я, як я бачив, модель Builder не використовує конвенцію setFoo (foo) .setBar (bar), але більше foo (foo) .bar (bar). Можливо, саме з цих причин.

Це, як завжди, справа смаку. Мені просто подобається підхід "найменших сюрпризів".


2
Я погоджуюсь на побічний ефект. Розробники, які повертають речі, порушують їх назву. Ви встановлюєте foo, але ви отримуєте об'єкт назад? Це новий об’єкт чи я змінив старий?
хрускіт

2

Ця особливість називається Метод ланцюга. Вікіпедія посилання , це має більше пояснень та приклад того, як це робиться в різних мовах програмування.

PS: Просто думав залишити його тут, оскільки шукав конкретне ім’я.


1

На перший погляд: "Примарно!".

На подальшу думку

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

насправді менш схильний до помилок, ніж

Employee anEmployee = new Employee();
anEmployee.setName("xxx");
...
list.add(anEmployee);

Так досить цікаво. Додавання ідеї до пакету інструментів ...


1
Ні, це все ще примарно. З точки зору технічного обслуговування, останній кращий, оскільки його легше читати. Крім того, автоматизовані перевірки коду, такі як CheckStyle, примушуватимуть рядків до 80 символів за замовчуванням - код все-таки обернеться, ускладнюючи питання читабельності / обслуговування. І нарешті - це Java; немає користі писати все в одному рядку, коли це все одно буде складено до байтового коду.
OMG Ponies

1
особисто я думаю, що колишнє легше читати, особливо якщо ти створюєш кілька об’єктів таким чином.
Кен Лю

@Ken: Код методу. Написати один примірник у вільному форматі; інший примірник в іншому. Тепер дайте ці два примірники пару людей і запитайте, який з них їм легше читати. Швидше читати, швидше кодувати.
OMG Ponies

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

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

1

Так, я думаю, що це гарна ідея.

Якщо я можу щось додати, як щодо цієї проблеми:

class People
{
    private String name;
    public People setName(String name)
    {
        this.name = name;
        return this;
    }
}

class Friend extends People
{
    private String nickName;
    public Friend setNickName(String nickName)
    {
        this.nickName = nickName;
        return this;
    }
}

Це спрацює:

new Friend().setNickName("Bart").setName("Barthelemy");

Це не буде прийнято Eclipse! :

new Friend().setName("Barthelemy").setNickName("Bart");

Це тому, що setName () повертає People, а не Друга, і немає PeoplesetNickName.

Як ми могли написати сетерів для повернення класу SELF замість імені класу?

Щось подібне було б добре (якби існувало ключове слово SELF). Чи існує це все-таки?

class People
{
    private String name;
    public SELF setName(String name)
    {
        this.name = name;
        return this;
    }
}

1
Окрім Eclipse, є кілька інших компіляторів Java, які не приймуть це :). В основному, ви працюєте з птахом системи типу Java (яка є статичною, а не динамічною, як деякі мови сценаріїв): вам доведеться передати друзям те, що виходить із setName (), перш ніж ви зможете встановити на нього NickName (). Тож, на жаль, для ієрархій успадкування це виключає велику перевагу читабельності та робить цей інакше потенційно корисний прийом не таким корисним.
Корнел Массон

5
Використовуйте дженерики. клас Chainable <Self розширюється Chavable> {public Self doSomething () {return (Self) this;}} Це технічно не безпечно (він буде класовим, якщо ви реалізуєте клас із типом Self, якому ви не можете бути), але це є правильною граматикою, і підкласи повернуть власний тип.
Аякс

Крім того: Цей "САМО", який використовує Батістіст, називається самотипом, який не існує в багатьох мовах (Скала - це справді єдиний, про який я зараз можу придумати), де як використання Ajax Generics є так званим "Цікаво повторюваний шаблон шаблону ", який намагається впоратися з недоліком типу самоврядування, але у нього є деякі недоліки: (з class C<S extends C<S>>, що безпечніше звичайного S extends C. a) Якщо A extends Bі B extends C<B>, і у вас є A, ви знаєте лише, що B повертається, якщо A не переосмислює кожен метод. b) Ви не можете позначати місцеве, несире C<C<C<C<C<...>>>>>.
Adowrath

1

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


2
Як щодо використання винятків для позначення стану помилки? Коди помилок можна легко проігнорувати, оскільки багато програмістів C болісно навчилися. Винятки можуть підмітати стек до точки, де з ними можна обробитись.
ddimitrov

Винятки переважно кращі, але коди помилок також корисні, коли ви не можете використовувати винятки.
Нарек

1
Привіт @Narek, можливо, ви могли б пояснити, у яких випадках краще використовувати коди помилок у сеттерах, а не винятки і чому?
ddimitrov

0

З заяви

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

я бачу дві речі

1) Безглузде твердження. 2) Відсутність читабельності.


0

Це може бути менш читабельним

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!")); 

або це

list.add(new Employee()
          .setName("Jack Sparrow")
          .setId(1)
          .setFoo("bacon!")); 

Це читабельніше, ніж:

Employee employee = new Employee();
employee.setName("Jack Sparrow")
employee.setId(1)
employee.setFoo("bacon!")); 
list.add(employee); 

8
Я думаю, що це досить легко читається, якщо ви не намагаєтесь поставити весь код в один рядок.
Кен Лю

0

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

Наприклад, я не перевірив це напевно, але я не здивуюсь, що Джексон не визнає їх сеттерами під час створення вам об’єктів Java з json / maps. Я сподіваюсь, що я помиляюся на цьому (я протестую це незабаром).

Насправді я розробляю полегшений ORM, орієнтований на SQL, і мені потрібно додати деякий код beyong getPropertyDescriptors до визнаних сетерів, які це повертають.


0

Давно відповідь, але два мої центи ... Це добре. Хочеться, щоб цей вільний інтерфейс використовувався частіше.

Повторення змінної "заводу" не додає більше інформації нижче:

ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Foo.class);
factory.setFilter(new MethodFilter() { ...

Це чистіше, імхо:

ProxyFactory factory = new ProxyFactory()
.setSuperclass(Properties.class);
.setFilter(new MethodFilter() { ...

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


0

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

Цей підхід дозволяє вашому клієнтському коду бути:

  • Байдужий до типу повернення
  • Простіше в обслуговуванні
  • Уникайте побічних ефектів компілятора

Ось кілька прикладів.

val employee = Employee().apply {
   name = "Jack Sparrow"
   id = 1
   foo = "bacon"
}


val employee = Employee()
with(employee) {
   name = "Jack Sparrow"
   id = 1
   foo = "bacon"
}


val employee = Employee()
employee.let {
   it.name = "Jack Sparrow"
   it.id = 1
   it.foo = "bacon"
}

0

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

Однак, це дійсно питання уподобань, і прив’язка сетерів дійсно виглядає досить круто, на мою думку.


0

Я погоджуюся з усіма плакатами, які стверджують, що це порушує специфікацію JavaBeans. Є причини, щоб зберегти це, але я також вважаю, що використання цього шаблону для будівельників (на що було сказано) має своє місце; до тих пір, поки він не використовується скрізь, він повинен бути прийнятним. Для мене, це місце, де кінцевою точкою є заклик до методу "build ()".

Звичайно, існують і інші способи налаштування всіх цих речей, але перевага тут полягає в тому, що це дозволяє уникнути 1) багатопараметричних публічних конструкторів та 2) частково заданих об'єктів. Тут ви маєте будівельника зібрати необхідне, а потім в кінці викликати його "build ()", який може переконатися, що частково заданий об'єкт не побудований, оскільки ця операція може отримати меншу публічну видимість. Альтернативою були б "об'єкти параметрів", але IMHO просто відсуває проблему на один рівень.

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

Коротше кажучи, я вважаю, що це правильна практика, якщо її правильно використовувати.


-4

Погана звичка: сетер встановив отримувач отримати

як щодо явного оголошення способу, який робить це для U

setPropertyFromParams(array $hashParamList) { ... }

не підходить для автоматичного доопрацювання та реконструкції та читабельності кодового котла
Андреас Дітріх

Тому що: 1) Ви повинні запам’ятати замовлення або прочитати його з документів, 2) ви втратите всю безпеку типу компіляції, 3) ви повинні передавати значення (це і 2), звичайно, не є проблемою в динаміці мова, звичайно), 4) ви не можете розширити це корисно, окрім перевірки довжини масиву на кожному виклику, і 5) якщо ви щось змінили, будь то порядок або навіть наявність певних значень, ви не можете перевірити, що ні час виконання ні компілювати час без сумнівів взагалі . З сеттерами: 1), 2), 3): Не проблема. 4) Додавання нових методів нічого не порушує. 5) явне повідомлення про помилку.
Adowrath
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.