Полиморфизм — одна из грандиозных идей, лежащих в фундаменте ООП. Это слово, заимствованное из греческого языка, означает множество (poly) форм (morph). В контексте программирования полиморфизм означает многообразие форм некоторого метода или класса объектов. Но полиморфизм — это не просто альтернативные реализации. Уместное применение полиморфизма создает миниатюрные локализованные контексты исполнения и позволяет обойтись без громоздких блоков if-then-else. Находясь в контексте, мы можем напрямую выполнять нужные действия, тогда как, находясь вне этого контекста, мы вынуждены сначала воссоздать его и лишь затем выполнять нужные действия. Аккуратное использование альтернативных реализаций позволяет выделить контекст, а значит, решить ту же задачу через меньший объем более удобочитаемого кода. Лучше всего продемонстрировать это на примере. Возьмем следующий код для (нереально) простой корзины покупок:
public class ShoppingCart {
private ArrayList- cart = new ArrayList
- ();
public void add(Item item) { cart.add(item); }
public Item takeNext() { return cart.remove(0); }
public boolean isEmpty() { return cart.isEmpty(); }
}
Допустим, некоторые товары в нашем интернет-магазине можно скачать из сети Интернет, а другие требуют доставки. Создадим другой класс, который поддерживает эти операции:
public class Shipping {
public boolean ship(Item item, SurfaceAddress address) {… }
public boolean ship(Item item, EMailAddress address) {… }
}
Когда клиент рассчитался, нужно доставить покупки:
while (!cart.isEmpty()) {
shipping.ship(cart.takeNext(), ???);
}
Параметр ??? — это не какой-то очередной оператор Элвиса;[21] это неразрешенный вопрос о том, как нужно доставить товар — электронной или обычной почтой. Контекста для ответа на этот вопрос уже нет. Можно было сохранить метод доставки в виде boolean или enum, а затем использовать if-then-else, чтобы заполнить значение недостающего параметра. А другое решение — создать два класса, расширяющих Item. Назовем их DownloadableItem и SurfaceItem. Теперь напишем немного кода. Я сделаю из Item интерфейс, который поддерживает единственный метод ship (доставить). Чтобы доставить содержимое корзины, вызываем item. ship(shipper). Оба класса, DownloadableItem и SurfaceItem, реализуют ship:
public class DownloadableItem implements Item {
public boolean ship(Shipping shipper, Customer customer) {
shipper.ship(this, customer.getEmailAddress());
}
}
public class SurfaceItem implements Item {
public boolean ship(Shipping shipper, Customer customer)
{
shipper.ship(this, customer.getSurfaceAddress());
}
}
В этом примере мы делегировали ответственность за Shipping (доставку) каждому Item (товару). Поскольку каждый товар знает, как его следует доставлять, такая организация позволяет справиться с доставкой, не прибегая к if-then-else. Этот код также демонстрирует применение двух шаблонов проектирования, которые часто хорошо сочетаются между собой: Command и Double Dispatch. Эффективное применение этих шаблонов основано на правильном использовании полиморфизма. При выполнении этих условий число блоков if-then-else сокращается.
В некоторых ситуациях гораздо практичнее использовать не полиморфизм, а if-then-else, однако при написании кода в стиле полиморфизма он имеет меньший объем, более удобочитаем и менее хрупок. Количество упущенных возможностей несложно подсчитать — оно совпадает с числом операторов if-then-else в коде.
Невероятно, но факт: тестировщики — ваши друзья
Берк Хафнагель
Сами себя они могут называть контролем качества или обеспечением качества, но многие программисты зовут их просто напастью. Мой опыт показывает, что у программистов часто враждебные отношения с теми, кто тестирует их программы. «Они слишком придирчивы» или «Они хотят, чтобы все было идеально» — вот обычные жалобы. Знакомо, да?