Я большой поклонник статической проверки типов. Это мешает вам делать глупые ошибки, подобные этой:
// java code
Adult a = new Adult();
a.setAge("Roger"); //static type checker would complain
a.setName(42); //and here too
Но это не мешает вам делать глупые ошибки, подобные этой:
Adult a = new Adult();
// obviously you've mixed up these fields, but type checker won't complain
a.setAge(150); // nobody's ever lived this old
a.setWeight(42); // a 42lb adult would have serious health issues
Проблема возникает, когда вы используете один и тот же тип для представления явно разных видов информации. Я думал, что хорошим решением для этого было бы расширение Integer
класса, просто для предотвращения ошибок бизнес-логики, но не для добавления функциональности. Например:
class Age extends Integer{};
class Pounds extends Integer{};
class Adult{
...
public void setAge(Age age){..}
public void setWeight(Pounds pounds){...}
}
Adult a = new Adult();
a.setAge(new Age(42));
a.setWeight(new Pounds(150));
Это считается хорошей практикой? Или есть непредвиденные инженерные проблемы в будущем с таким ограниченным дизайном?
a.SetAge( new Age(150) )
еще не скомпилируется?new Age(...)
объект, вы не сможете неправильно назначить его переменной типаWeight
в других местах. Это уменьшает количество мест, где могут возникнуть ошибки.Ответы:
По сути, вы запрашиваете единичную систему (нет, не юнит-тесты, «юнит», как в «физической единице», например, счетчики, вольт и т. Д.).
В вашем коде
Age
представляет время иPounds
представляет массу. Это приводит к таким вещам, как преобразование единиц измерения, базовые единицы измерения, точность и т. Д.Были / есть попытки внедрить такую вещь в Java, например:
Последние два, кажется, живут в этом github: https://github.com/unitsofmeasurement
C ++ имеет единицы через Boost
LabView поставляется с кучей модулей .
Есть другие примеры на других языках. (изменения приветствуются)
Как вы можете видеть выше, чем более вероятно использование языка для обработки значений с единицами, тем более естественным образом он поддерживает единицы измерения. LabView часто используется для взаимодействия с измерительными приборами. Таким образом, имеет смысл иметь такую функцию в языке, и ее использование, безусловно, будет считаться хорошей практикой.
Но в любом языке высокого уровня общего назначения, где спрос на такую строгость низкий, это, вероятно, неожиданно.
Мое предположение будет: производительность / память. Если вы работаете с большим количеством значений, накладные расходы объекта на значение могут стать проблемой. Но как всегда: преждевременная оптимизация - корень всего зла .
Я думаю, что большая «проблема» - это привыкание людей, так как единица обычно неявно определяется следующим образом:
Люди будут сбиты с толку, когда им придется передать объект как значение для чего-то, что, казалось бы, можно описать простым
int
, когда они не знакомы с системами единиц.источник
int
...» - для которого мы руководствуемся принципом наименьшего сюрприза. Хороший улов.В отличие от нулевого ответа, определение типа для «единицы измерения» может быть полезным, если целого числа недостаточно для описания измерения. Например, вес часто измеряется в нескольких единицах в пределах одной и той же системы измерения. Подумайте «фунты» и «унции» или «килограммы» и «граммы».
Если вам нужен более детальный уровень измерения, определите тип для единицы:
Для чего-то вроде «возраста» я рекомендую рассчитать это во время выполнения на основе даты рождения человека:
источник
То, что вы ищете, известно как помеченные типы . Они являются способом сказать «это целое число, которое представляет возраст», в то время как «это также целое число, но оно представляет вес» и «вы не можете назначить одно другому». Обратите внимание, что это идет дальше, чем физические единицы, такие как метры или килограммы: у меня в программе могут быть «высоты людей» и «расстояния между точками на карте», которые измеряются в метрах, но не совместимы друг с другом, так как я назначил один для другой не имеет смысла с точки зрения бизнес-логики.
Некоторые языки, такие как Scala, довольно легко поддерживают теговые типы (см. Ссылку выше). В других вы можете создавать свои собственные классы-обертки, но это менее удобно.
Проверка, например, проверка того, что рост человека является «разумным», является другой проблемой. Вы можете поместить такой код в ваш
Adult
класс (конструктор или сеттеры) или внутри ваших классов с тегами type / wrapper. В некотором смысле, встроенные классы, такие какURL
илиUUID
выполняют такую роль (среди прочего, например, предоставляя служебные методы).Использование теговых типов или классов-оберток на самом деле поможет улучшить ваш код, будет зависеть от нескольких факторов. Если ваши объекты просты и имеют мало полей, риск неправильного их назначения низок, и дополнительный код, необходимый для использования теговых типов, может не стоить усилий. В сложных системах со сложной структурой и множеством полей (особенно, если многие из них имеют один и тот же примитивный тип), это может оказаться полезным.
В коде, который я пишу, я часто создаю классы-обертки, если обхожу карты. Такие типы, как
Map<String, String>
сами по себе, очень непрозрачны, поэтому перенос их в классы со значимыми именами, например, оченьNameToAddress
помогает. Конечно, с помеченными типами вы можете писатьMap<Name, Address>
и не нуждаться в обертке для всей карты.Однако для простых типов, таких как Strings или Integer, я обнаружил, что классы-обертки (в Java) слишком неприятны. Обычная бизнес-логика была не так уж и плоха, но возникло множество проблем с сериализацией этих типов в JSON, их сопоставлением с объектами БД и т. Д. Вы можете написать мапперы и хуки для всех больших сред (например, Jackson и Spring Data), но дополнительная работа и обслуживание, связанные с этим кодом, компенсируют все, что вы получаете от использования этих оболочек. Конечно, у YMMV и в другой системе баланс может быть разным.
источник