Mam hierarchię klas reprezentujących kontrolki GUI. Coś takiego:
Control->ContainerControl->Form
Muszę zaimplementować serię algorytmów, które działają z obiektami wykonującymi różne czynności i myślę, że wzorzec dla gości byłby najczystszym rozwiązaniem. Weźmy na przykład algorytm, który tworzy reprezentację Xml hierarchii obiektów. Stosując podejście „klasyczne” zrobiłbym to:
public abstract class Control
{
public virtual XmlElement ToXML(XmlDocument document)
{
XmlElement xml = document.CreateElement(this.GetType().Name);
// Create element, fill it with attributes declared with control
return xml;
}
}
public abstract class ContainerControl : Control
{
public override XmlElement ToXML(XmlDocument document)
{
XmlElement xml = base.ToXML(document);
// Use forech to fill XmlElement with child XmlElements
return xml;
}
}
public class Form : ContainerControl
{
public override XmlElement ToXML(XmlDocument document)
{
XmlElement xml = base.ToXML(document);
// Fill remaining elements declared in Form class
return xml;
}
}
Ale nie jestem pewien, jak to zrobić ze wzorcem odwiedzin. To jest podstawowa implementacja:
public class ToXmlVisitor : IVisitor
{
public void Visit(Form form)
{
}
}
Ponieważ nawet klasy abstrakcyjne pomagają w implementacji, nie jestem pewien, jak zrobić to poprawnie w ToXmlVisitor?
Powodem, dla którego rozważam wzorzec użytkownika, jest to, że niektóre algorytmy będą wymagały odwołań niedostępnych w projekcie, w którym zaimplementowane są klasy, i istnieje wiele różnych algorytmów, więc unikam dużych klas.
Odpowiedzi:
Wzorzec gościa jest mechanizmem symulującym podwójne wiązanie w językach programowania, które obsługują tylko pojedyncze wiązanie. Niestety, to stwierdzenie może nie wyjaśniać wielu rzeczy, dlatego wyjaśnię to prostym przykładem.
W .NET i C #, używanej platformie, obiekty można konwertować na ciągi znaków za pomocą
ToString()
funkcji. To, co robi ta funkcja, tj. Wykonywany kod, zależy od typu obiektu, do którego się go stosuje (jest to metoda wirtualna). To, jaki kod zostanie wykonany, zależy od jednej rzeczy, jednego typu obiektu, dlatego zastosowany mechanizm nazywa się pojedynczym wiązaniem.Ale co jeśli chcę mieć więcej niż jeden sposób przekonwertowania obiektu na ciąg, dla każdego innego rodzaju obiektu? Co jeśli chciałbym mieć dwa sposoby konwertowania obiektów na ciągi, tak że wykonywany kod zależy od dwóch rzeczy: nie tylko obiektu do konwersji, ale także sposobu, w jaki chcemy go przekonwertować?
Można to ładnie rozwiązać, gdybyśmy mieli podwójne wiązanie. Ale większość języków OO, w tym C #, obsługuje tylko pojedyncze wiązanie.
Wzór odwiedzającego rozwiązuje problem, zamieniając podwójne wiązanie w dwa kolejne pojedyncze wiązania.
W powyższym przykładzie użyłby do konwersji metody wirtualnej w obiekcie, która wywołuje drugą metodę wirtualną w obiekcie implementującym algorytm konwersji.
Ale to oznacza, że obiekt, na którym chcesz zastosować algorytm, musi z tym współpracować: musi mieć wsparcie dla wypalonego wzorca odwiedzającego.
Wygląda na to, że używasz klas Windows Forms .NET, które nie obsługują wzorca odwiedzającego. Mówiąc dokładniej, musieliby mieć
public virtual void Accept(IVisitor)
metodę, której oczywiście nie mają.Więc jaka jest alternatywa? Cóż. .NET obsługuje nie tylko pojedyncze wiązanie, ale także dynamiczne wiązanie, które jest jeszcze bardziej wydajne niż podwójne wiązanie.
Aby uzyskać więcej informacji na temat zastosowania tej techniki, która pozwoli ci rozwiązać problem (jeśli dobrze go rozumiem), spójrz na Farewell Visitor .
AKTUALIZACJA:
Aby zastosować technikę do konkretnego problemu, najpierw zdefiniuj metodę rozszerzenia:
Utwórz dynamicznego dyspozytora:
Następnie wprowadź określone metody:
źródło