Використовувати конструктор чи метод сеттера?


16

Я працюю над кодом інтерфейсу, де я маю Actionклас, щось подібне -

public class MyAction extends Action {
    public MyAction() {
        setText("My Action Text");
        setToolTip("My Action Tool tip");
        setImage("Some Image");
    }
}

Коли цей клас Action був створений, ми вважали, що Actionклас не може бути налаштований (у певному сенсі - його текст, підказку чи зображення не будуть змінені ніде в коді). Тепер нам потрібно змінити текст дії в якомусь місці в коді. Отже, я запропонував своєму співробітникові вилучити з конструктора текст з твердим кодом і прийняти його як аргумент, так що всі змушені передавати текст дії. Щось подібне до цього коду нижче -

public class MyAction extends Action {
    public MyAction(String actionText) {
        setText(actionText);
        setTooltip("My Action tool tip"); 
        setImage("My Image"); 
    }
}

Однак він вважає, що оскільки setText()метод належить до базового класу, його можна гнучко використовувати для передачі тексту дії, де б створено екземпляр дії. Таким чином, не потрібно змінювати існуючий MyActionклас. Тож його код виглядав би приблизно так.

MyAction action = new MyAction(); //this creates action instance with the hardcoded text
action.setText("User required new action text"); //overwrite the existing text.

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


1
Мова, якою ви користуєтесь, не дозволяє перевантажувати конструктори?
мат

1
Я використовую Java. Так, це дозволяє, і я думаю, що це може бути одним із способів впоратися з цим
zswap

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

Я б сказав, що якщо його потрібно встановити, щоб ваш об’єкт був дійсним, введіть його в кожен конструктор ... якщо це необов'язково (має розумний дефолт) і вам не байдуже незмінність, покладіть його в сеттер. Потрібно неможливо інстанціювати ваш об’єкт у недійсному стані або після його встановлення в інвалідний стан, де це можливо.
Білл К

Відповіді:


15

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

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

Я бачу два чіткі шляхи вперед:

  1. Використовуйте параметр конструктора, як ви пропонуєте. Якщо ви дійсно хочете, ви можете передати nullабо порожній рядок, але тоді факт, що ви не призначаєте текст, явний, а не неявний. Легко побачити існування nullпараметра і побачити, що, мабуть, була якась думка, вкладена в нього, але не так просто побачити відсутність виклику методу і визначити, чи був такий недолік навмисним чи ні. У такому простому випадку, мабуть, це я був би підходом.
  2. Використовуйте заводський / будівельний візерунок. Це може бути надмірним для такого простого сценарію, але в більш загальному випадку він є дуже гнучким, оскільки дозволяє встановити будь-яку кількість параметрів і перевірити передумови до або під час інстанцізації об'єкта (якщо побудова об'єкта - це велика операція та / або клас можна використовувати більш ніж одним способом, це може бути величезною перевагою). Зокрема, на Java це також загальна ідіома, і дотримуватися встановлених зразків у мові та рамках, якими ви користуєтесь, дуже рідко є поганою справою.

10

Тут перевантаження конструктора було б простим і простим рішенням:

public class MyAction extends Action {
    public MyAction(String actionText) {
        setText(actionText);
        setTooltip("My Action tool tip"); 
        setImage("My Image"); 
    }
    public MyAction() {
        this("My Action Text");
    }
}

Це краще, ніж дзвонити .setTextпізніше, тому що таким чином нічого не потрібно перезаписувати, це actionTextможе бути задумане з самого початку.

У міру того, як ваш код розвиватиметься, і вам знадобиться ще більша гнучкість (що, безумовно, станеться), ви отримаєте користь від фабричного / будівельного шаблону, запропонованого іншою відповіддю.


Що відбувається, коли вони хочуть налаштувати другу властивість?
кевін клайн

3
Для 2-го, 3-го, .. властивості ви можете застосувати ту саму техніку, але чим більше властивостей ви хочете налаштувати, тим більш громіздкою це стане. У якийсь момент буде більше сенсу реалізувати заводський / будівельний зразок, як це добре заявив @ michael-kjorling у своїй відповіді.
janos

6

Додайте вільний метод "setText":

public class MyAction ... {
  ...
  public MyAction setText(String text) { ... ; return this; }
}

MyAction a = new MyAction().setText("xxx");

Що може бути зрозумілішим за це? Якщо ви вирішили додати інше настроювану властивість, немає проблем.


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

Мені подобаються вільні інтерфейси, особливо для будівельників, які виробляють незмінні предмети! Чим більше параметрів, тим краще це працює. Але, дивлячись на конкретний приклад у цьому питанні, я здогадуюсь, що setText()це визначено у класі Action, від якого успадковується MyAction. Ймовірно, він вже має недійсний тип повернення.
GlenPeterson

1

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

Це зробить ваш код більш читабельним, і, з моєї точки зору, легше і, на жаль , "сексуально" писати.

У вашому випадку це піде так (вибачте за будь-яку друкарську помилку, минув рік, як я написав останню програму java):

 public class MyAction extends Action {
    private String _text     = "";
    private String _tooltip  = "";
    private String _imageUrl = "";

    public MyAction()
    {
       // nothing to do here.
    }

    public MyAction text(string value)
    {
       this._text = value;
       return this;
    }

    public MyAction tooltip(string value)
    {
       this._tooltip = value;
       return this;
    }

    public MyAction image(string value)
    {
       this._imageUrl = value;
       return this;
    }
}

І використання було б таким:

MyAction action = new MyAction()
    .text("My Action Text")
    .tooltip("My Action Tool tip")
    .image("Some Image");

Погана ідея, що робити, якщо вони забудуть встановити текст або будь-яку важливу річ.
Прахар

1

Поради щодо використання конструкторів або будівельників загалом є чудовими, але, на мій досвід, не вистачає деяких ключових моментів для дій, які

  1. Можливо, потрібно інтернаціоналізувати
  2. Ймовірно, що маркетинг зміниться в останню хвилину.

Я настійно пропоную прочитати ім’я, підказку, піктограму тощо ... з файлу властивостей, XML тощо. Наприклад, для дії File-Open ви можете перейти у "Властивості".

File.open.name=Open
File.open.tooltip=Open a file
File.open.icon=somedir/open.jpg

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

Це лише приблизний контур, читачеві залишається багато… Шукайте інші приклади інтернаціоналізації.


0

Марно називати setText (actionText) або setTooltip ("Моя порада інструменту дії") всередині конструктора; простіше (і ви отримаєте більшу продуктивність), якщо просто ініціалізувати відповідне поле безпосередньо:

    public MyAction(String actionText) {
        this.actionText = actionText;
    }

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

Оскільки підказка та зображення є константами, розглядайте їх як константи; мати поля:

private (or even public) final static String TOOLTIP = "My Action Tooltip";

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


4
Будь-який наполовину компетентний компілятор та JITter повинен вбудовувати виклики setText () тощо, тому різниця в продуктивності між викликом функції для виконання завдання, і тим, що це призначення замість виклику функції, має бути максимум незначним та більш ймовірним ніж не нуль.
CVn

0

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


Я думаю, що перевантажений конструктор все-таки повинен вирішити проблему. Оскільки у всіх випадках "10%" ви спершу створите дію з текстом за замовчуванням, а потім зміните текст дії за допомогою методу "setText ()". Чому б не встановити відповідний текст під час побудови дії.
zswap

0

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

Наприклад, якщо передбачається, що клас MyAction буде незмінним після побудови (і, можливо, іншої ініціалізації), він не повинен мати метод сеттера. Якщо більшу частину часу він буде використовувати за замовчуванням "Мій текст дії", має бути конструктор без параметрів, плюс конструктор, який дозволяє необов'язковий текст. Тепер користувачеві не потрібно думати, щоб правильно використовувати клас 90% часу. Якщо користувачеві зазвичай слід подумати над текстом, пропустіть конструктор без параметрів. Тепер користувач змушений думати, коли це необхідно, і не може не помітити необхідний крок.

Якщо MyActionекземпляр потрібно змінити після повного створення, тоді вам потрібен сетер для тексту. Заманливо пропустити встановлення значення в конструкторі (принцип DRY - "Не повторюй себе"), і якщо значення за замовчуванням зазвичай досить добре, я б. Але якщо ні, то необхідність тексту в конструкторі змушує користувача задуматися, коли слід.

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


0

У наступному запропонованому рішенні суперклас є абстрактним і має всі три члени встановлені за замовчуванням.

Підклас має різні конструктори, щоб програміст міг його інстанціювати.

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

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

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

І так далі.

public abstract class Action {
    protected String text = "Default action text";
    protected String toolTip = "Default action tool tip";
    protected String imageURl = "http://myserver.com/images/default.png";

    .... rest of code, I guess setters and getters
}

public class MyAction extends Action {


    public MyAction() {

    }

    public MyAction(String actionText) {
        setText(actionText);
    }

    public MyAction(String actionText, String toolTip_) {
        setText(actionText);
        setToolTip(toolTip_);   
    }

    public MyAction(String actionText, String toolTip_; String imageURL_) {
        setText(actionText);
        setToolTip(toolTip_);
        setImageURL(imageURL_);
    }


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