Чи неправильно використовувати булевий параметр для визначення значень?


39

Згідно Чи неправильно використовувати булевий параметр для визначення поведінки? , Я знаю важливість уникати використання булевих параметрів для визначення поведінки, наприклад:

оригінальна версія

public void setState(boolean flag){
    if(flag){
        a();
    }else{
        b();
    }
    c();
}

Нова версія:

public void setStateTrue(){
    a();
    c();
}

public void setStateFalse(){
    b();
    c();
}

Але як щодо випадку, коли булевий параметр використовується для визначення значень замість поведінки? наприклад:

public void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

Я намагаюся усунути прапор isHintOn і створити дві окремі функції:

public void setHintOn(){
    this.layer1.visible=true;
    this.layer2.visible=false;
    this.layer3.visible=true;
}

public void setHintOff(){
    this.layer1.visible=false;
    this.layer2.visible=true;
    this.layer3.visible=false;
}

але модифікована версія здається менш рентабельною, оскільки:

  1. він має більше кодів, ніж оригінальна версія

  2. це не може чітко показати, що видимість шару2 протилежна варіанту підказки

  3. коли додається новий шар (наприклад: layer4), мені потрібно додати

    this.layer4.visible=false;
    

    і

    this.layer4.visible=true;  
    

    у setHintOn () та setHintOff () окремо

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


26
Ніколи не помиляється, якщо отриманий код є більш читабельним і доступним для обслуговування ;-) Я рекомендував би використовувати один метод замість двох окремих методів.
helb

32
Ви представляєте переконливий аргумент, що одна реалізація методу, який встановлює ці булеви, призведе до більш легкого обслуговування класу та розуміння його реалізації. Дуже добре; це законні міркування. Але публічний інтерфейс класу не повинен бути деформованим для їх розміщення. Якщо окремі методи полегшать розуміння та роботу із загальнодоступним інтерфейсом, визначте свій setHint(boolean isHintOn)як приватний метод та додайте public setHintOnта setHintOffметоди, які відповідно викликають setHint(true)та setHint(false).
Марк Амері

9
Я був би дуже незадоволений назвами методів: вони насправді не пропонують жодної вигоди setHint(true|false). Картопляний калій. Принаймні використовувати щось на кшталт setHintі unsetHint.
Конрад Рудольф


4
@kevincline Якщо умова - одне ім'я, ви пишете isна початку. isValidі т. д. То навіщо змінювати це на два слова? Крім того, "більш природне" є в очах глядача. Якщо ви хочете вимовити це як англійське речення, тоді для мене було б більш природно мати "якщо натяк на" з "за", що тут ".
Містер Лістер

Відповіді:


95

Дизайн API повинен орієнтуватися на те, що найбільш корисно для клієнта API, з боку виклику .

Наприклад, якщо цей новий API вимагає, щоб абонент регулярно писав такий код

if(flag)
    foo.setStateTrue();
else
    foo.setStateFalse();

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

 foo.setState(flag);

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

Сторона реалізації , однак, не повинна диктувати, як виглядає публічний API. Якщо така функція, як setHintпараметр, потребує меншого коду в реалізації, але API в термінах setHintOn/ setHintOffвиглядає простіше у використанні для клієнта, можна реалізувати його таким чином:

private void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

public void setHintOn(){
   setHint(true);
}

public void setHintOff(){
   setHint(false);
}

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

Це працює і навпаки: якщо setStateметод зверху потребує перемикання між двома різними фрагментами коду, ці фрагменти коду можуть бути відновлені до двох різних приватних методів. Отже, IMHO не має сенсу шукати критерій для вирішення між "одним параметром / одним методом" та "нульовими параметрами / двома методами", дивлячись на внутрішні органи. Однак подивіться, як ви хотіли б бачити API в ролі його споживача.

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


DocBrown, ви б сказали, що відповідна лінія розбиття полягає в тому, чи встановлення кожної держави має складні та, можливо, незворотні побічні ефекти? Наприклад, якщо ви просто перемикаєте прапор, який виконує те, що написано на тині, і немає машини, що лежить в основі, ви параметризуєте різні стани. Якщо, наприклад, ви б не параметризували такий метод SetLoanFacility(bool enabled), оскільки, надавши позику, може бути непросто взяти її знову, і два варіанти можуть включати зовсім іншу логіку - і ви хочете перейти до окремої створення / Видалити методи.
Стів

15
@Steve: ви все ще намагаєтесь розробити API відповідно до вимог, які ви бачите на стороні реалізації. Якщо бути прямо: це абсолютно не має значення. Використовувати будь-який варіант публічного API простіше у використанні з боку виклику. Всередині ви завжди можете дозволити двом загальнодоступним методам викликати один приватний з параметром. Або навпаки, ви можете дозволити один метод з перемиканням параметрів між двома приватними методами з різною логікою.
Док Браун

@Steve: дивіться мою редагування.
Док Браун

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

1
@Steve Отже, якщо користувачеві потрібно підтвердити певний параметр, Toggle()це не є правильною функцією, яку потрібно надати. Ось і вся суть; якщо абонент дбає лише про те, щоб "змінити", а не "чим він закінчується", Toggle()це варіант, який дозволяє уникнути додаткової перевірки та прийняття рішень. Я б не називав це загальним випадком, і не рекомендував би зробити його доступним без поважних причин, але якщо користувачеві потрібен перемикач, то я б переключив їх.
Каміль Дракарі

40

Мартін Фаулер цитує Кента Бека, рекомендуючи окремі setOn() setOff()методи , але також каже, що це не слід вважати недоторканним:

Якщо ви будете витягувати [sic] дані з булевого джерела, такого як елемент управління інтерфейсом або джерело даних, я вважаю за краще, setSwitch(aValue)ніж

if (aValue)
  setOn();
else
  setOff();

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

Інша рекомендація полягає в тому, щоб використовувати перераховані значення або прапори типу , щоб дати trueі falseкраще, імена конкретних умов. У вашому прикладі, showHintі hideHintможе бути краще.


16
На мій досвід, setSwitch (значення) майже завжди призводить до менш загального коду, ніж setOn / setOff саме через код if / else, процитований у вашій відповіді. Я схильний проклинати розробників, які надають мені API setOn / setOff, а не setSwitch (значення).
17 з 26

1
Дивлячись на це з подібного об'єктива: Якщо вам потрібно ввести жорсткий код, яке значення буде, це легко і з одним. Однак якщо вам потрібно встановити його, скажімо, на введення користувача, якщо ви можете просто передати значення безпосередньо в, це зберігає крок.
Нік Хартлі

@ 17of26 як конкретний зразок зустрічного прикладу (щоб показати, що "це залежить", а не те, що один є кращим для іншого), в AppKit Apple є -[NSView setNeedsDisplay:]метод, коли ви переходите, YESякщо погляд повинен перемальовуватися, а NOякщо він не повинен. Вам майже ніколи не потрібно говорити про це, тому UIKit просто не має -[UIView setNeedsDisplay]жодного параметра. У ньому немає відповідного -setDoesNotNeedDisplayметоду.
Грем Лі

2
@GrahamLee, я думаю, що аргумент Фоулера є досить тонким і справді спирається на судження. Я б не використовував bool isPremiumпрапор у його прикладі, але я б використав enum ( BookingType bookingType), щоб параметризувати той самий метод, якщо тільки логіка кожного бронювання не була зовсім іншою. "Заплутана логіка", на яку посилається Фоулер, часто бажана, якщо хочеться бачити, яка різниця між двома режимами. І якщо вони кардинально відрізняються, я би відкрив параметризований метод зовнішньо, а окремі методи реалізував би всередині.
Стів

3

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

Почнемо з API, як:

public void setHint(boolean isHintOn)

і:

public void setHintOn()
public void setHintOff()

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

setHint(true)
// Do your process
…
setHint(false)

тоді як другий варіант дає вам це:

setHintOn()
// Do your process
…
setHintOff()

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

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

Потім ви повинні вибрати, як реалізувати свій загальнодоступний API. Скажімо, ми вибрали методи setHintOnі setHintOffяк наш публічний API, і вони поділяють цю загальну логіку, як у вашому прикладі. Ви можете легко абстрагувати цю логіку приватним методом (код скопійований з Doc):

private void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

public void setHintOn(){
   setHint(true);
}

public void setHintOff(){
   setHint(false);
}

І навпаки, скажімо, ми вибрали setHint(boolean isHintOn)наш API, але давайте повернемо ваш приклад, з будь-якої причини встановлення підказки On повністю відрізняється від встановлення його на Off. У цьому випадку ми могли б реалізувати це так:

public void setHint(boolean isHintOn){
    if(isHintOn){
        // Set it On
    } else {
        // Set it Off
    }    
}

Або навіть:

public void setHint(boolean isHintOn){    
    if(isHintOn){
        setHintOn()
    } else {
        setHintOff()
   }    
}

private void setHintOn(){
   // Set it On
}

private void setHintOff(){
   // Set it Off 
}

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

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


3
Як зауваження, якщо це щось на кшталт псевдоблоку (один вислів на початку, один у кінці), ви, ймовірно, повинні використовувати beginта endабо синоніми, просто щоб чітко зрозуміти, що вони роблять, і щоб мати на увазі, що початок повинен мати закінчення і навпаки.
Нік Хартлі

Я погоджуюся з Nic, і я додав би: Якщо ви хочете переконатися, що початок і кінець завжди поєднані, ви також повинні надати для цього мовні ідіоми: RAII / охоронці області в C ++, usingблоки в C #, withменеджери контексту операторів у Python, передаючи тіло як лямбда-або дзвонячого предмета (наприклад, синтаксис блоку Ruby) тощо.
Даніель Приден

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

2

По-перше, спочатку: код не є автоматично менш ретельним, лише тому, що він трохи довший. Чіткість - це важливо.

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

bool isBackgroundVisible() {
    return isHintVisible;
}    

bool isContentVisible() {
    return !isHintVisible;
}

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

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

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


Отже, tl; dr: Ви рекомендуєте розділити на дві функції, тому що вам не доведеться їх змінювати, додаючи шари? Якщо доданий шар 4, нам може знадобитися сказати "this.layer4.visibility = isHintOn", тож я не впевнений, що згоден. Якщо що-небудь, це недооцінка проти нього, тому що тепер, коли додається шар, ми повинні редагувати дві функції, не одну.
Ердрік Айронроуз

Ні, я (слабо) рекомендую це для ясності ( showHintпроти setHintVisibility). Я згадав про новий шар тільки тому, що ОП хвилювався з цього приводу. Крім того , нам потрібно просто додати ще один новий метод: isLayer4Visible. showHintі hideHintпросто встановіть isHintVisibleатрибут true / false, і це не зміниться.
подвійно Ти

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

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

@Steve Подумайте «код гольфу» - якщо взяти такий код і переписати його в більше рядків , зазвичай це стає зрозумілішим. „Код гольфу” - це екстремал, але все ще є багато програмістів, які вважають, що затиснути все в один розумний вираз є „елегантним” і, можливо, навіть швидшим, оскільки компілятори не оптимізують достатньо добре.
BlackJack

1

Булеві параметри є чудовими у другому прикладі. Як ви вже зрозуміли, булеві параметри самі по собі не є проблематичними. Це поведінка комутації на основі прапора, що проблематично.

Перший приклад є проблематичним, оскільки ім'я вказує на метод встановлення, але реалізація, здається, є дещо іншою. Таким чином, у вас є поведінка, що перемикає антипатерн, і вводять в оману метод. Але якщо метод насправді є звичайним сеттером (без переключення поведінки), з цим проблем немає setState(boolean). Маючи два методи, setStateTrue()і setStateFalse()просто зайве ускладнення речей без користі.


1

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

Наприклад, на Java ви можете:

public enum HintState {
    SHOW_HINT(true, false, true),
    HIDE_HINT(false, true, false);

    private HintState(boolean layer1Visible, boolean layer2Visible, boolean layer3Visible) {
         // constructor body and accessors omitted for clarity
    }
}

І тоді ваш код абонента виглядатиме так:

setHint(HintState.SHOW_HINT);

І ваш код реалізації виглядатиме так:

public void setHint(HintState hint) {
    this.layer1Visible = hint.isLayer1Visible();
    this.layer2Visible = hint.isLayer2Visible();
    this.layer3Visible = hint.isLayer3Visible();
}

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


0

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

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

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

Ось приклад сліду стека:

function visible() : line 440
function parent() : line 398
function mode() : line 384
function run() : line 5

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

Тепер малюнок працює з слідом стека для функції, яка має поведінку A або B на основі булевого значення.

function bar() : line 92
function setVisible() : line 120
function foo() : line 492
function setVisible() : line 120
function run() : line 5

Це заплутано, якщо ви запитаєте мене. Одні й ті ж setVisibleлінії дають два різних сліди.

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

Ось кілька порад:

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

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


1
Що мене бентежить щодо setVisibleтрасування стека - це те, що, як setVisible(true)видається, дзвінок призводить до виклику setVisible(false)(або навпаки, залежно від того, як ви отримали цей слід).
Девід К

0

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

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

У цьому прикладі використовується Nodeклас із JavaFX:

public enum Visiblity
{
    SHOW, HIDE

    public boolean toggleVisibility(@Nonnull final Node node) {
        node.setVisible(!node.isVisible());
    }
}

завжди краще, ніж, як це зустрічається на багатьох JavaFXоб'єктах:

public void setVisiblity(final boolean flag);

але я думаю, .setVisible()і .setHidden()це найкраще рішення для ситуації, коли прапор є a, booleanоскільки він є найбільш явним і найменш багатослівним.

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

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


0

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

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

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

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


Що робити, якщо ви проектуєте інтегровану систему, і ви , зрештою, і виробник коду, і "споживач клієнта" коду? Як людина, що стоїть у взутті споживчого клієнта, формулює свою перевагу одного підходу над іншим?
Стів

@Steve, Як споживач, який ви споживає, ви знаєте, чи передаєте ви постійну чи змінну чи ні. Якщо проходять константи, віддайте перевагу перевантаженням без параметра.
Ерік Ейдт

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

@Steve, якщо ми знаємо, що під час проектування / часу компіляції клієнти будуть використовувати постійне значення (true / false) у всіх випадках, то це говорить про те, що дійсно існує два різних конкретних методу, а не один узагальнений метод (який бере параметр). Я б заперечував проти введення загальності параметризованого методу, коли він не використовується - це аргумент YAGNI.
Ерік Ейдт

0

Для проектування є два аспекти:

  • API: який інтерфейс ви представляєте користувачеві,
  • Реалізація: чіткість, технічне обслуговування тощо ...

Їх не слід плутати.

Це ідеально:

  • мати кілька методів у делегатові API для однієї реалізації,
  • мають єдиний метод в розсилці API для декількох реалізацій залежно від умови.

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


З боку API

Як програміст я, як правило, віддаю перевагу програмованим API. Код набагато зрозуміліше, коли я можу переслати значення, ніж коли мені потрібно if/ switchтвердження про значення, щоб вирішити, яку функцію викликати.

Останнє може знадобитися, якщо кожна функція очікує різних аргументів.

У вашому випадку, таким чином, єдиний метод setState(type value)здається кращим.

Однак , немає нічого гіршого , ніж безіменний true, false, 2і т.д. ... ці цінності магії не мають ніякого значення самих по собі. Уникайте примітивної нав'язливості і прийміть сильний набір тексту.

Таким чином, з API POV, я хочу: setState(State state).


З боку реалізації

Я рекомендую робити все, що простіше.

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


Нарешті, розглянемо групування .

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

this.layer1.visible = isHintOn;
this.layer2.visible = ! isHintOn;
this.layer3.visible = isHintOn;

Чому саме layer2тенденція знижується? Це особливість чи це помилка?

Можливо, є 2 списки [layer1, layer3]і [layer2]з чіткою назвою, що вказує на причину їх згрупування, а потім перегляньте ці списки.

Наприклад:

for (auto layer : this.mainLayers) { // layer2
    layer.visible = ! isHintOn;
}
for (auto layer : this.hintLayers) { // layer1 and layer3
    layer.visible = isHintOn;
}

Код говорить сам за себе, зрозуміло, чому існує дві групи і їх поведінка відрізняється.


0

Окремо від питання setOn() + setOff()vs set(flag)я б ретельно розглядав, чи булевий тип тут найкращий. Ви впевнені, що ніколи не буде третього варіанту?

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

setHint(false)

проти

setHint(Visibility::HIDE)

З перерахунком буде набагато простіше продовжити, коли хтось вирішить, що хоче параметр "за потреби":

enum class Visibility {
  SHOW,
  HIDE,
  IF_NEEDED // New
}

проти

setHint(false)
setHint(true)
setHintAutomaticMode(true) // New

0

Згідно ... я знаю важливість уникати використання булевих параметрів для визначення поведінки

Я б запропонував переоцінити ці знання.

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

У вашому прикладі ви правильно оцінюєте параметр у своєму методі. У цьому відношенні він зовсім не відрізняється від будь-якого іншого виду параметрів.

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


0

Визначення питання

Ваше заголовкове запитання "Чи не так [...]?" - але що ви маєте на увазі під "неправильним" ?.

За словами компілятора C # або Java, це не помиляється. Я впевнений, що ви знаєте про це, і це не те, що ви просите. Боюсь, крім цього, у нас тільки nпрограмісти n+1різної думки. У цій відповіді представлено, що з цього приводу має сказати книга « Чистий код ».

Відповідь

Чистий код в цілому є вагомим аргументом проти аргументів функції:

Аргументи важкі. Вони забирають багато концептуальної сили. [...] нашим читачам довелося б інтерпретувати це кожного разу, коли вони його бачили.

"Читачем" тут може бути споживач API. Це також може бути наступний кодер, який ще не знає, чим займається цей код - який може бути вам через місяць. Вони або пройдуть через 2 функції окремо, або через 1 функцію двічі , один раз маючи на увазі trueта один раз falseна увазі.
Словом, використовуйте якомога менше аргументів .

Конкретний випадок аргументу прапора пізніше вирішується безпосередньо:

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

Щоб відповісти на ваші запитання безпосередньо:
Відповідно до Чистого кодексу , рекомендується усунути цей параметр.


Додаткова інформація:

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


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

if (isAfterSunset) light.TurnOn();
else light.TurnOff();

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


Я не знаю, чи ви робите тестування - у цьому випадку також врахуйте це:

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


Ви дійсно поховали тут lede: "... ваше перше речення згадує" важливість уникати [sic] використання булевих параметрів для визначення поведінки ", і це основа для всього питання. Я не бачу причина зробити те, що погано робити простіше для користувача API ". Це цікавий момент, але ви швидше підриваєте свій власний аргумент в останньому абзаці.
Wildcard

Поховали лед? Основою цієї відповіді є "використовувати якомога менше аргументів", що пояснюється в першій половині цієї відповіді. Все після цього - лише додаткова інформація: спростування суперечливого аргументу (іншим користувачем, а не ОП) і те, що стосується не всіх.
Р. Шмітц

Останній абзац просто намагається зрозуміти, що заголовне питання недостатньо визначене, щоб відповісти. ОП запитує, чи це "неправильно", але не каже, хто чи що. За словами Укладача? Виглядає як дійсний код, тому не помиляється. Відповідно до книги Чистий кодекс? Він використовує аргумент прапора, так що так, це "неправильно". Однак я натомість пишу "рекомендовано", оскільки прагматичний> догматичний. Ви вважаєте, що мені потрібно зробити це більш зрозумілим?
Р. Шмітц

тому зачекайте, ваш захист вашої відповіді полягає в тому, що заголовок питання занадто неясний, щоб відповісти? : D Гаразд ... Насправді я подумав, що я процитував цікаву новину.
Wildcard

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