2009-03-18 9 views
17

Mam kod, który analizuje niektóre pliki szablonów, a gdy znajdzie symbol zastępczy, zastępuje go wartością. Coś jak:Zastąp symbol gigantycznego przełącznika czym?

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

w kodzie, parser znajdzie tych, nazywa tę funkcję:

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

a następnie zastępuje oryginalny zastępczy z zwracanej wartości.

W rzeczywistości nie jest to fikcyjna strona internetowa i istnieje wiele (50+) różnych symboli zastępczych, które mogą wystąpić.

Mój kod to C++, ale myślę, że ten problem istnieje w dowolnym języku. Chodzi raczej o algorytmy i projektowanie OO. Ważną rzeczą jest to, że trzeba to skompilować, nawet gdybym chciał, nie mogłem mieć żadnego kodu dynamicznego/eval'd.

Chodzi mi o wdrożenie schematu Łańcuch Odpowiedzialności, ale wygląda na to, że nie poprawiłoby to sytuacji.

AKTUALIZACJA: i martwię się również o this comment w innym wątku. Czy powinienem się tym przejmować?

Odpowiedz

25

Użyj słownika, który odwzorowuje nazwy znaczników na obsługę znaczników.

+0

+1 Dla wyjaśnienia, w C++ są to nazywane std :: map. – Eclipse

+1

Szczególnie dobry, jeśli słownik może korzystać z wyszukiwania O (1), takiego jak mieszanie. –

+0

Świetna odpowiedź. Troszkę po chudej stronie, ale zdecydowanie dobra droga. :) –

4

Chcesz replace conditional with polymorphism. Z grubsza:

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

gdzie doStuff jest przeciążony.

+2

Oczywiście przełącznik jest przenoszony w inne miejsce (fabryka), czyli tam, gdzie powinien być. –

+0

Prawdopodobnie chciałbyś połączyć fabrykę z mapą Neila Butterwortha i załadować logikę instancji z jakiegoś pliku konfiguracyjnego. Skompilowany * i * dynamiczny - niesamowity. –

+0

W rzeczywistości ten szablon jest plikiem konfiguracyjnym. Użytkownicy mogą to zmienić samodzielnie. –

3

Czy brałeś pod uwagę XSLT? Bardzo dobrze nadaje się do tego rodzaju rzeczy. Opracowałem system zarządzania treścią, który zrobił dokładnie to samo i stwierdził, że XSLT jest bardzo skuteczny. Parser wykonuje dla ciebie dużo pracy.

AKTUALIZACJA: Komentarz Stevena podnosi ważną kwestię - będziesz chciał, aby szablony były poprawne XHTML, jeśli zdecydujesz się na trasę XSLT. Również użyłbym innego ogranicznika dla tokenów zastępczych. Coś mniej prawdopodobnego występuje naturalnie. Użyłem #! PLACEHOLDER #! w moim CMS.

+0

Sądzę, że optymizmem jest sądzić, że szablony HTML będą miały prawidłowy format XML. :) –

2

Zamiast analizować, próbowałem po prostu przeczytać szablon w ciągu znaków, a następnie po prostu zastępuje.

fileContents = fileContents.Replace("%title%", page->getTitle()); 
fileContents = fileContents.Replace("%color%", getBodyColor()); 
+0

hit wydajności, ale prawdopodobnie warto za prostotę kodu, jeśli absolutna wydajność nie jest absolutnie potrzebna. +1 –

+1

Jeśli zmienna "titleValue" zawiera ciąg "% color%", nie będzie działać poprawnie. –

+0

ya jest także mniej bezpieczna, ale jeszcze prostsza :) –

3

będę połączyć 3 pomysły:

  1. (Steven Hugig): użyć metody fabryki, które dostaje inną klasę dla każdego selektora.
    • (od Neila Butterwortha): w fabryce użyj słownika, aby pozbyć się dużego switch(){}.
    • (moja): dodaj metodę setup() do każdej klasy obsługi, która dodaje siebie (lub nową instancję klasy) do słownika.

wyjaśniając trochę:

  • uczynić klasę abstrakcyjną, która ma static dict i metody rejestrowania instancji sznurkiem selektora.
  • na każdej podklasy metoda setup() rejestruje się z dict nadklasy
  • sposób fabryka jest niewiele więcej niż słownik czytać
+0

Badany facet ... nie będziesz – Warrior

+0

+1. Sugeruję pozbycie się oddzielnej funkcji setup() i przeniesienie jej zachowania do konstruktora - w ten sposób nie można zapomnieć. –

2

As "Uncle" Bob Martin mentioned in a previous podacast with Joel and Jeff, prawie nic wymyślić będzie zasadniczo być odtwarzanie dużej instrukcji przełącznika.

Jeśli czujesz się lepiej, stosując jedno z powyższych rozwiązań, to dobrze. Może sprawić, że twój kod będzie ładniejszy, ale pod okładkami jest zasadniczo równoważny.

Ważne jest, aby upewnić się, że istnieje tylko jedno wystąpienie Twojego dużego oświadczenia dotyczącego przełącznika. Twoja instrukcja switch lub słownik powinien określać, która klasa obsługuje ten znacznik, a następnie kolejne oznaczenia powinny być obsługiwane przy użyciu polimorfizmu.

+0

To nie jest prawda (i jest typowa dla tych bzdur, z którymi Martin wychodzi). Aby dodać do przełącznika, muszę zmodyfikować kod przełącznika - mogę dodać do słownika bez modyfikowania istniejącego kodu. –

+0

Dodanie do słownika wciąż jest zmianą kodu, a kompilator ma mniejsze szanse na wychwycenie problemów z .. to nie jest tak, że słownik jest zapełniany danymi, zostanie zapełniony przez kod. Dodawanie przypadków do instrukcji switcha nie musi wpływać na istniejące przypadki ...? – Bittercoder

+0

Teoretycznie tak, dictrionary może zostać wypełniony z pliku konfiguracyjnego lub tabeli bazy danych, aby mógł zostać zmodyfikowany bez ponownego kompilowania. W praktyce, jeśli zmieniasz mapowania, prawdopodobnie jest to spowodowane tym, że masz nowy program obsługi, więc już teraz wykonujesz ponowną kompilację. – JohnMcG