// The element interface declares an `accept` method that takes // the base visitor interface as an argument. interfaceShape is method move(x, y) method draw() method accept(v: Visitor)
// Each concrete element class must implement the `accept` // method in such a way that it calls the visitor's method that // corresponds to the element's class. classDotimplementsShape is // ...
// Note that we're calling `visitDot`, which matches the // current class name. This way we let the visitor know the // class of the element it works with. method accept(v: Visitor) is v.visitDot(this)
classCircleimplementsShape is // ... method accept(v: Visitor) is v.visitCircle(this)
classRectangleimplementsShape is // ... method accept(v: Visitor) is v.visitRectangle(this)
classCompoundShapeimplementsShape is // ... method accept(v: Visitor) is v.visitCompoundShape(this)
// The Visitor interface declares a set of visiting methods that // correspond to element classes. The signature of a visiting // method lets the visitor identify the exact class of the // element that it's dealing with. interfaceVisitor is method visitDot(d: Dot) method visitCircle(c: Circle) method visitRectangle(r: Rectangle) method visitCompoundShape(cs: CompoundShape)
// Concrete visitors implement several versions of the same // algorithm, which can work with all concrete element classes. // // You can experience the biggest benefit of the Visitor pattern // when using it with a complex object structure such as a // Composite tree. In this case, it might be helpful to store // some intermediate state of the algorithm while executing the // visitor's methods over various objects of the structure. classXMLExportVisitorimplementsVisitor is method visitDot(d: Dot) is // Export the dot's ID and center coordinates.
method visitCircle(c: Circle) is // Export the circle's ID, center coordinates and // radius.
method visitRectangle(r: Rectangle) is // Export the rectangle's ID, left-top coordinates, // width and height.
method visitCompoundShape(cs: CompoundShape) is // Export the shape's ID as well as the list of its // children's IDs.
// The client code can run visitor operations over any set of // elements without figuring out their concrete classes. The // accept operation directs a call to the appropriate operation // in the visitor object. classApplication is field allShapes: array of Shapes
classConcreteComponentA(Component): """ Each Concrete Component must implement the `accept` method in such a way that it calls the visitor's method corresponding to the component's class. """
defaccept(self, visitor: Visitor) -> None: """ Note that we're calling `visitConcreteComponentA`, which matches the current class name. This way we let the visitor know the class of the component it works with. """
visitor.visit_concrete_component_a(self)
defexclusive_method_of_concrete_component_a(self) -> str: """ Concrete Components may have special methods that don't exist in their base class or interface. The Visitor is still able to use these methods since it's aware of the component's concrete class. """
return"A"
classConcreteComponentB(Component): """ Same here: visitConcreteComponentB => ConcreteComponentB """
classVisitor(ABC): """ The Visitor Interface declares a set of visiting methods that correspond to component classes. The signature of a visiting method allows the visitor to identify the exact class of the component that it's dealing with. """
""" Concrete Visitors implement several versions of the same algorithm, which can work with all concrete component classes. You can experience the biggest benefit of the Visitor pattern when using it with a complex object structure, such as a Composite tree. In this case, it might be helpful to store some intermediate state of the algorithm while executing visitor's methods over various objects of the structure. """
defclient_code(components: List[Component], visitor: Visitor) -> None: """ The client code can run visitor operations over any set of elements without figuring out their concrete classes. The accept operation directs a call to the appropriate operation in the visitor object. """
# ... for component in components: component.accept(visitor) # ...
if __name__ == "__main__": components = [ConcreteComponentA(), ConcreteComponentB()]
print("The client code works with all visitors via the base Visitor interface:") visitor1 = ConcreteVisitor1() client_code(components, visitor1)
print("It allows the same client code to work with different types of visitors:") visitor2 = ConcreteVisitor2() client_code(components, visitor2)
输出.txt:执行结果
1 2 3 4 5 6
The client code works with all visitors via the base Visitor interface: A + ConcreteVisitor1 B + ConcreteVisitor1 It allows the same client code to work with different types of visitors: A + ConcreteVisitor2 B + ConcreteVisitor2
/// Visitor can visit one type, do conversions, and output another type. /// /// It's not like all visitors must return a new type, it's just an example /// that demonstrates the technique. pubtraitVisitor { typeValue;
/// Visits a vector of integers and outputs a desired type. fnvisit_vec(&self, v: Vec<i32>) ->Self::Value; }
/// Visitor implementation for a struct of two values. implVisitorforTwoValuesStruct { typeValue = TwoValuesStruct;
/// A struct of two integer values. /// /// It's going to be an output of `Visitor` trait which is defined for the type /// in `visitor.rs`. #[derive(Default, Debug)] pubstructTwoValuesStruct { a: i32, b: i32, }
/// A struct of values array. /// /// It's going to be an output of `Visitor` trait which is defined for the type /// in `visitor.rs`. #[derive(Default, Debug)] pubstructTwoValuesArray { ab: [i32; 2], }
/// `Deserializer` trait defines methods that can parse either a string or /// a vector, it accepts a visitor which knows how to construct a new object /// of a desired type (in our case, `TwoValuesArray` and `TwoValuesStruct`). traitDeserializer<V: Visitor> { fncreate(visitor: V) ->Self; fnparse_str(&self, input: &str) ->Result<V::Value, &'staticstr> { Err("parse_str is unimplemented") } fnparse_vec(&self, input: Vec<i32>) ->Result<V::Value, &'staticstr> { Err("parse_vec is unimplemented") } }
fnparse_str(&self, input: &str) ->Result<V::Value, &'staticstr> { // In this case, in order to apply a visitor, a deserializer should do // some preparation. The visitor does its stuff, but it doesn't do everything. letinput_vec = input .split_ascii_whitespace() .map(|x| x.parse().unwrap()) .collect();