Paths
Утилитарный класс — это класс-помощник, содержащий статические переменные и статические методы, которые выполняют определенный перечень задач, объединённых одним смыслом.
Давайте рассмотрим примеры стандартных утилитарных классов:
java.lang.Math | Класс, который может выполнять множество различных математических вычислений, дает нам некоторые математические константы. |
java.util.Arrays | Класс содержит различные методы для работы с массивами (такие как сортировка и поиск). Этот класс также содержит статическую фабрику, которая позволяет просматривать массивы как списки. |
java.lang.System | Класс реализует методы для работы с системой. Чаще всего мы можем его использовать для вывода текста в консоли. Для этого мы обращаемся к статической переменной out , которая хранит в себе объект класса PrintStream , и уже у него вызываем метод println ( System.out.println ). |
Мы можем и сами создать утилитарный класс: для этого мы просто создаем класс со статическими public методами, которые нам необходимы. Но нужно помнить, что для создания утилитарного класса необходимы весомые причины. Например, необходимость использования одного и того же метода или набора методов, выполняющего единственную задачу (скажем, сложное вычисление) из нескольких различных классов.
Давай рассмотрим пример утилитарного класса — класс Paths .
Класс Paths
Это класс состоит всего из одного статического метода — get — с разными принимаемыми параметрами.
Мы можем передать в метод get :
get(String first, String. more) | Путь целиком, либо перечисление названий директорий и (или) файла в последнем аргументе. |
get(URI uri) | URI. |
Этот утилитарный класс решает задачу преобразования пути (в виде строки) или URI в Path . Path мы уже проходили и понимаем, зачем он нам нужен и как мы с ним далее можем работать.
Часто бывает, что мы имеем дело с путем именно в виде String или URI. Вот тут-то мы и можем воспользоваться методами утилитарного класса Paths .
Давайте рассмотрим примеры:
Path path = Paths.get("C:\\Users\\User\\Documents\\MyFile.txt");
Path path = Paths.get(URI.create("file:///Users/User/Code/MyClass.java"));
Path path = Paths.get(System.getProperty("user.home"),"documents", "document.txt");
Но в самом деле есть один нюанс. С появлением Java 11 версии любая реализация метода get вызывает в себе Path.of .
public static Path get(String first, String. more) < return Path.of(first, more); >public static Path get(URI uri)
Теперь этот утилитарный класс может быть признан устаревшим, поэтому мы должны использовать именно Path.of для наших целей.
Path path = Paths.get("C:\\Users\\User\\Documents\\MyFile.txt");
Path path = Path.of("C:\\Users\\User\\Documents\\MyFile.txt");
Path path = Paths.get(URI.create("file:///Users/User/Code/MyClass.java"));
Path path = Path.of(URI.create("file:///Users/User/Code/MyClass.java"));
Path path = Paths.get(System.getProperty("user.home"),"documents", "document.txt");
ath path = Path.of(System.getProperty("user.home"),"documents", "document.txt");
Create a Utility Class in Java
Utility classes in Java are also known as Helper Class. It is an efficient way to create methods that can be re-used. The code we need to use over and over again can be put inside a utility class.
Usage of Utility Class in Java
The Java utility class is a stateless class that cannot be instantiated and declared using final and public keywords. In the example given below, we have a UtilityClassExample , which has a private constructor that prevents instantiation. For example, there are many examples of Util classes in Java like Apache StringUtils , CollectionUtils , or java.lang.Math .
The methods in the utility class should be declared static and not abstract as object methods need instantiation. The final keyword prevents subclassing. Here, we create our own Utility class with a private constructor, which, when invoked, throws an exception. Since we declared a private constructor, default can not be created; hence class can not be instantiated.
In the code given below, we have all members of the UtilityClassExample static. If we need to add or subtract two int or float type variables, we created methods in the utility class to re-use the code. We also have a method that returns a number multiplied by ten.
In the method addFloatValues() , we have also used Math.round() to round off the result to the nearest int. The Float class has the sum() method that returns the sum of two float arguments. We call each member method of this utility class passing arguments and print the output in the main() method of the class TestUtitity . Thus this utility class has methods that are used very often.
public final class UtilityClassExample private static final int constantValue = 10; private UtilityClassExample() throw new java.lang.UnsupportedOperationException("Utility class and cannot be instantiated"); > public static int addIntValues(int i,int j) int sum = i + j; return sum; > public static int subIntValues(int i,int j) int diff = 0; if(i>j) diff = i - j; >else diff = j - i; > return diff; > public static float addFloatValues(float i, float j) float sum = Float.sum(i,j); return Math.round(sum); > public static float subFloatValues(float i, float j) float diff = 0.00f; if(i>j) diff = i - j; >else diff = j - i; > return diff; > public static int returnValAfterMultiplying(int i) return i * constantValue; > > class TestUtility public static void main(String [] args) int a = 4; int b = 9; int c = 7; float d = 3.12f; float e = 6.85f; System.out.println(+a+" multiplied by ten is : "+UtilityClassExample.returnValAfterMultiplying(a)); System.out.println(b+"+"+c+" is : "+UtilityClassExample.addIntValues(b,c)); System.out.println(d+"+"+e+" is : "+UtilityClassExample.addFloatValues(d,e)); System.out.println(b+"-"+a+" is : "+UtilityClassExample.subIntValues(b,a)); System.out.println(e+"-"+d+" is : "+UtilityClassExample.subFloatValues(e,d)); > >
4 multiplied by ten is : 40 9+7 is : 16 3.12+6.85 is : 10.0 9-4 is : 5 6.85-3.12 is : 3.73
It is not recommended to use a Utility class of your own as it reduces flexibility.
Rupam Saini is an android developer, who also works sometimes as a web developer., He likes to read books and write about various things.
Утилитарные классы — добро или зло?
Есть вот такой спорный вопрос. Многие используют утилитарные классы, которые состоят из статических методов. Это всего функции со входом и результатом. Такие используют и библиотеки Java, например, Math. Многие делают свои классы типа Utils, которые содержат в себе набор используемых в разных местах инструментов. Я не вижу минусов, с точки зрения SOLID: S класс создан для общей по смыслу задачи O да L неприменимо. Обычно не используется наследование. I неприменимо. Нет интерефейса D да. Также они отвечают принципу KISS, и легко модульно тестируются при желании. На методы также можно ссылаться для функционального подхода (напр., Function
Статический метод просто выполняет свою работу. Он ни от чего не зависит, они не изменяется со временем. Ты просто передаешь ему значение, он значение возвращает. Он идеален. Какой смысл создавать объект? ЧТобы прилепить ООП?
легко модульно тестируются — давайте отталкиваться от этого. Если статические методы такого класса легко тестируются, то есть они зависят только от входных парметров, то всё хорошо. Методы класса Math соответствуют этим требованиям. Например, Math.sin всегда будет возвращать один и тот же результат для одного и того же входного значения. Но часто делают так, что такие методы используют внутри себя поля других статических классов. В итоге при вызове одного и того же метода с одинаковыми параметрами можно получать разные результаты. Вот против такого дизайна и направлены все возражения.
2 ответа 2
Имхо, статические методы должны:
1) делать очень маленькую специфичную работу;
2) не должны напрямую относиться к бизнес-логике;
3) как следствие 1) и 2) — их не надо мокать;
4) их можно тестировать отдельно.
Давайте рассмотрим каждый пункт отдельно.
Делать очень маленькую специфичную работу
Я имею ввиду, что в статическом методе-помощнике должно быть минимум логики, которая относится только к той одной конкретной операции, для которой метод предназначен. Например, как вы думаете, сколько логики в методe типа Math.min ?
Также такие методы должны быть чистыми. То есть при одинаковых входных данных они должны возвращать один и тот же результат и они не должны иметь состояния. (Но тут есть исключения, связанные со временем. Например, получение текущего времени в разные моменты будет возвращать разный результат).
Не должны напрямую относиться к бизнес-логике
Ваша бизнес-логика должна обладать гибкостью. Например, сегодня вы используете один калькулятор зарплат, а завтра — другой. По идее, это должно достигаться просто подменой одной реализации калькулятора другой. Отсутсвие O и L из SOLID по сути связывает статические методы, вы не можете подложить другую реализацию статического метода в рантайме, если только этот статический метод не нарушает предыдущий пункт. Поэтому, если вам надо сделать что-то, что не относится напрямую к бизнес логике и никогда не планируется измениться (например, округлить число, сортировать массив, возвести число в степень, посчитать расстояние между точками, распарсить дату их строки) — вы смело можете писать статические методы. Но если вам надо реализовать бизнес-логику (найти победителя в игре, посчитать налоговую декларацию, обновить значение поля А в зависимости от значения поля Б на форме, отправить сообщение юзеру при возникновении какого-либо события) — то эта логика должна быть максимально гибкой, основные её части должны быть заменяемы, там где надо даже в райтейме — то есть вы не можете делать её на статических методах.
Как следствие 1) и 2) — их не надо мокать
При тестировании логики, которая использует ваши статические методы, нет никакого смысла их мокать (опять же, если речь идет не о получении текущей даты/времени), так как эти методы к логике не относятся, никогда не получат альтернативной реализации и всегда на один и тот же запрос вернут один и тот же ответ. Вам никогда не понадобится имитация того, что строка «10» была распарсена в 15 или 20. «10» всегда при парсинге будет 10.
Их можно тестировать отдельно
Тут, по идее, очевидно, если вы следовали предыдущим советам, то у вас ваши статические методы — небольшие, не будут меняться, не имеют состояния и не относятся напрямую к бизнес-логике. В этом случае для тестирования этих методов вам не надо тянуть классы бизнес-логики или мокать их как-либо: вы можете спокойно тестировать эти методы без привязки к логике приложения.