Упущенные возможности применения полиморфизма Кирк Пеппердин

Полиморфизм — одна из грандиозных идей, лежащих в фундаменте ООП. Это слово, заимствованное из греческого языка, означает множество (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 в коде.

Загрузка...