The accessor pattern is a behavioral pattern that allows arbitrary detached visitors to be able to access managed elements under the control of the manager. The visitor cannot change the definition of the object (but this is not mandatory, you can agree to allow changes). For the manager, it does not care how many visitors there actually are, it only cares about a defined order of access to the elements (for example, for a binary tree, you can provide multiple access orders such as mid-order, pre-order, etc.).
The Visitor schema contains two main objects: the Visitable object and the Vistor object. In addition, Visited objects are included in the Visitor schema as objects that will be manipulated.
A Visitable object, i.e., a manager, may contain a series of elements (Visited) of various shapes and sizes that may have complex structural relationships within the Visitable (but can also be some kind of simple containment relationship, such as a simple vector.) The Visitable will generally be a complex container that is responsible for interpreting these relationships and traversing them with A standard logic is used to traverse these elements. When Visitable traverses these elements, it provides each element to the Visitor so that it can access the Visited element.
Such a programming pattern is the Visitor Pattern.
In order to be able to observe each element, there is therefore a practical necessity to have a constraint: all observable elements have a common base class Visited.
All Visitors must be derived from Visitor in order to be available to the Visitable.accept(visitor&) interface.
For example, suppose we are designing a set of vector editors. In the Canvas, there can be many Layers, each containing certain properties (e.g. fill color, transparency), and there can be multiple Elements. Elements can be Point, Line, Rect, Arc, etc.
To be able to draw the canvas on the screen, we can have a Screen device object that implements the Visitor interface, so the canvas can accept access to the Screen and thus draw the elements of the canvas to the screen.
If we provide Printer as an observer, then the canvas will be able to print out the image elements.
If we provide Document as an observer, then the canvas will be able to serialize the graphical properties to a disk file.
If we need additional behavior in the future, we can go ahead and add new observers and then do something similar with the canvas and the tuples it holds.
- If you need to perform certain operations on all elements in a complex object structure (such as an object tree), you can use the visitor pattern.
- The visitor pattern takes the non-primary functionality away from the object manager, so it is also a means of decoupling.
- If you are making a class library for an object library, then providing an access interface to the outside will facilitate the user to develop his own visitor to access your class library without intrusion - he doesn’t have to give you issue/pull request for his own little thing.
- For complex structural hierarchies, make good use of object nesting and recursion capabilities to avoid writing similar logic over and over again.
Check out the reference implementations of canva, layer, and group, which accomplish nesting self-management capabilities by implementing
vistiable<drawable>, and enabling
accept()to go recursively into each container.
We implemented a part of the vector image editor as an example, using the base class template given earlier.
drawable and base elements
First do the basic declaration of drawable/shape and the base tuples.
For debugging purposes, we overload the ‘"’ stream output operator and use the macro MAKE_DRAWABLE to cut down on repetitive code keystrokes. In the MAKE_DRAWABLE macro, we get the class name via
hicc::debug::type_name<T>() and return this as a string from
For simplicity reasons the base tuples are not hierarchical, but are derived in parallel from drawable.
Composite elements and layers
The following declares a group object, which contains a set of tuples. Since we want to have as much recursive structure as possible, layers are also considered to be a combination of a group of tuples
The group class already implements the visitable interface, and its accept can accept visitors, at which point the tuples group iterates through all of its tuples and makes them available to visitors.
You can also create compound tuples based on the group class, which allows several tuples to be combined into a new tuples component, the difference being that groups are generally temporary objects for UI operations, while compound tuples can be selected and used by the user as part of a component library.
By default, guest accesses elements of the form
visited const &, which is read-only.
The layer has at least all the capabilities of a group, so it does the same thing for visitors. The properties part of the layer (mask, overlay, etc.) is omitted.
The canvas contains several layers, so it should also implement the visitable interface.
Where add will create a new layer with default arguments and the layer order follows an upward stacking pattern. get and the
 operator can access a layer with a positive integer subscript. However, the code does not contain a layer order management function. If you wish, you can add a
std::vector<draw_id> helper structure to help manage the layer order.
Now let’s review the canvas-layer-tuple system. The accept interface successfully runs through the entire system.
It’s time to create the visitors
screen or printer
Both implement simple visitor interfaces.
hicc::to_string is a simple stream wrapper that does the following core logic.
The test program constructs a miniature canvas and several graph elements and then accesses them schematically.
The output should look something like this.
The Visitor pattern can sometimes be replaced by the Iterator pattern. But iterators often have a fatal flaw that affects their usefulness: iterators themselves can be rigid, costly, and inefficient - unless you make the most appropriate design choices and implement the most elaborate iterator. They both allow the user to access the contents of a known complex container without intrusion.
BTW, some people say that it is because languages like Java do not support double assignment that the visitor pattern was born. This is pure fucking bullshit: Java was officially released in 1995, when it was a fairly rudimentary prototype and there were no patterns to speak of, and it wasn’t until 1998, when JDK 1.2 came out, that EJB, J2EE, etc., and then its design patterns took off in a big way. It is not too much to say that Java is riding on the east wind of GoF summary, without the milestone experience summary of GoF in 1994, there would not be Design Patterns terminology in the following decades until today, so too many kids are ignorant and fearless to open their mouths.
For C++, the definition of double-distribution or whatever is completely meaningless. I’m so sick of Java.