diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9bb36d0..16286c9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+## 0.22.0
+- Add visitor pattern: "Shape Xml Export".
+
## 0.21.0
- Add strategy pattern: "Reservation cargo spaces".
diff --git a/README.md b/README.md
index 2bcd1de..69a5674 100644
--- a/README.md
+++ b/README.md
@@ -19,8 +19,8 @@ It contains **Dart** examples for all classic **GoF** design patterns.
- [x] **Observer** - [[Open-Close Editor Events](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/open_close_editor_events)] [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/app_observer)] [[Subscriber Flutter Widget](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/subscriber_flutter_widget)]
- [ ] **State**
- [ ] **Template Method**
- - [ ] **Visitor**
- - [X] **Strategy** [[Reservation cargo spaces](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/strategy/reservation_cargo_spaces)]
+ - [X] **Visitor** [[Shape XML Exporter](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/visitor/shapes_exporter)]
+ - [X] **Strategy** [[Reservation Cargo Spaces](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/strategy/reservation_cargo_spaces)]
- [ ] **Structural**
- [x] **Adapter** - [[Text Graphics](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/adapter/text_graphics)] [[Square Round conflict](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/adapter/square_round_conflict)] [[Flutter Adapter](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/adapter/flutter_adapter)]
- [x] **Bridge** - [[Remote Device Control](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/bridge/devices_remote_control)] [[Clock](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/bridge/clock)]
diff --git a/patterns/visitor/shapes_exporter/README.md b/patterns/visitor/shapes_exporter/README.md
new file mode 100644
index 0000000..11ebdc4
--- /dev/null
+++ b/patterns/visitor/shapes_exporter/README.md
@@ -0,0 +1,78 @@
+# Visitor pattern
+Visitor is a behavioral design pattern that lets you separate algorithms from the objects on which
+they operate.
+
+Tutorial: [here](https://refactoring.guru/design-patterns/visitor).
+
+### About example: Shape XML Exporter.
+In this example, the Visitor pattern adds XML export support to the class hierarchy of geometric
+shapes [More info](https://refactoring.guru/design-patterns/visitor#pseudocode).
+
+This example rewrite from original source code [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/main/src/refactoring_guru/visitor/example)
+
+### Diagram:
+
+
+### Client code:
+```dart
+ final compoundShape = CompoundShape(
+ x: 30,
+ y: 45,
+ children: [
+ Rectangle(x: 10, y: 10, width: 100, height: 100),
+ Circle(xCenter: 300, yCenter: 20, radius: 35),
+ Dot(x: 60, y: 60),
+ CompoundShape(
+ x: 5,
+ y: 5,
+ children: [
+ Dot(x: 15, y: 15),
+ Dot(x: 20, y: 20),
+ ],
+ ),
+ ],
+ );
+
+ final xml = XMLExportVisitor().export(compoundShape);
+ print(xml);
+```
+
+### Output:
+```xml
+
+
+ 30
+ 45
+
+
+ 10
+ 10
+ 100
+ 100
+
+
+ 300
+ 20
+ 35
+
+
+ 60
+ 60
+
+
+ 5
+ 5
+
+
+ 15
+ 15
+
+
+ 20
+ 20
+
+
+
+
+
+```
diff --git a/patterns/visitor/shapes_exporter/main.dart b/patterns/visitor/shapes_exporter/main.dart
new file mode 100644
index 0000000..bf0c2df
--- /dev/null
+++ b/patterns/visitor/shapes_exporter/main.dart
@@ -0,0 +1,28 @@
+import 'shapes/circle.dart';
+import 'shapes/compound_shape.dart';
+import 'shapes/dot.dart';
+import 'shapes/rectangle.dart';
+import 'visitor/xml_export_visitor.dart';
+
+void main() {
+ final compoundShape = CompoundShape(
+ x: 30,
+ y: 45,
+ children: [
+ Rectangle(x: 10, y: 10, width: 100, height: 100),
+ Circle(xCenter: 300, yCenter: 20, radius: 35),
+ Dot(x: 60, y: 60),
+ CompoundShape(
+ x: 5,
+ y: 5,
+ children: [
+ Dot(x: 15, y: 15),
+ Dot(x: 20, y: 20),
+ ],
+ ),
+ ],
+ );
+
+ final xml = XMLExportVisitor().export(compoundShape);
+ print(xml);
+}
diff --git a/patterns/visitor/shapes_exporter/shapes/circle.dart b/patterns/visitor/shapes_exporter/shapes/circle.dart
new file mode 100644
index 0000000..739c99e
--- /dev/null
+++ b/patterns/visitor/shapes_exporter/shapes/circle.dart
@@ -0,0 +1,24 @@
+import '../visitor/visitor.dart';
+import 'shape.dart';
+
+class Circle implements Shape {
+ final int xCenter;
+ final int yCenter;
+ final int radius;
+
+ Circle({
+ required this.xCenter,
+ required this.yCenter,
+ required this.radius,
+ });
+
+ @override
+ void accept(Visitor visitor) {
+ visitor.visitCircle(this);
+ }
+
+ @override
+ void draw() {
+ // ...
+ }
+}
diff --git a/patterns/visitor/shapes_exporter/shapes/compound_shape.dart b/patterns/visitor/shapes_exporter/shapes/compound_shape.dart
new file mode 100644
index 0000000..d35338e
--- /dev/null
+++ b/patterns/visitor/shapes_exporter/shapes/compound_shape.dart
@@ -0,0 +1,24 @@
+import '../visitor/visitor.dart';
+import 'shape.dart';
+
+class CompoundShape implements Shape {
+ final int x;
+ final int y;
+ final List children;
+
+ CompoundShape({
+ required this.x,
+ required this.y,
+ required this.children,
+ });
+
+ @override
+ void accept(Visitor visitor) {
+ visitor.visitCompoundShape(this);
+ }
+
+ @override
+ void draw() {
+ // ...
+ }
+}
diff --git a/patterns/visitor/shapes_exporter/shapes/dot.dart b/patterns/visitor/shapes_exporter/shapes/dot.dart
new file mode 100644
index 0000000..5d5811a
--- /dev/null
+++ b/patterns/visitor/shapes_exporter/shapes/dot.dart
@@ -0,0 +1,22 @@
+import '../visitor/visitor.dart';
+import 'shape.dart';
+
+class Dot implements Shape {
+ final int x;
+ final int y;
+
+ Dot({
+ required this.x,
+ required this.y,
+ });
+
+ @override
+ void accept(Visitor visitor) {
+ visitor.visitDot(this);
+ }
+
+ @override
+ void draw() {
+ // ...
+ }
+}
diff --git a/patterns/visitor/shapes_exporter/shapes/rectangle.dart b/patterns/visitor/shapes_exporter/shapes/rectangle.dart
new file mode 100644
index 0000000..bd0bb4e
--- /dev/null
+++ b/patterns/visitor/shapes_exporter/shapes/rectangle.dart
@@ -0,0 +1,26 @@
+import '../visitor/visitor.dart';
+import 'shape.dart';
+
+class Rectangle implements Shape {
+ final int x;
+ final int y;
+ final int width;
+ final int height;
+
+ Rectangle({
+ required this.x,
+ required this.y,
+ required this.width,
+ required this.height,
+ });
+
+ @override
+ void accept(Visitor visitor) {
+ visitor.visitRectangle(this);
+ }
+
+ @override
+ void draw() {
+ // ...
+ }
+}
diff --git a/patterns/visitor/shapes_exporter/shapes/shape.dart b/patterns/visitor/shapes_exporter/shapes/shape.dart
new file mode 100644
index 0000000..aecdf09
--- /dev/null
+++ b/patterns/visitor/shapes_exporter/shapes/shape.dart
@@ -0,0 +1,7 @@
+import '../visitor/visitor.dart';
+
+abstract class Shape {
+ void accept(Visitor visitor);
+
+ void draw();
+}
diff --git a/patterns/visitor/shapes_exporter/visitor/visitor.dart b/patterns/visitor/shapes_exporter/visitor/visitor.dart
new file mode 100644
index 0000000..3d6ba3b
--- /dev/null
+++ b/patterns/visitor/shapes_exporter/visitor/visitor.dart
@@ -0,0 +1,14 @@
+import '../shapes/circle.dart';
+import '../shapes/compound_shape.dart';
+import '../shapes/dot.dart';
+import '../shapes/rectangle.dart';
+
+abstract class Visitor {
+ void visitCompoundShape(CompoundShape compound);
+
+ void visitDot(Dot dot);
+
+ void visitCircle(Circle circle);
+
+ void visitRectangle(Rectangle rectangle);
+}
diff --git a/patterns/visitor/shapes_exporter/visitor/xml_export_visitor.dart b/patterns/visitor/shapes_exporter/visitor/xml_export_visitor.dart
new file mode 100644
index 0000000..be6acd3
--- /dev/null
+++ b/patterns/visitor/shapes_exporter/visitor/xml_export_visitor.dart
@@ -0,0 +1,73 @@
+import '../shapes/compound_shape.dart';
+import '../shapes/dot.dart';
+import '../shapes/rectangle.dart';
+import '../shapes/shape.dart';
+import 'visitor.dart';
+
+class XMLExportVisitor implements Visitor {
+ final buff = StringBuffer();
+
+ String export(Shape shape) {
+ buff.clear();
+ _write('?');
+ shape.accept(this);
+ return buff.toString().trim();
+ }
+
+ @override
+ void visitCompoundShape(CompoundShape compound) {
+ _write('', openTag: true);
+ _write('${compound.x}');
+ _write('${compound.y}');
+ _write('', openTag: true);
+
+ for (final shape in compound.children) {
+ shape.accept(this);
+ }
+
+ _write('', closeTag: true);
+ _write('', closeTag: true);
+ }
+
+ @override
+ void visitCircle(circle) {
+ _write('', openTag: true);
+ _write('${circle.xCenter}');
+ _write('${circle.yCenter}');
+ _write('${circle.radius}');
+ _write('', closeTag: true);
+ }
+
+ @override
+ void visitDot(Dot dot) {
+ _write('', openTag: true);
+ _write('${dot.x}');
+ _write('${dot.y}');
+ _write('', closeTag: true);
+ }
+
+ @override
+ void visitRectangle(Rectangle rectangle) {
+ _write('', openTag: true);
+ _write('${rectangle.x}');
+ _write('${rectangle.y}');
+ _write('${rectangle.width}');
+ _write('${rectangle.height}');
+ _write('', closeTag: true);
+ }
+
+ int _openTags = 0;
+
+ void _write(String str, {openTag = false, closeTag = false}) {
+ if (closeTag) {
+ _openTags--;
+ }
+
+ final tab = ' ' * _openTags;
+ buff.writeln(tab + str);
+
+ if (openTag) {
+ _openTags++;
+ }
+ }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 8dc319c..6ac14f2 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: design_patterns_dart
description: Dart examples for all classic GoF design patterns.
-version: 0.21.0
+version: 0.22.0
homepage: https://refactoring.guru/design-patterns
repository: https://github.com/RefactoringGuru/design-patterns-dart
issue_tracker: https://github.com/RefactoringGuru/design-patterns-dart/issue