Заменить оператор гигантского переключения с чем?

17

У меня есть код, который анализирует некоторые файлы шаблонов, и когда он находит местозаполнитель, он заменяет его значением. Что-то вроде:

<html>
<head>
    <title>%title%</title>
</head>
<body bgcolor="%color%">
...etc.

В коде парсер находит их, вызывает эту функцию:

string getContent(const string& name)
{
    if (name == "title")
        return page->getTitle();
    else if (name == "color")
        return getBodyColor();
    ...etc.
}

, а затем заменит исходный заполнитель на возвращаемое значение.

В реальном случае это не фиктивная веб-страница, и есть много (50+) разных заполнителей, которые могут произойти.

Мой код - C ++, но я предполагаю, что эта проблема существует на любом языке. Думаю, речь идет о алгоритмах и дизайне OO. Важно только то, что это должно быть скомпилировано, даже если бы я хотел, чтобы у меня не было никакого динамического / eval'd кода.

Я, однако, о реализации шаблона Chain of Responsibility, но, похоже, он не улучшит ситуацию.

UPDATE: и я также обеспокоен тем, что этот комментарий в другой теме. Должен ли я заботиться об этом?

    
задан Milan Babuškov 18.03.2009 в 19:38
источник

6 ответов

25

Используйте словарь, который сопоставляет имена тегов с обработчиком тегов.

    
ответ дан anon 18.03.2009 в 19:41
источник
  • +1 Для уточнения в C ++ они называются std :: map. –  Eclipse 18.03.2009 в 19:43
  • Особенно хорошо, если ваш словарь может использовать поиск O (1), например, хеширование. –  Paul Tomblin 18.03.2009 в 19:43
  • Отличный ответ. Тонкий на бережливой стороне, но определенно хороший способ пойти. :) –  e.James 18.03.2009 в 19:44
  • @ejames Я никогда не понимал, почему длинные ответы считаются лучшими, чем короткие –  Mar 18 '09 at 18:46 18.03.2009 в 19:46
  • @Neil Butterworth Короткие ответы с полезными комментариями на самом деле являются просто длинными ответами, которые требуют большего нажатия ... :) –  Andy Mikula 18.03.2009 в 19:52
Показать остальные комментарии
4

Вы хотите заменить условный на полиморфизм . Грубо говоря:

string getContent(const string& name) {
    myType obj = factory.getObjForName(name);
    obj.doStuff();
}

, где doStuff перегружен.

    
ответ дан Steven Huwig 18.03.2009 в 19:43
источник
  • Конечно, переключатель просто перемещается где-то в другом месте (на заводе), где и должно быть. –  Ed S. 18.03.2009 в 19:45
  • Возможно, вы захотите объединить фабрику с картой Нила Баттерворта и загрузить логику создания экземпляра из некоторого файла конфигурации. Скомпилированный и динамичный - потрясающий. –  Steven Huwig 18.03.2009 в 19:47
  • Собственно, этот шаблон является конфигурационным файлом. Пользователи могут сами изменить его. –  Milan Babuškov 18.03.2009 в 19:59
  • Я имел в виду «настроить сопоставление имен поведением» (перечисленные выше как myType). Тогда не будет задействован выключатель. –  Steven Huwig 18.03.2009 в 20:07
  • Умммм ... это будет «obj.doStuff ()»? –  Dan 18.03.2009 в 21:50
3

Вы рассматривали XSLT? Это очень хорошо подходит для такого рода вещей. Я разработал систему управления контентом, которая сделала то же самое, и нашла XSLT очень эффективной. Парсер выполняет большую работу за вас.

UPDATE: комментарий Стивена поднимает важный момент: вы хотите, чтобы ваши шаблоны были действительными XHTML, если вы решите пойти по пути XSLT. Кроме того, я бы использовал другой разделитель для ваших токенов замены. Что-то менее вероятно происходит естественным образом. Я использовал #! PLACEHOLDER #! в моей CMS.

    
ответ дан Dave Swersky 18.03.2009 в 19:44
источник
  • Я думаю, что довольно оптимистично думать, что HTML-шаблоны будут действительными XML. :) –  Steven Huwig 18.03.2009 в 19:46
3

Я объединю 3 идеи:

  1. (от Steven Hugig): используйте фабричный метод, который дает вам другой класс для каждого селектора.
    • (от Нила Баттерворта): внутри фабрики используйте словарь, чтобы избавиться от большого switch(){} .
    • (мой): добавьте метод setup() для каждого класса обработчика, который добавляет себя (или новый экземпляр класса) в словарь.

пояснив немного:

  • создать абстрактный класс с static dict и методы для регистрации экземпляра с помощью селекторной строки.
  • в каждом подклассе метод setup() регистрируется с помощью суперкласса 'dict
  • заводский метод - это немного больше, чем словарь.
ответ дан Javier 18.03.2009 в 20:01
источник
  • Исследовательский парень .. не вы –  Warrior 18.03.2009 в 20:20
  • +1. Я предлагаю избавиться от отдельной функции setup () и переместить ее поведение в конструктор - таким образом его нельзя забыть. –  j_random_hacker 19.03.2009 в 05:31
2

Вместо синтаксического анализа попробуйте просто прочитать шаблон в строке и затем просто выполнить замену.

fileContents = fileContents.Replace("%title%", page->getTitle());
fileContents = fileContents.Replace("%color%", getBodyColor());
    
ответ дан Gordon Bell 18.03.2009 в 19:44
источник
  • производительность, но, вероятно, стоит того, чтобы простота кода, если абсолютная эффективность не нужна абсолютно. +1 –  Brian R. Bondy 18.03.2009 в 19:48
  • Если переменная «titleValue» содержит строку «% color%», она не будет работать должным образом. –  Milan Babuškov 18.03.2009 в 19:49
  • ya он менее безопасен, но еще проще: –  Brian R. Bondy 18.03.2009 в 19:53
  • Да, возможно, лучше сделать жетоны немного сложнее, например «[% color%]». –  Gordon Bell 18.03.2009 в 20:05
  • Мне все еще не нравится идея, потому что она должна искать по всей строке для каждого из 50 + шаблонов-замещающих и все еще подвержена ошибкам. Обратите внимание, что пользователи моего приложения - это те, которые предоставляют как данные, так и шаблоны. –  Milan Babuškov 18.03.2009 в 20:07
2

Как «Дядя» Боб Мартин упомянул в предыдущем подкасте с Джоэлом и Джеффом , почти все, что вы пришли по существу, будет воспроизводить большую инструкцию switch.

Если вам лучше реализовать одно из решений, выбранных выше, все в порядке. Это может сделать ваш код красивее, но под обложками он существенно эквивалентен.

Важно то, что есть только один экземпляр вашего большого оператора switch. Ваш оператор switch или словарь должны определить, какой класс обрабатывает этот тег, а затем последующие определения должны обрабатываться с использованием полиморфизма.

    
ответ дан JohnMcG 18.03.2009 в 20:31
источник
  • Это неверно (и характерно для дерьма, с которым Мартин выходит). Чтобы добавить к коммутатору, мне нужно изменить код коммутатора - я могу добавить в словарь без изменения существующего кода. –  Mar 18 '09 at 19:38 18.03.2009 в 20:38
  • Добавление в словарь по-прежнему является сменой кода, а у одного из компиляторов меньше шансов на выбор проблем. Это не значит, что словарь заполняется данными, он будет заполнен кодом. Добавление случаев в оператор switch не должно затрагивать какие-либо существующие случаи ...? –  Bittercoder 18.03.2009 в 22:14
  • Теоретически, да, dictrionary может быть заполнен из файла конфигурации или таблицы базы данных, поэтому его можно было бы изменить без повторной компиляции. На практике, если вы меняете сопоставления, это, вероятно, потому, что у вас есть новый обработчик, поэтому вы уже делаете повторную компиляцию. –  JohnMcG 18.03.2009 в 22:38
  • @john на практике да тоже - вы можете использовать архитектуру плагина, реализованную с помощью DLL (или аналогичных), которая вообще не требует доступа к существующей кодовой базе. Никакой перекомпиляции не требуется (или возможно). –  Mar 18 '09 at 21:44 18.03.2009 в 22:44
  • @Neil Butterworth: В глубоком философском смысле вы действительно перемещаете оператор switch. Этот ход может изменить форму, расширяемость и другие грани, но суть происходящего неизменна. Тем не менее, многие из замен гораздо более удобны / расширяемы, чем оригинал. –  Harper Shelby 19.03.2009 в 00:03
Показать остальные комментарии