Читаем Дефрагментация мозга. Софтостроение изнутри полностью

Линейный способ

void DocStats::UpdateStats(DocElement& elem)

{

if (Paragraph* p = dynamic_cast(&elem))

{

chars_ += p->NumChars;

words_ += p->NumWords;

}

else if (dynamic_cast(&elem))

{

++images_;

}

else…

добавляем по одному оператору if для каждого типа инспектируемого объекта

}

Автором совершенно справедливо отмечается существенный недостаток этого фрагмента: преобразование типов делает его сложным для сопровождения, кроме того, нет никакой гарантии, что проверка для базового класса не выполнится раньше, чем для производного. Достаточно ошибиться в порядке следования операторов if, и на ветку производных классов программа никогда не попадёт.

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

После рассуждений о недостатках линейного кода выносится решение о необходимости виртуализации вызовов путём построения иерархии, аналогичной существующей иерархии классов, и добавления новых методов в неё. То есть реализации шаблона Visitor, описание которого на добрых двух десятках (!) страниц вы можете посмотреть в книжке. Если очень захотите.

Теперь представьте, что вы используете другой язык с развитым механизмом интроспекции типов времени выполнения. Например, отражение ( reflection ) для. NET. В этом случае «лобовое» решение может выглядеть примерно так:

Использование отражения

class DocStats

{

void UpdateStats(DocElement elem)

{

Type elemType = typeof(DocElement);

BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic |

BindingFlags.Instance;

if (elemType.GetMethod("NumChars")!= null)

chars += (int) elemType.InvokeMember("NumChars", flags | BindingFlags.

InvokeMethod,

null, elem, null);

if (elemType.GetMethod("NumWords")!= null)

words += (int) elemType.InvokeMember("NumWords", flags | BindingFlags.

InvokeMethod, null, elem, null);

if (elemType.GetProperty("Image")!= null)

images++;

обследуем все интересующие нас свойства классов

}

}

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже