Статьи

Делай это коротко, но делай это правильно!

Написание сжатого, элегантного и понятного кода всегда было сложной задачей для разработчиков. Ваши коллеги будут не только благодарны вам, но вы также будете удивлены, увидев, насколько увлекательно постоянно ожидать решения по рефакторингу, чтобы делать больше (или, по крайней мере, то же самое) с меньшим количеством кода. Раньше говорили, что хорошие программисты — ленивые программисты. Правда, правда … Но действительно хорошие программисты добавляют красоту к этому .

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

Давайте начнем с конкретного примера:

1
2
3
4
5
String color = "green";
...
if  ( color!=null && color.equals("red") ) {
 System.out.println("Sorry, red is forbidden !");
}

Один из первых уроков, которые вы, вероятно, извлекли из своего учителя Java (или объектно-ориентированного программирования), — это важность проверки недействительности объекта перед вызовом метода для него. Исключения нулевого указателя (NPE) действительно являются одними из самых распространенных (и раздражающих) ошибок, возникающих в коде объектно-ориентированных языков .

В приведенном выше примере безопасно убедиться, что объект String ‘color’ не равен NULL, прежде чем сравнивать его с константой. Лично я всегда считал это ненужным бременем для программиста — особенно для современных ОО-языков, таких как Java. В качестве обходного пути, существует (действительно глупый) прием для переписывания условия без необходимости проверки на ничтожность. Помните, что метод equals () является симметричным (если a = b, то b = a).

1
2
3
if  ( "red".equals(color) ) {
 System.out.println("Sorry, red is forbidden !");
}

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

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

1
2
3
4
5
if ( "red".equals(color) ||
     "yellow".equals(color) ||
     "blue".equals(color) ) {
 System.out.println("This is a primary color");
}

Иногда я встречал более опытных Java-программистов, сокращающих такое длинное выражение if :

1
2
3
if ( "red|yellow|blue".indexOf(color)>=0 ) {
 System.out.println("This is a primary color");
}

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

1
2
3
4
5
String type = "man";
...
if ( "woman|child".indexOf(type)>=0 ) {
 System.out.println("Women and children first !");
}

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
import java.util.HashSet;
import java.util.Set;
 
public static final Set<string> PRIMARY_COLORS;
static {
 PRIMARY_COLORS = new HashSet<string>();
 PRIMARY_COLORS.add("red");
 PRIMARY_COLORS.add("yellow");
 PRIMARY_COLORS.add("blue");
}
...
if ( PRIMARY_COLORS.contains(color) ) {
 System.out.println("This is a primary color");
}

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

1
2
3
4
5
public static final Set<string> PRIMARY_COLORS = new HashSet<string>() {{
 add("red");
 add("yellow");
 add("blue");
}};

Если сжатие кода становится навязчивой идеей, Java Collections Framework также может прийти на помощь:

1
2
3
4
5
6
7
8
import java.util.Arrays;
import java.util.Collection;
 
public static final Collection<string> PRIMARY_COLORS = Arrays.asList("red", "yellow", "blue");
...
if ( PRIMARY_COLORS.contains(color) ) {
 System.out.println("This is a primary color");
}

Ключевое слово final предотвращает повторное присвоение переменной PRIMARY_COLORS другой коллекции значений — это особенно важно, когда ваша переменная определена как public . Если безопасность является серьезной проблемой, вы должны также обернуть оригинальную коллекцию в неизменяемую коллекцию. Это гарантирует доступ только для чтения.

1
2
3
4
5
6
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
 
public static final Collection PRIMARY_COLORS =
   Collections.unmodifiableCollection( Arrays.asList("red", "yellow", "blue") );

Следует заметить, что, хотя и более читабельный, использование набора значений (особенно в больших коллекциях), как правило, будет медленнее, чем (я бы сказал: не так быстро, как), классических ленивых ИЛИ (т. Е. Использование «||» вместо « | ‘) из-за оценки короткого замыкания . Я склонен думать, что в настоящее время такие соображения становятся бесполезными.

После 16 лет жалоб у Java 7 — наконец-то! — введена поддержка String в операторах switch-case . Это позволяет нам кодировать такие вещи, как:

01
02
03
04
05
06
07
08
09
10
11
12
boolean primary;
switch(color) {
 case "red":
  primary=true; break;
 case "green":
  primary=true; break;
 case "blue":
  primary=true; break;
 default:
     primary=false;
}
if (primary) System.out.println("This is a primary color");

Давайте, наконец, закончим тем, что, пожалуй, является наиболее объектно-ориентированным решением нашей (так сказать) проблемы. Перечисления Java в основном являются классами и поэтому могут иметь методы и поля, как и любые другие классы. Применяя шаблон проектирования « Шаблонный метод» , можно определить абстрактный метод (моделирование теста), который должен быть реализован всеми подклассами (моделирование ответа теста, примененного к конкретному элементу перечисления):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Color c = Color.valueOf("RED");
if ( c.isPrimaryColor() ) {
  System.out.println("This is a primary color");
}
 
public enum Color {
  RED() {
    @Override
    public boolean isPrimaryColor() {
     return true;
    }
  },
  BLUE() {
   @Override
   public boolean isPrimaryColor() {
      return true;
    }
  },
  YELLOW() {
   @Override
   public boolean isPrimaryColor() {
      return true;
    }
  };
  GREEN() {
   @Override
   public boolean isPrimaryColor() {
      return false;
    }
  };
  public abstract boolean isPrimaryColor();
}

Полученный код понятен и самодокументирован. Использование этого шаблона во многих случаях является отличной альтернативой более распространенной логике «если — еще, если», поскольку ее легче читать, расширять и поддерживать.

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

Справка: делай это коротко, но делай правильно! от нашего партнера по W4G Бернарда Линьи .

Статьи по Теме :