May 4th, 2010

59. Упущеные возможности полиморфизма

(В оригинале - Missing Opportunities for Polymorphism)

Полиморфизм – одна из фундаментальных идей объектно-ориентированного подхода. Перевод этого слова с греческого – «множество форм». В контексте программирования речь идет о множестве форм конкретного класса, объекта или метода. Но полиморфизм – это не только альтернативные реализации. Будучи правильно использованным, полиморфизм позволяет обходиться без конструкций 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(), ???);
}

«???» здесь вовсе не какой-то новый супероператор, а всего лишь вопрос, ответ на который указывает, надо ли послать этот товар по электронной почте или же по обычной. Однако контекст, необходимый для ответа на этот вопрос, уже не существует. Мы могли бы использовать флаг или enum для этого и применить конструкцию if-then-else. Другое же решение – создать два класса, расширяющих функциональность класса item. Назовем их для примера DownloadableItem и SurfaceItem. Теперь напишем код. Сделаем Item интерфейсом с единственным методом ship. Чтобы выполнить доставку, нужно будет вызвать item.ship(). Реализация метода ship будет в классах DownloadableItem и SurfaceItem.

public class DownloadableItem implements Item
{
  public boolean ship(Shipping shipper)
  {
   shipper.ship(this, customer.getEmailAddress());
  }
}

public class SurfaceItem implements Item
{
  public boolean ship(Shipping shipper)
  {
   shipper.ship(this, customer.getSurfaceAddress());
  }
}

В этом примере мы делегировали ответственность за правильную доставку объекту Item. Поскольку каждый объект Item знает как его нужно доставить, это позволяет нам обойтись без конструкции if-then-else. Данный код также демонстрирует использование двух паттернов проектирования, часто работающих в паре: Command и Double Dispatch. Эффективное использование этих паттернов основано на правильном использовании полиморфизма. Такое использование приведет к уменьшению количества конструкций if-then-else в коде.

И хотя бывают случаи, когда использование if-then-else оправдано более, чем применение полиморфизма, чаще всего полиморфизм дает в результате более компактный и более ясный для понимания код. Количество упущенных возможностей – это просто количество конструкций if-then-else в вашем коде.

Автор оригинала - Kirk Pepperdine

Перевод мой, при использовании ссылка на мой живой журнал обязательна!

И снова о винде...

На этот раз порадовала рабочая ХР.

Вдруг оказалось, что сборка проекта перестала работать. Копание вглубь привело вот к чему. В процессе сборки задействованы несколько .vbs скриптов, и вот при их запуске и выдавалась вот такая вот ошибка:

Error: ActiveX component can't create object
Code: 800A01AD
Source: Microsoft VBScript Runtime Error

Совсем недавно все работало, скрипты не менялись, да и на других компах продолжают успешно запускаться. Строка с ошибкой указывает на

set objStdOut = Wscript.StdOut

Из микрософтовского же описания следует, что объект Wscript существует всегда, однако вышеприведенная ошибка показывает, что таки нет.

Гугление ни к чему не привело. Одна из немногих адекватно выглядящих статей на эту тему вела вот сюда: http://support.microsoft.com/kb/311269 и естественно, предлагаемое там решение нифига не помогало.

Ладно, копаем дальше. Находим отдельный пакет этого самого Windows Script Host, пытаемся его установить - винда говорит "а хер вам, он уже стоит и точка".

Заодно замечаю, что подозрительно давно не устанавливается ни одного апдейта (последний был в сентябре). При попытке проверить апдейты напрямую через сайт тоже нифига не работает с очень информативным сообщением "update failed"...

Ладно, смотрим, чего там было последним поставлено - SP3. Кстати, как раз в нем вроде и этот самый Windows Script Host. Его вроде можно удалить. Чего и проделываем. Удаляем SP3, перегружаемся, ставим заново... ага, хер. Посреди установки выскакивает еще одно офигительно информативное сообщение "Access denied" и инсталляция молча закрывается. Отлично блин... Микрософты во всей своей красе - нефиг сообщать всяким быдлопользователям какие-либо подробности ошибок.

К счастью, гугл знал про access denied при инсталле сервиспака :) Делаем то, что там советуется (а советуется там сбросить ограничения доступа ко ВСЕМУ реестру, мол некоторые программы могут их так модифицировать, что у админа к ним не будет доступа! Поэтому мол инсталл и не проходит.) Блиять... Вот как это назвать? У админа нет доступа, потому что какая-то программа этот доступ ограничила. Что самое смешное, другая программа благополучно этот доступ возвращает. Так блин, сделайте это АВТОМАТОМ в сервиспаке... Но нет, легкие пути - это не для микрософта... Также классно сбрасывать ограничения для ВСЕГО реестра - сервиспак конечно же никому и никуда не сообщает (даже в лог пишет только о факте ошибки), куда именно он не смог получить доступ.

После этой манипуляции сервис пак благополучно стал на место, только первоначальную проблему это нифига не решило.

Копаем дальше. Апдейты. Может если их поставить, это поможет?

Напрямую через сайт апдейта (автоапдейт все также ничего не видит) стал предлагаться апдейт для самого виндоуз апдейта. Ставим. После этого единственный требуемый апдейт - проверка подлинности винды, без него ничего другого недоступно. И конечно же, при попытке его скачать - сообщение о том, что failed. Как всегда у микрософта, очень информативно.

Что бы мы делали без гугла? А он показывает, где можно проверку подлинности проделать онлайн, после чего станут доступны остальные апдейты. Делаем. Перегружаемся. Действительно, через сайт становится доступно 50 апдейтов. Но при попытке скачать - все тот же failed, но на этот раз уже с 8-значным кодом ошибки. И в автоапдейтере тоже эти 50 апдейтов видны, и тоже после начала скачивания он тупо молча закрывается, не сообщая вообще ничего (кто б сомневался)...

Вводим этот 8-значный код в гугл (да-да :)), находим ссылку на еще одну статью в суппорте микрософта о том, как вручную запустить службу BITS. Находим, (действительно остановлена), пытаемся запустить, получаем сообщение "путь не найден". Присматриваемся, замечаем, что путь действительно неверный - %fystemRoot%\... Да уж, похоже вирус постарался (NOD32 business edition свежеобновленный тогда его благополучно профукал, а бесплатная убивалка похоже не восстановила все до конца). Исправляем на как надо, перегружаемся - апдейты загружаются!

Но блядь после перезагрузки первоначальная проблема ВСЕ РАВНО ОСТАЛАСЬ НЕРЕШЕННОЙ!

Посмотрим, что на это ответит техподдержка микрософта. Хотя - зная о том, КАК в корпорациях устроена техподдержка, я не верю, что они что-то реальное смогут решить. Посмотрим, о результатах отпишусь...