# "Подмоченный" код сложнее оптимизировать

(В оригинале - WET Dilutes Performance Bottlenecks)

Важность принципа DRY (Не повторяйся, Don't Repeat Yourself) в том, что он реализует идею, согласно которой каждый фрагмент знаний представлен лишь единожды. Другими словами, каждое знание должно быть реализовано один и только один раз. Противоположность ему – принцип WET (Реализуй каждый раз, Write Every Time). (В английском здесь игра слов – аббревиатуры WET и DRY имеют еще и смысловое значение «мокрый» и «сухой»). WET код – код, в котором знание закодировано в нескольких реализациях. Влияние DRY и WET на производительность станет ясным после рассмотрения их влияния на профилирование.

Давайте предположим, что одна из функций нашей системы, скажем, *X*, является «узким местом» по производительности. Пусть, например, она расходует 30% процессорного времени. И теперь предположим, что она реализована в коде в десяти различных местах. В среднем, каждая реализация будет тратить около 3% процессорного времени. И вот тут мы можем просто не заметить, что данная функциональность является узким местом. Однако если мы даже каким-то образом и распознали узкое место, нам потребуется найти и исправить все десять реализаций. Будь наш код написан по принципу DRY, мы бы, во-первых, ясно увидели, что на функциональность расходуется 30% процессорного времени, а во-вторых, исправлять бы нам пришлось тоже лишь единственное место в коде. И да, нам бы не надо было тратить время на то, чтобы сначала найти все десять мест, как это было бы нужно в WET коде.

Есть один общий случай, когда часто нарушается принцип DRY – это использование множеств. Общей техникой реализации запроса является проход по всему множеству и применение запроса к каждому из элементов:

```
public class UsageExample {
    private ArrayList<Customer> allCustomers = new ArrayList<Customer>();
    // ...
    public ArrayList<Customer> findCustomersThatSpendAtLeast(Money amount) {
        ArrayList<Customer> customersOfInterest = new ArrayList<Customer>();
        for (Customer customer: allCustomers) {
            if (customer.spendsAtLeast(amount))
               customersOfInterest.add(customer);
        }
        return customersOfInterest;
    }
}
```

Открывая это множество клиентам, мы нарушаем инкапсуляцию. И это не только ограничивает нас в возможностях рефакторинга, а также вынуждает пользователей нашего кода нарушить DRY, поскольку наиболее вероятно, что каждому из них придется еще раз реализовать практически аналогичный запрос. Ситуации легко избежать, убрав «сырое» множество из API. В данном случае мы предоставим новый, специфичный для предметной области тип `CustomerList`. Данный класс послужит «домом» для всех наших запросов.

Этот новый тип легко позволит нам отслеживать факт, являются ли запросы «узким местом» или нет. Включив запросы в класс, мы избавимся от необходимости открывать представления вроде `ArrayList` клиентам. Это дает нам свободу изменять реализации без страха нарушить контракт с клиентом:

```
public class CustomerList {
    private ArrayList<Customer> customers = new ArrayList<Customer>();
    private SortedList<Customer> customersSortedBySpendingLevel = new SortedList<Customer>();
    // ...
    public CustomerList findCustomersThatSpendAtLeast(Money amount) {
        return new CustomerList(customersSortedBySpendingLevel.elementsLargerThan(amount));
    }
}

public class UsageExample {
    public static void main(String[] args) {
        CustomerList customers = new CustomerList();
        // ...
        CustomerList customersOfInterest = customers.findCustomersThatSpendAtLeast(someMinimalAmount);
        // ...
    }
}
```

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

Автор оригинала - [Kirk Pepperdine](http://programmer.97things.oreilly.com/wiki/index.php/Kirk_Pepperdine)
