Согласно Неправильно ли использовать логический параметр для определения поведения? Я знаю важность избегания использования логических параметров для определения поведения, например:
оригинальная версия
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 и создать 2 отдельные функции:
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;
}
но модифицированная версия кажется менее ремонтопригодной, потому что:
в нем больше кодов, чем в оригинальной версии
это не может ясно показать, что видимость layer2 противоположна опции подсказки
когда новый слой (например: layer4) добавлен, мне нужно добавить
this.layer4.visible=false;
а также
this.layer4.visible=true;
в setHintOn () и setHintOff () отдельно
Поэтому у меня вопрос: если булев параметр используется только для определения значений, но не для поведения (например, нет в этом параметре if-else), рекомендуется ли по-прежнему исключать этот булев параметр?
setHint(boolean isHintOn)
как частный метод и добавьте открытыйsetHintOn
иsetHintOff
методы, которые соответственно вызываютsetHint(true)
иsetHint(false)
.setHint(true|false)
. Картофельные потахто. По крайней мере, использовать что-то вродеsetHint
иunsetHint
.is
в начале.isValid
и т.д. Так зачем менять это на два слова? Кроме того, «более естественный» в глазах смотрящего. Если вы хотите произнести это как английское предложение, то для меня было бы более естественным иметь «если подсказка включена» с «заправленной» буквой.Ответы:
Разработка API должна быть сосредоточена на том, что наиболее полезно для клиента API, с вызывающей стороны .
Например, если этот новый API требует от вызывающего абонента регулярно писать такой код
тогда должно быть очевидно, что избегать параметра хуже, чем иметь API, который позволяет вызывающей стороне писать
Предыдущая версия просто создает проблему, которая затем должна быть решена на вызывающей стороне (и, возможно, более одного раза). Это не увеличивает удобочитаемость и удобство обслуживания.
Сторона реализации , однако, не должна диктовать , как публичный API выглядит. Если функция, подобная
setHint
параметру, требует меньше кода при реализации, но API в терминахsetHintOn
/setHintOff
выглядит проще для клиента, можно реализовать это следующим образом:Таким образом, хотя публичный API не имеет логического параметра, здесь нет дублирующейся логики, поэтому можно изменить только одно место при появлении нового требования (как в примере с вопросом).
Это работает и наоборот: если
setState
вышеуказанному методу нужно переключаться между двумя разными частями кода, эти части кода могут быть реорганизованы в два разных закрытых метода. Поэтому ИМХО не имеет смысла искать критерий для выбора между «один параметр / один метод» и «нулевые параметры / два метода», просматривая внутренние компоненты. Однако посмотрите, как бы вы хотели видеть API в роли его потребителя.Если вы сомневаетесь, попробуйте использовать «разработку через тестирование» (TDD), которая заставит вас задуматься об общедоступном API и о том, как его использовать.
источник
SetLoanFacility(bool enabled)
, потому что, предоставив кредит, может быть нелегко забрать его снова, и эти два варианта могут включать в себя совершенно разную логику - и вы захотите перейти к отдельному Create Удалить методы.Toggle()
это неправильная функция для предоставления. Вот и весь смысл; если вызывающий абонент заботится только о «изменить его», а не о том, «чем он заканчивается», тоToggle()
это вариант, позволяющий избежать дополнительной проверки и принятия решения. Я бы не назвал это ОБЩИМ случаем, и при этом я не рекомендовал бы сделать это доступным без уважительной причины, но если пользователю нужен переключатель, то я бы дал им переключение.Мартин Фаулер цитирует Кента Бека, который рекомендует отдельные
setOn()
setOff()
методы , но также говорит, что это не следует считать неприкосновенным:Другая рекомендация состоит в том, чтобы использовать перечисляемое значение или тип флагов, чтобы дать
true
иfalse
более точные, зависящие от контекста имена. В твоем примереshowHint
иhideHint
могло быть и лучше.источник
-[NSView setNeedsDisplay:]
метод, который вы передаете,YES
если представление должно перерисовываться, аNO
если нет. Вам почти никогда не нужно говорить, что нет, поэтому UIKit просто не-[UIView setNeedsDisplay]
имеет параметров. У него нет соответствующего-setDoesNotNeedDisplay
метода.bool isPremium
флаг в его примере, но я бы использовал enum (BookingType bookingType
) для параметризации того же метода, если только логика для каждого бронирования не была совершенно другой. «Запутанная логика», на которую ссылается Фаулер, часто желательна, если кто-то хочет увидеть разницу между этими двумя режимами. И если они радикально отличаются, я бы раскрыл параметризованный метод извне и реализовал бы отдельные методы внутри.Я думаю, что вы смешиваете две вещи в своем посте: API и реализацию. В обоих случаях я не думаю, что есть строгое правило, которое вы можете использовать постоянно, но вы должны рассмотреть эти две вещи независимо (насколько это возможно).
Давайте начнем с API, оба:
а также:
являются допустимыми альтернативами в зависимости от того, что ваш объект должен предложить и как ваши клиенты будут использовать API. Как отметил Док, если у ваших пользователей уже есть логическая переменная (из элемента управления пользовательского интерфейса, действия пользователя, внешнего интерфейса, API и т. Д.), Первый вариант имеет больше смысла, в противном случае вы просто добавляете дополнительный оператор if в коде клиента. , Однако, если, например, вы меняете подсказку на true при запуске процесса и на false в конце, первый вариант дает вам что-то вроде этого:
в то время как второй вариант дает вам это:
какая ИМО гораздо более читабельна, поэтому я перейду ко второму варианту в этом случае. Очевидно, что ничто не мешает вам предлагать оба варианта (или даже больше, вы можете использовать перечисление, как сказал Грэм, если это имеет смысл, например).
Дело в том, что вы должны выбирать свой API, основываясь на том, что должен делать объект и как клиенты будут его использовать, а не на том, как вы собираетесь его реализовать.
Затем вы должны выбрать способ реализации вашего публичного API. Допустим, мы выбрали методы
setHintOn
иsetHintOff
наш публичный API, и они используют эту общую логику, как в вашем примере. Вы можете легко абстрагировать эту логику с помощью частного метода (код, скопированный из Doc):И наоборот, допустим, что мы выбрали
setHint(boolean isHintOn)
наш API, но давайте перевернем ваш пример, по любой причине установка подсказки On полностью отличается от установки его в Off. В этом случае мы могли бы реализовать это следующим образом:Или даже:
Дело в том, что в обоих случаях мы сначала выбрали наш общедоступный API, а затем адаптировали нашу реализацию для соответствия выбранному API (и имеющимся у нас ограничениям), а не наоборот.
Между прочим, я думаю, что то же самое относится и к сообщению, которое вы связали с использованием логического параметра для определения поведения, то есть вы должны решить, основываясь на вашем конкретном случае использования, а не на каком-то жестком правиле (хотя в этом конкретном случае обычно правильные действия это разбить его на несколько функций).
источник
begin
иend
или синонимы, просто чтобы четко дать понять, что они делают, и подразумевать, что начало должно иметь конец и наоборот.using
блоки в C #,with
менеджеры контекста операторов в Python, передача тело как лямбда или вызываемый объект (например, синтаксис блока Ruby) и т. д.Перво-наперво: код не менее автоматически обслуживаем, просто потому, что он немного длиннее. Ясность - вот что имеет значение.
Теперь, если вы действительно имеете дело с данными, то у вас есть установщик для логического свойства. В этом случае вы можете просто сохранить это значение напрямую и получить видимости слоя, т.е.
(Я позволил себе дать реальным названиям слоев - если у вас нет этого в исходном коде, я бы начал с этого)
Это все еще оставляет вас с вопросом о том, иметь ли
setHintVisibility(bool)
метод. Лично я бы рекомендовал заменить его сshowHint()
иhideHint()
методом - как будет на самом деле просто и вам не придется менять их , когда вы добавляете слои. Однако, это не однозначно правильно / неправильно.Теперь, если вызов функции на самом деле должен изменить видимость этих слоев, у вас действительно есть поведение. В этом случае я бы определенно рекомендовал отдельные методы.
источник
showHint
противsetHintVisibility
). Я упомянул новый слой только потому, что OP беспокоился об этом. Кроме того , нам нужно просто добавить еще один новый метод:isLayer4Visible
.showHint
иhideHint
просто установите дляisHintVisible
атрибута значение true / false, и это не изменится.Булевы параметры хороши во втором примере. Как вы уже поняли, логические параметры сами по себе не являются проблематичными. Это переключение поведения на основе флага, что проблематично.
Первый пример, тем не менее, проблематичен, потому что наименование указывает метод установки, но реализации кажутся чем-то другим. Таким образом, у вас есть антипаттерн с переключением поведения и обманчиво названный метод. Но если метод на самом деле является обычным установщиком (без переключения поведения), то с этим проблем нет
setState(boolean)
. Наличие двух методов,setStateTrue()
иsetStateFalse()
это просто без необходимости усложнять вещи без пользы.источник
Другой способ решить эту проблему - ввести объект, представляющий каждую подсказку, и заставить объект отвечать за определение логических значений, связанных с этой подсказкой. Таким образом, вы можете добавлять новые перестановки, а не просто иметь два логических состояния.
Например, в Java вы можете сделать:
И тогда ваш код вызывающего абонента будет выглядеть так:
И ваш код реализации будет выглядеть так:
Это позволяет сохранить код реализации и код вызывающей стороны в обмен на определение нового типа данных, который четко отображает строго типизированные именованные намерения в соответствующие наборы состояний. Я думаю, что все вокруг лучше.
источник
Когда у меня есть сомнения по поводу таких вещей. Мне нравится представлять, как будет выглядеть трассировка стека.
В течение многих лет я работал над проектом PHP, который использовал ту же функцию, что и setter и getter . Если вы передали значение null, оно вернет значение, в противном случае установите его. С ним было ужасно работать .
Вот пример трассировки стека:
Вы понятия не имели о внутреннем состоянии, и это усложняло отладку. Существует множество других негативных побочных эффектов, но постарайтесь увидеть, что в подробном названии функции есть значение и ясность, когда функции выполняют одно действие.
Теперь представьте, как работает трассировка стека для функции, которая имеет поведение A или B на основе логического значения.
Это сбивает с толку, если вы спросите меня. Одни и те же
setVisible
линии дают два разных пути трассировки.Итак, вернемся к вашему вопросу. Попробуйте представить, как будет выглядеть трассировка стека, как она сообщает человеку, что происходит, и спросите себя, помогаете ли вы будущему человеку отладить код.
Вот несколько советов:
Иногда код выглядит чрезмерно под микроскопом, но когда вы возвращаетесь к более широкой картине, минималистичный подход заставит его исчезнуть. Если вам нужно, чтобы он выделялся, чтобы вы могли поддерживать его. Добавление многих небольших функций может показаться слишком многословным, но это улучшает удобство обслуживания при широком использовании в более широком контексте.
источник
setVisible
трассировке стека, так это то, что вызов, по-setVisible(true)
видимому, приводит к вызовуsetVisible(false)
(или наоборот, в зависимости от того, как вы получили эту трассировку).Почти в каждом случае, когда вы передаете
boolean
параметр методу в качестве флага для изменения поведения чего-либо, вы должны рассмотреть более явный и безопасный для этого способ.Если вы делаете не что иное, как использование состояния
Enum
, представляющего состояние, вы улучшили понимание своего кода.В этом примере используется
Node
класс изJavaFX
:всегда лучше, чем, как найдено на многих
JavaFX
объектах:но я думаю ,
.setVisible()
и.setHidden()
являются лучшим решением для ситуации , когда флаг являетсяboolean
поскольку он является наиболее явным и наименее многословным.в случае чего-то с множественным выбором это еще более важно сделать таким образом.
EnumSet
существует только по этой причине.источник
При выборе между подходом для некоторого интерфейса, который передает (логический) параметр, и перегруженными методами без указанного параметра, обратите внимание на потребляющих клиентов.
Если все виды использования будут передавать постоянные значения (например, истина, ложь), то это приводит к перегрузкам.
Если все случаи использования передают значение переменной, то это аргумент для метода с параметрическим подходом.
Если ни одна из этих крайностей не применима, это означает, что существует разнородное использование клиентов, поэтому вам нужно выбрать, поддерживать ли обе формы, или настроить одну категорию клиентов для адаптации к другой (что для них более неестественный) стиль.
источник
Есть два соображения для проектирования:
Их не следует смешивать.
Это прекрасно, чтобы:
Таким образом, любой аргумент, который пытается сбалансировать затраты / выгоды дизайна API с затратами / преимуществами проекта реализации, является сомнительным и должен быть тщательно изучен.
На стороне API
Как программист, я обычно предпочитаю программируемые API. Код гораздо понятнее, когда я могу переслать значение, чем когда мне нужно выражение
if
/switch
для значения, чтобы решить, какую функцию вызывать.Последнее может быть необходимо, если каждая функция ожидает разные аргументы.
В вашем случае, таким образом, единственный метод
setState(type value)
кажется лучше.Тем не менее , нет ничего хуже, чем безымянные
true
,false
и2
т. Д. ... эти магические ценности не имеют значения сами по себе. Избегайте примитивных навязчивых идей и придерживайтесь строгой типизации.Таким образом, с API POV, я хочу:
setState(State state)
.На стороне реализации
Я рекомендую делать все, что проще.
Если метод прост, его лучше хранить вместе. Если поток управления запутан, лучше разделить его на несколько методов, каждый из которых имеет дело с подслучаем или этапом конвейера.
Наконец, рассмотрим группировку .
В вашем примере (с добавленными пробелами для удобства чтения):
Почему
layer2
противодействует тенденция? Это особенность или это ошибка?Возможно ли иметь 2 списка
[layer1, layer3]
и[layer2]
, с явным именем, указывающим причину, по которой они сгруппированы, а затем выполнить итерацию по этим спискам.Например:
Кодекс говорит сам за себя, понятно, почему есть две группы, и их поведение отличается.
источник
Отдельно от вопроса
setOn() + setOff()
противset(flag)
, я бы тщательно подумал, лучше ли здесь использовать логический тип. Вы уверены, что никогда не будет третьего варианта?Возможно, стоит рассмотреть перечисление вместо логического. Помимо возможности расширения, это также затрудняет неправильное получение логического значения, например:
против
С помощью enum будет намного легче расширяться, когда кто-то решит, что он хочет использовать опцию «при необходимости»:
против
источник
Я бы предложил переоценить эти знания.
Прежде всего, я не вижу вывод, который вы предлагаете в вопросе SE, который вы связали. В основном они говорят о переадресации параметра на несколько этапов вызовов метода, где он оценивается очень далеко по цепочке.
В вашем примере вы оцениваете параметр прямо в вашем методе. В этом отношении он ничем не отличается от других параметров.
В общем, нет ничего плохого в использовании логических параметров; и, очевидно, какой-либо параметр будет определять поведение, или с чего бы вам это вообще понадобилось?
источник
Определение вопроса
Ваш титульный вопрос: "Это неправильно [...]?" - но что вы имеете в виду под «неправильно»?
Согласно компилятору C # или Java, это не так. Я уверен, что вы знаете об этом, и это не то, что вы спрашиваете. Боюсь, кроме этого, у нас только
n
программистыn+1
разные мнения. Этот ответ представляет то, что книга Чистый Кодекс говорит об этом.Ответ
Чистый код делает веские аргументы против аргументов функции в целом:
«Читатель» здесь может быть потребителем API. Это также может быть следующий кодировщик, который еще не знает, что делает этот код, - которым вы могли бы стать через месяц. Они будут либо проходить через 2 функции отдельно, либо через 1 функцию дважды , один раз помня
true
и один разfalse
помня.Короче говоря, используйте как можно меньше аргументов .
Конкретный случай аргумента флага позже рассматривается напрямую:
Чтобы ответить на ваши вопросы напрямую: в
соответствии с «Чистым кодом» рекомендуется исключить этот параметр.
Дополнительная информация:
Ваш пример довольно прост, но даже там вы можете увидеть простоту, распространяющуюся на ваш код: функции без параметров выполняют только простые присваивания, в то время как другая функция должна выполнять булеву арифметику для достижения той же цели. В этом упрощенном примере это тривиальная логическая арифметика, но она может быть довольно сложной в реальной ситуации.
Я видел много аргументов, что вы должны сделать это зависимым от пользователя API, потому что делать это во многих местах было бы глупо:
Я действительно согласен , что - то неоптимальным, что здесь происходит. Может быть, это слишком очевидно, чтобы увидеть, но ваше самое первое предложение упоминает «важность избегать [sic] использования булевых параметров для определения поведения», и это является основой для всего вопроса. Я не вижу причины, чтобы сделать эту вещь, которую плохо делать проще для пользователя API.
Я не знаю, проводите ли вы тестирование - в этом случае также подумайте об этом:
источник