Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 0.22.0
- Add visitor pattern: "Shape Xml Export".

## 0.21.0
- Add strategy pattern: "Reservation cargo spaces".

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
78 changes: 78 additions & 0 deletions patterns/visitor/shapes_exporter/README.md
Original file line number Diff line number Diff line change
@@ -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:
![image](https://user-images.githubusercontent.com/8049534/167304227-04237030-879e-4d7f-be32-4c815a3e1cbf.png)

### 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
<?xml version="1.0" encoding="utf-8"?>
<compound>
<x>30</x>
<y>45</y>
<children>
<rectangle>
<x>10</x>
<y>10</y>
<width>100</width>
<height>100</height>
</rectangle>
<circle>
<xCenter>300</xCenter>
<yCenter>20</yCenter>
<radius>35</radius>
</circle>
<dot>
<x>60</x>
<y>60</y>
</dot>
<compound>
<x>5</x>
<y>5</y>
<children>
<dot>
<x>15</x>
<y>15</y>
</dot>
<dot>
<x>20</x>
<y>20</y>
</dot>
</children>
</compound>
</children>
</compound>
```
28 changes: 28 additions & 0 deletions patterns/visitor/shapes_exporter/main.dart
Original file line number Diff line number Diff line change
@@ -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);
}
24 changes: 24 additions & 0 deletions patterns/visitor/shapes_exporter/shapes/circle.dart
Original file line number Diff line number Diff line change
@@ -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() {
// ...
}
}
24 changes: 24 additions & 0 deletions patterns/visitor/shapes_exporter/shapes/compound_shape.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import '../visitor/visitor.dart';
import 'shape.dart';

class CompoundShape implements Shape {
final int x;
final int y;
final List<Shape> children;

CompoundShape({
required this.x,
required this.y,
required this.children,
});

@override
void accept(Visitor visitor) {
visitor.visitCompoundShape(this);
}

@override
void draw() {
// ...
}
}
22 changes: 22 additions & 0 deletions patterns/visitor/shapes_exporter/shapes/dot.dart
Original file line number Diff line number Diff line change
@@ -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() {
// ...
}
}
26 changes: 26 additions & 0 deletions patterns/visitor/shapes_exporter/shapes/rectangle.dart
Original file line number Diff line number Diff line change
@@ -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() {
// ...
}
}
7 changes: 7 additions & 0 deletions patterns/visitor/shapes_exporter/shapes/shape.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import '../visitor/visitor.dart';

abstract class Shape {
void accept(Visitor visitor);

void draw();
}
14 changes: 14 additions & 0 deletions patterns/visitor/shapes_exporter/visitor/visitor.dart
Original file line number Diff line number Diff line change
@@ -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);
}
73 changes: 73 additions & 0 deletions patterns/visitor/shapes_exporter/visitor/xml_export_visitor.dart
Original file line number Diff line number Diff line change
@@ -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('<?xml version="1.0" encoding="utf-8">?');
shape.accept(this);
return buff.toString().trim();
}

@override
void visitCompoundShape(CompoundShape compound) {
_write('<compound>', openTag: true);
_write('<x>${compound.x}</x>');
_write('<y>${compound.y}</y>');
_write('<children>', openTag: true);

for (final shape in compound.children) {
shape.accept(this);
}

_write('</children>', closeTag: true);
_write('</compound>', closeTag: true);
}

@override
void visitCircle(circle) {
_write('<circle>', openTag: true);
_write('<xCenter>${circle.xCenter}</xCenter>');
_write('<yCenter>${circle.yCenter}</yCenter>');
_write('<radius>${circle.radius}</radius>');
_write('</circle>', closeTag: true);
}

@override
void visitDot(Dot dot) {
_write('<dot>', openTag: true);
_write('<x>${dot.x}</x>');
_write('<y>${dot.y}</y>');
_write('</dot>', closeTag: true);
}

@override
void visitRectangle(Rectangle rectangle) {
_write('<rectangle>', openTag: true);
_write('<x>${rectangle.x}</x>');
_write('<y>${rectangle.y}</y>');
_write('<width>${rectangle.width}</width>');
_write('<height>${rectangle.height}</height>');
_write('</rectangle>', 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++;
}
}
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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
Expand Down