{"id":18013053,"url":"https://github.com/sim642/oop-visitor-template","last_synced_at":"2025-08-21T01:09:10.151Z","repository":{"id":141777766,"uuid":"131468671","full_name":"sim642/oop-visitor-template","owner":"sim642","description":null,"archived":false,"fork":false,"pushed_at":"2018-05-07T20:01:00.000Z","size":44,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-04T14:21:37.652Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sim642.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-04-29T05:31:17.000Z","updated_at":"2018-05-07T19:59:19.000Z","dependencies_parsed_at":null,"dependency_job_id":"abf85e0f-1fba-4445-954c-b004ddb6c95a","html_url":"https://github.com/sim642/oop-visitor-template","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sim642/oop-visitor-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sim642%2Foop-visitor-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sim642%2Foop-visitor-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sim642%2Foop-visitor-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sim642%2Foop-visitor-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sim642","download_url":"https://codeload.github.com/sim642/oop-visitor-template/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sim642%2Foop-visitor-template/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271411017,"owners_count":24754836,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-20T02:00:09.606Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-30T03:19:44.877Z","updated_at":"2025-08-21T01:09:10.126Z","avatar_url":"https://github.com/sim642.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Visitor pattern\n\n\u003e The visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existent object structures without modifying the structures.\n\u003e — _[Visitor pattern, Wikipedia](https://en.wikipedia.org/wiki/Visitor_pattern)_\n\nYou should immediately clone [this repository](https://github.com/sim642/oop-visitor-template) to look at all the classes and examples mentioned here.\n\nBefore talking about the visitor pattern in more detail, there must be a data structure to work with.\n\n## Data structure\nFor the following examples, a tree-like data structure describing simple vector graphics will be used. \nIt has been defined in package `nodes` and has the following structure:\n* `DrawNode` - base class for all nodes\n    * `LeafNode` - base class for all visual elements\n        * `RectangleNode` - class for rectangles\n        * `CircleNode` - class for circles\n        * `TextNode` - class for text\n    * `GroupNode` - class for grouping multiple nodes to be treated as one\n    * `ImageNode` - class for the entire image with a single node\n\nThey may be used to define an image like (in `DemoImages` class)\n```java\nnew ImageNode(\n        new GroupNode(List.of(\n                new RectangleNode(10, 10, 100, 50),\n                new GroupNode(List.of(\n                        new CircleNode(150, 30, 20),\n                        new TextNode(200, 50, \"Some text\")\n                )),\n                new TextNode(30, 100, \"My cool image\")\n        ))\n);\n```\n\nStructurally this is somewhat similar to JavaFX scenes and scene graphs.\nIt is actually an example of the [**composite pattern**](https://en.wikipedia.org/wiki/Composite_pattern).\n\n## Algorithms on the structure\nThe node classes themselves are very simple: they just store data in fields.\nWith that in place, there are several different ways to operate on the data structure when different nodes should behave differently.\nFor example, print this kind of image's structure or count its nodes.\n\n### Overloaded methods\nFirstly, these algorithms may be added as abstract methods in the base class and implemented in all the nodes.\nThis is the simplest approach that we've seed and used so far.\nIt's not always suitable:\n1. If there are too many different methods to add, then the node classes will be all about the pieces of all the different operations to implement and not the original structure itself.\n2. If the classes cannot be modified (e.g. they're part of a library), then it's simply impossible to add methods to the nodes.\n\nThe next two approaches avoid these issues by implementing desired algorithms outside of the data structure.\n\n### `instanceof` and casts\nSecondly, operations may be implemented using if statements with `instanceof` conditions to differentiate different types.\nIn the respective branches of such if statements, the object of the general type can be safely cast into a more specific type safely, after having checked it.\n\n**`InstanceofPrinter` has an example of printing the node structure and `InstanceofCounter` has an example of counting nodes via this approach.**\n\nIt also has several downsides:\n1. Using `instanceof` and casting is considered bad style in object-oriented programming because nicer and intended techniques are not being utilized. They are also rather verbose.\n2. It is easy to forget to handle a type that may be in the data structure.\n\nThe next approach avoids these issues by introducing a significantly more sophisticated system.\n\n### Visitor pattern\nThirdly, there is the visitor pattern. `FileVisitor`, used for `Files.walkFileTree`, also is one example of the visitor pattern that exists in JDK.\nWe look at the pattern more classically on a data structure, not the file tree.\n\n#### Setup \nIt allows implementing operations by implementing a visitor interface (`DrawVisitor`) which contains a method overload(s) for every concrete node type:\n```java\npublic interface DrawVisitor {\n    void visit(RectangleNode rectangle);\n    void visit(CircleNode circle);\n    void visit(TextNode text);\n    \n    void preVisit(GroupNode group);\n    void postVisit(GroupNode group);\n    \n    void preVisit(ImageNode image);\n    void postVisit(ImageNode image);\n}\n```\n\nThe abstract node base class (`DrawNode`) will have an additional abstract method for accepting a visitor:\n```java\npublic abstract void accept(DrawVisitor visitor);\n```\nEvery concrete leaf node class implements this in a very simple manner:\n```java\n@Override\npublic void accept(DrawVisitor visitor) {\n    visitor.visit(this);\n}\n```\nComposite node classes do it similarly by calling `preVisit` and `postVisit` and sending the visitor to their children in between, so **the whole structure automatically gets visited**.\nThe implementation is pretty much the same in every subclass intentionally because it cannot be implemented directly in the superclass!\n_This is explained below._\n\nAn implementation of the visitor interface with corresponding actions for every node type can be passed to a node's `accept` method to run the algorithm over the data structure. \n**`VisitorPrinter` has a corresponding example of printing the node structure and `VisitorCounter` has a corresponding example of counting nodes via this approach.**\n\nThe latter extends the `SimpleDrawVisitor` class instead of implementing the `DrawVisitor` interface directly.\nIt contains a reasonable default behavior to simplify creating new visitors where not all methods need to do something special.\nNote that no `instanceof` nor casts are required when implementing an algorithm.\n\n#### Working mechanism\nThe visitor pattern works through **double-dispatch**.\nWhen `accept` is called on a node with a visitor, two calls are dispatched:\n1. The implementation of `accept` that runs is found using dynamic dispatch from the node's actual class, which overrides the `accept` method.\n2. The implementation of `visit` (or `preVisit`/`postVisit`) that runs is found using overloading based on the node's actual class, which calls `visitor.visit(this)`.\n    Because every class implements this on their own, the type of `this` is specifically that class and thus the desired overloaded version of `visit` can be run on the visitor. \n    This is why `accept` needs to be identically implemented in all the subclasses. If it were implemented in the superclass, the type of `this` would be the superclass type, which is insufficient to decide, which `visit` overload needs to be called.\n    _In fact, it wouldn't even compile._\n\n##### Detailed example\nWhen running the following in `VisitorPrinter`\n```java\nprintDrawNode(DemoImages.DEMO_GROUP, System.out);\n```\nthese things happen:\n1. Visitor created (indent = 0).\n2. `GroupNode#accept` called:\n    1. `preVisit(GroupNode)` called:\n        1. `Group` printed (indent = 0).\n        2. Indent increased (indent = 1).\n    2. `CircleNode#accept` called:\n        1. `visit(CircleNode)` called:\n            1. `  Circle` printed (indent = 1).\n    3. `TextNode#accept` called:\n        1. `visit(TextNode)` called:\n            1. `  Text` printed (indent = 1).\n    4. `postVisit(GroupNode)` called:\n        1. Indent decreased (indent = 0).\n    \n**You should also try running the visitor examples under the debugger and step through them to see first-hand how things run.**\n\n#### Pros \u0026 cons\nVisitor pattern allows implementing any number of algorithms on the same data structure without resorting to undesired `instanceof` and casting by adding exactly one method to the data classes.\nThus it solves all the issues described above with previous approaches.\n\nIt also is simpler in the sense, that the visitor (specifically the `accept` methods) send the visitor to child nodes automatically, so this doesn't need to be done when implementing such visiting algorithm.\nThis is great since we don't need to worry about the complex internal structure of the nodes if we don't want to and can only care about some specific nodes, like rectangles.\n\nThe pattern doesn't, however, come without its cons:\n1. It is significantly more complicated than the other approaches, both in its setup and working mechanism.\n2. it isn't always neater to implement a visitor, especially when multiple types need to be treated the same, e.g. in `VisitorCounter` class.\n\nStill, the visitor pattern and other visitor-like patterns are widely preferred when dealing with hierarchical and tree structures consisting of elements of different types. \nIn addition to specific application data structures, it's also used more generally, for example:\n* Navigating directory and file trees.\n* Parsing and writing XML efficiently by streaming.\n* Manipulating (abstract) syntax trees of programs in compiler construction.\n\n#### Variations\nThe visitor pattern implementation used here is only one out of many possible variants.\nDifferent possibilities may include\n* iterating children in `visit` methods instead of `accept` methods,\n* returning something from `visit` methods,\n* passing extra arguments to `accept` and `visit` methods.\n\nThe different variants have usually are more flexible but also more complex.\nWhat features are needed from a visitor depends on the specific algorithms and applications.\n\n## Design patterns\nThere is a very famous software engineering book called [\"Design Patterns\"](https://en.wikipedia.org/wiki/Design_Patterns) by the so-called **Gang of Four** (GoF) that describes 23 such design patterns.\nAmong those are the composite and visitor patterns. Others you might have knowingly or unknowingly used are:\n* [adapter pattern](https://en.wikipedia.org/wiki/Adapter_pattern),\n* [decorator pattern](https://en.wikipedia.org/wiki/Decorator_pattern),\n* [chain-of-responsibility pattern](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern),\n* [observer pattern](https://en.wikipedia.org/wiki/Observer_pattern),\n* [template method pattern](https://en.wikipedia.org/wiki/Template_method_pattern).\n\nWhile you shouldn't resort to these possibly sophisticated patterns to solve every object-oriented design issue, being aware of their existence might become handy, especially while reading other people's advanced code that makes use of such patterns.\n\n## Tasks\n[This repository](https://github.com/sim642/oop-visitor-template) contains some tasks (in suitable packages) that can be solved by implementing a suitable visitor. Study the `DrawNode` hierarchy, the examples in `sample` package and tests to solve the tasks.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsim642%2Foop-visitor-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsim642%2Foop-visitor-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsim642%2Foop-visitor-template/lists"}