Kontekst
Używałem z hierarchią obiektów (drzewa wyrażeń) wzorca „pseudo” użytkownika (pseudo, ponieważ w nim nie stosuje podwójnej wysyłki):
public interface MyInterface
{
void Accept(SomeClass operationClass);
}
public class MyImpl : MyInterface
{
public void Accept(SomeClass operationClass)
{
operationClass.DoSomething();
operationClass.DoSomethingElse();
// ... and so on ...
}
}
Ten projekt był jednak wątpliwy, całkiem wygodny, ponieważ liczba implementacji MyInterface jest znacząca (~ 50 lub więcej) i nie musiałem dodawać dodatkowych operacji.
Każda implementacja jest unikalna (jest to inne wyrażenie lub operator), a niektóre są kompozytami (tj. Węzłami operatora, które będą zawierać inne węzły operatora / liści).
Przechodzenie jest obecnie wykonywane przez wywołanie operacji Accept w węźle głównym drzewa, który z kolei wywołuje Accept na każdym z jego węzłów potomnych, który z kolei ... i tak dalej ...
Ale nadszedł czas, aby dodać nową operację , na przykład ładny druk:
public class MyImpl : MyInterface
{
// Property does not come from MyInterface
public string SomeProperty { get; set; }
public void Accept(SomeClass operationClass)
{
operationClass.DoSomething();
operationClass.DoSomethingElse();
// ... and so on ...
}
public void Accept(SomePrettyPrinter printer)
{
printer.PrettyPrint(this.SomeProperty);
}
}
Zasadniczo widzę dwie opcje:
- Zachowaj ten sam projekt, dodając nową metodę mojej operacji do każdej klasy pochodnej, kosztem konserwacji (nie opcja, IMHO)
- Użyj „prawdziwego” wzorca gościa, kosztem rozszerzenia (nie jest to opcja, ponieważ spodziewam się, że po drodze pojawi się więcej implementacji ...), z około 50+ przeciążeniami metody Visit, z których każda odpowiada konkretnej implementacji ?
Pytanie
Czy poleciłbyś użycie wzorca gościa? Czy jest jakiś inny wzorzec, który mógłby pomóc rozwiązać ten problem?
MyInterface
… czy wszystkie te klasy mają unikalną implementacjęDoSomething
iDoSomethingElse
? Nie wiem, gdzie faktycznie twoja klasa gości przemierza hierarchię -facade
w tej chwili wygląda to bardziej jakOdpowiedzi:
Używam wzorca odwiedzających do reprezentowania drzew wyrażeń przez ponad 10 lat w sześciu dużych projektach w trzech językach programowania i jestem bardzo zadowolony z rezultatu. Znalazłem kilka rzeczy, które znacznie ułatwiły stosowanie wzoru:
Nie używaj przeciążeń w interfejsie gościa
Wpisz typ do nazwy metody, tzn. Użyj
zamiast
Dodaj metodę „złap nieznaną” w interfejsie użytkownika.
Umożliwiłoby to użytkownikom, którzy nie mogą modyfikować kodu:
Pozwoliłoby im to na zbudowanie własnych implementacji
IExpression
iIVisitor
„zrozumienie” ich wyrażeń poprzez wykorzystanie informacji o typie wykonawczym przy implementacji metody catch-allVisitExpression
.Zapewnij domyślną implementację
IVisitor
interfejsu „ nic nie rób”Pozwoliłoby to użytkownikom, którzy muszą poradzić sobie z podzbiorem typów wyrażeń, szybciej budować użytkowników i uczynić ich kod odpornym na dodawanie kolejnych metod
IVisitor
. Na przykład napisanie użytkownika, który zbierze wszystkie nazwy zmiennych z wyrażeń, stanie się łatwym zadaniem, a kod się nie złamie, nawet jeśli dodasz kilka nowych typów wyrażeń do swojegoIVisitor
późniejszego.źródło
Do not use overloads in the interface of the visitor
?