2008-08-28 7 views
16

Я нахожусь в середине чтения отличной Clean CodeПереходя нуль к методу

Одна дискуссии о прохождении аннулирует в метод.

public class MetricsCalculator { 
    public double xProjection(Point p1, Point p2) { 
     return (p2.x - p1.x) * 1.5; 
    } 
} 
... 
calculator.xProjection(null, new Point(12,13)); 

Он представляет собой различные способы обработки:

public double xProjection(Point p1, Point p2) { 
    if (p1 == null || p2 == null) { 
     throw new IllegalArgumentException("Invalid argument for xProjection"); 
    } 
    return (p2.x - p1.x) * 1.5; 
} 

public double xProjection(Point p1, Point p2) { 
    assert p1 != null : "p1 should not be null"; 
    assert p2 != null : "p2 should not be null"; 
    return (p2.x - p1.x) * 1.5; 
} 

Я предпочитаю assertions подход, но мне не нравится тот факт, что утверждения по умолчанию выключены.

Книга, наконец, заявляет:

В большинстве языков программирования нет хорошего способа иметь дело с нулем, который передается от вызывающего случайно. Поскольку это так, рациональный подход - запретить передачу null по умолчанию.

На самом деле это не значит, что вы будете применять это ограничение?

У любого из вас есть сильные мнения в любом случае.

ответ

4

Как использование утверждений, так и исключение исключений являются действительными подходами здесь. Любой механизм может использоваться для указания ошибки программирования, а не ошибки времени выполнения, как в данном случае.

  • Утверждения имеют преимущество в производительности, поскольку они обычно отключены в производственных системах.
  • Исключения имеют преимущество безопасности, так как проверка всегда выполняется.

Выбор действительно зависит от практики разработки проекта. Проект в целом должен принять решение о политике утверждения: если выбор состоит в том, чтобы разрешить утверждения во время всей разработки, я бы сказал, чтобы использовать утверждения для проверки этого недопустимого параметра - в производственной системе исключение NullPointerException, вызванное ошибка программирования вряд ли сможет быть поймана и обработана в значимом смысле так или иначе, и поэтому будет действовать точно так же, как утверждение.

Практически все же, я знаю много разработчиков, которые не доверяют, что утверждения будут включены, когда это необходимо, и поэтому выбирают безопасность бросания исключения NullPointerException.

Конечно, если вы не можете применять политику для своего кода (например, если вы создаете библиотеку, и так зависит от того, как другие разработчики запускают ваш код), вы должны выбрать безопасный подход throwing NullPointerException для тех методов, которые являются частью API библиотеки.

1

Обычно я предпочитаю не делать этого, так как это просто замедляет работу. В любом случае NullPointerExceptions выдаются позже, что быстро приведет пользователя к обнаружению того, что они передают значение null методу. Раньше я проверял, но 40% моего кода оказалось проверкой кода, и в этот момент я решил, что это просто не стоит приятных сообщений об утверждении.

3

На самом деле не идет речь о том, как вы будете применять это ограничение?

Вы применяете его, бросая ArgumentExcexception, если они пройдут в нуле.

if (p1 == null || p2 == null) { 
    throw new IllegalArgumentException("Invalid argument for xProjection"); 
} 
1

Я согласен или не согласен с wvdschel's post, это зависит от того, что он конкретно говорил.

В этом случае, конечно, этот метод выйдет из строя на null, поэтому явная проверка здесь, вероятно, не нужна.

Однако, если метод просто хранит переданные данные, и есть другой метод, который вы позже называете, который будет иметь дело с ним, обнаружение неудачного ввода как можно раньше является ключом к исправлению ошибок быстрее. На этом более позднем этапе может существовать множество способов, которым плохие данные были переданы вашему классу. Это похоже на попытку выяснить, как крысы попали в ваш дом после этого, пытаясь найти дырку где-то.

2

Я предпочитаю использовать утверждения.

У меня есть правило, что я использую утверждения только в публичных и защищенных методах. Это связано с тем, что я считаю, что метод вызова должен гарантировать, что он передаёт действительные аргументы частным методам.

+0

От https://docs.oracle.com/javase/7/docs/technotes/guides/language/assert.html Не используйте утверждения для проверки аргументов в публичных методах. Проверка аргументов обычно является частью опубликованных спецификаций (или контракта) метода, и эти спецификации должны выполняться независимо от того, включены или запрещены утверждения. Другая проблема с использованием утверждений для проверки аргументов заключается в том, что ошибочные аргументы должны приводить к соответствующему исключению среды выполнения (IllegalArgumentException, IndexOutOfBoundsException и т. Д.). Ошибка утверждения не приведет к соответствующему исключению. – 2017-12-27 11:01:40

8

Общее правило: если ваш метод не ожидает аргументов null, тогда вы должны бросить System.ArgumentNullException. Throwing proper Exception не только защищает вас от повреждения ресурсов и других плохих вещей, но и служит руководством для пользователей вашего кода, экономя время, затрачиваемое на отладку вашего кода.

Читайте также статью о Defensive programming

+4

Это хороший ответ C#, а не как хороший ответ java :) – 2008-10-30 01:27:58

1

Karcher @ Крис, я бы сказал абсолютно правильно. Единственное, что я хотел бы сказать, это проверить параметры отдельно и иметь отчет об исключении, который был также нулевым, так как он отслеживает, где значение null происходит намного проще.

@wvdschel ничего себе! Если для написания кода для вас слишком много усилий, вы должны изучить что-то вроде PostSharp (или эквивалент Java, если таковой имеется), который может обрабатывать ваши сборки и вставлять проверки параметров для вас.

1

Хотя это не связано строго, вы можете взглянуть на Spec#.

Я думаю, что он все еще находится в разработке (от Microsoft), но некоторые CTP доступны, и он выглядит многообещающим. В основном это позволяет сделать это:

public static int Divide(int x, int y) 
    requires y != 0 otherwise ArgumentException; 
    { 
    } 

или

public static int Subtract(int x, int y) 
    requires x > y; 
    ensures result > y; 
    { 
    return x - y; 
    } 

Он также предоставляет другие функции, такие как типы NotNull. Он построен поверх .NET Framework 2.0 и полностью совместим с ним. Синтаксис, как вы можете видеть, это C#.

2

SpeC# выглядит очень интересно!

Когда что-то подобное недоступно, я обычно тестирую неличные методы с нулевой проверкой времени выполнения и утверждениями для внутренних методов. Вместо кода проверки нулевой явно в каждом методе, делегировать, что к классу утилит с запорным компенсационным методом:

/** 
* Checks to see if an object is null, and if so 
* generates an IllegalArgumentException with a fitting message. 
* 
* @param o The object to check against null. 
* @param name The name of the object, used to format the exception message 
* 
* @throws IllegalArgumentException if o is null. 
*/ 
public static void checkNull(Object o, String name) 
    throws IllegalArgumentException { 
    if (null == o) 
     throw new IllegalArgumentException(name + " must not be null"); 
} 

public static void checkNull(Object o) throws IllegalArgumentException { 
    checkNull(o, "object"); 
} 

// untested: 
public static void checkNull(Object... os) throws IllegalArgumentException { 
    for(Object o in os) checkNull(o); 
} 

Тогда проверка превращается в:

public void someFun(String val1, String val2) throws IllegalArgumentException { 
    ExceptionUtilities.checkNull(val1, "val1"); 
    ExceptionUtilities.checkNull(val2, "val2"); 

    /** alternatively: 
    ExceptionUtilities.checkNull(val1, val2); 
    **/ 

    /** ... **/ 
} 

Это может быть добавлен редактор макросов или сценарий обработки кода. Редактировать: Эта подробная проверка также может быть добавлена, но я думаю, что значительно проще автоматизировать добавление одной строки.

4

Также не касается немедленного использования, но имеет отношение к упоминанию SpeC# ... Есть предложение добавить «нуль-безопасные типы» в будущую версию Java: "Enhanced null handling - Null-safe types".

В соответствии с предложением, ваш метод стал бы

public class MetricsCalculator { 
    public double xProjection(#Point p1, #Point p2) { 
     return (p2.x - p1.x) * 1.5; 
    } 
} 

#Point где тип непредставленных null ссылки на объекты типа Point.

+0

Я действительно не знаю, хочу ли я, чтобы он был частью Java, но это очень интересно. – 2008-10-29 22:51:25

0

Throwing C# ArgumentException, или Java IllegalArgumentException, в самом начале метода, выглядит для меня самым ясным решением.

Всегда следует соблюдать осторожность при использовании исключений времени выполнения - исключения, которые не объявлены в сигнатуре метода. Поскольку компилятор не принуждает вас их поймать, их очень легко забыть. Удостоверьтесь, что у вас есть какая-то «обработка всех исключений» для предотвращения внезапного прекращения работы программного обеспечения. Это самая важная часть вашего пользовательского опыта.

0

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

2

В большинстве языков программирования нет никакого способа справиться с пустым, который случайно передается вызывающим абонентом. Поскольку это так, рациональный подход - запретить передачу null по умолчанию.

Я нашел JetBrains '@Nullable и @NotNull аннотаций подход для борьбы с этим самым гениальным, до сих пор. Это, к сожалению, IDE, но действительно чистая и мощная, ИМО.

http://www.jetbrains.com/idea/documentation/howto.html

Имея это (или нечто подобное) в качестве стандарта Java будет очень приятно.

1

Немного не по теме, но одна особенность findbugs, которая, по моему мнению, очень полезна, состоит в том, чтобы уметь комментировать параметры методов для описания того, какие параметры не должны передаваться пустым значением.

Используя статический анализ вашего кода, findbugs затем могут указывать места, где вызывается метод с потенциально нулевым значением.

Это имеет два преимущества:

  1. аннотации описывают свое намерение о том, как следует назвать метод, помогая ДОКУМЕНТАЦИЮ
  2. FindBugs может указывать на потенциальные проблемы абонентов метода, что позволяет отследить потенциал ошибок.

Полезно только при наличии доступа к коду, который вызывает ваши методы ... но это обычно так.

0

В Java-способе, предполагая, что значение null происходит из-за ошибки программирования (то есть никогда не должно выходить за пределы фазы тестирования), затем оставьте систему бросить ее или если есть побочные эффекты, достигающие этой точки, проверьте значение null в начале и бросить либо IllegalArgumentException, либо NullPointerException.

Если нуль может исходить из исключения , но вы не хотите использовать проверенное исключение для этого, то вы обязательно захотите перейти по пути IllegalArgumentException в начале метода.

1

Поскольку не по теме, похоже, стала темой, Scala предлагает интересный подход к этому. Предполагается, что все типы не являются нулевыми, если только вы не обходите его в Option, чтобы указать, что он может быть нулевым. Итак:

// allocate null 
var name : Option[String] 
name = None 

// allocate a value 
name = Any["Hello"] 

// print the value if we can 
name match { 
    Any[x] => print x 
    _ => print "Nothing at all" 
} 
+0

Интересно :-) нужно будет читать на scala. – toolkit 2008-10-30 11:27:29