From 0782fc25ccc43b44d8563823afa250bf505b41d0 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Wed, 8 Dec 2021 00:43:49 +0200
Subject: [PATCH 001/479] Init project structure & add README.md
---
.gitignore | 6 ++++
CHANGELOG.md | 4 +++
README.md | 81 +++++++++++++++++++++++++++++++++++++++++++
analysis_options.yaml | 30 ++++++++++++++++
pubspec.yaml | 12 +++++++
5 files changed, 133 insertions(+)
create mode 100644 .gitignore
create mode 100644 CHANGELOG.md
create mode 100644 README.md
create mode 100644 analysis_options.yaml
create mode 100644 pubspec.yaml
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d111b3a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+.dart_tool/
+.idea/
+build/
+
+.packages
+pubspec.lock
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..d6fa168
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,4 @@
+## 0.0.1
+
+- Initial project structure.
+- Add README
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a314796
--- /dev/null
+++ b/README.md
@@ -0,0 +1,81 @@
+# Design Patterns in Dart
+
+This repository is part of the [Refactoring.Guru](https://refactoring.guru/design-patterns) project.
+
+It contains **Dart** examples for all classic GoF design patterns.
+
+# Implementation checklist:
+- [ ] **Creation**
+ - [ ] Abstract Factory
+ - [ ] Builder
+ - [ ] Factory Method
+ - [ ] Prototype
+ - [ ] Singleton
+- [ ] **Behavioral**
+ - [ ] Chain of Responsibility
+ - [ ] Command
+ - [ ] Interpreter
+ - [ ] Iterator
+ - [ ] Mediator
+ - [ ] Memento
+ - [ ] Observer
+ - [ ] State
+ - [ ] Template Method
+ - [ ] Visitor
+ - [ ] Strategy
+- [ ] **Structural**
+ - [ ] Adapter
+ - [ ] Bridge
+ - [ ] Composite
+ - [ ] Decorator
+ - [ ] Facade
+ - [ ] Flyweight
+ - [ ] Proxy
+
+## Requirements
+
+The examples were written in **Dart 2.15**.
+
+## Contributor's Guide
+
+We appreciate any help, whether it's a simple fix of a typo or a whole new example. Just [make a fork](https://help.github.com/articles/fork-a-repo/), do your change and submit a [pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
+
+Here's a style guide which might help you to keep your changes consistent with our code:
+
+1. All code should meet the [Effective Dart: Style](https://dart.dev/guides/language/effective-dart/style).
+
+2. Use [Dart Format](https://dart.dev/tools/dart-format) or auto format shortcut `Ctrl + Alt + L` in your ide.
+
+3. Try to hard wrap the code at 80th's character. It helps to list the code on the website without scrollbars.
+
+4. File names should match following convention: `some_class_name.dart`
+
+5. Places classes into separate files.
+
+6. Comments may or may not have language tags in them, such as this:
+
+ ```dart
+ /**
+ * EN: All products families have the same varieties (MacOS/Windows).
+ *
+ * This is a MacOS variant of a button.
+ *
+ * RU: Все семейства продуктов имеют одни и те же вариации (MacOS/Windows).
+ *
+ * Это вариант кнопки под MacOS.
+ */
+ ```
+
+ Don't be scared and ignore the non-English part of such comments. If you want to change something in a comment like this, then do it. Even if you do it wrong, we'll tell you how to fix it during the Pull Request.
+
+
+## License
+
+This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
+
+
+
+
+## Credits
+
+Authors: Alexander Shvets ([@neochief](https://github.com/neochief))
diff --git a/analysis_options.yaml b/analysis_options.yaml
new file mode 100644
index 0000000..dee8927
--- /dev/null
+++ b/analysis_options.yaml
@@ -0,0 +1,30 @@
+# This file configures the static analysis results for your project (errors,
+# warnings, and lints).
+#
+# This enables the 'recommended' set of lints from `package:lints`.
+# This set helps identify many issues that may lead to problems when running
+# or consuming Dart code, and enforces writing Dart using a single, idiomatic
+# style and format.
+#
+# If you want a smaller set of lints you can change this to specify
+# 'package:lints/core.yaml'. These are just the most critical lints
+# (the recommended set includes the core lints).
+# The core lints are also what is used by pub.dev for scoring packages.
+
+include: package:lints/recommended.yaml
+
+# Uncomment the following section to specify additional rules.
+
+# linter:
+# rules:
+# - camel_case_types
+
+# analyzer:
+# exclude:
+# - path/to/excluded/files/**
+
+# For more information about the core and recommended set of lints, see
+# https://dart.dev/go/core-lints
+
+# For additional information about configuring this file, see
+# https://dart.dev/guides/language/analysis-options
diff --git a/pubspec.yaml b/pubspec.yaml
new file mode 100644
index 0000000..9ae6642
--- /dev/null
+++ b/pubspec.yaml
@@ -0,0 +1,12 @@
+name: design_patterns_dart
+description: Dart examples for all classic GoF design patterns.
+version: 0.0.1
+homepage: https://refactoring.guru/design-patterns
+repository: https://github.com/RefactoringGuru/design-patterns-dart
+issue_tracker: https://github.com/RefactoringGuru/design-patterns-dart/issue
+
+environment:
+ sdk: '>=2.15.0-68.0.dev <3.0.0'
+
+dev_dependencies:
+ lints: ^1.0.0
From 1766515378e930811f106b55b929c8aa93f61457 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Wed, 8 Dec 2021 01:15:03 +0200
Subject: [PATCH 002/479] Add pattern Prototype
---
CHANGELOG.md | 3 +++
README.md | 16 ++++++-------
bin/prototype/example/main.dart | 27 +++++++++++++++++++++
bin/prototype/example/output.txt | 2 ++
bin/prototype/example/shape/circle.dart | 28 ++++++++++++++++++++++
bin/prototype/example/shape/rectangle.dart | 26 ++++++++++++++++++++
bin/prototype/example/shape/shape.dart | 14 +++++++++++
7 files changed, 108 insertions(+), 8 deletions(-)
create mode 100644 bin/prototype/example/main.dart
create mode 100644 bin/prototype/example/output.txt
create mode 100644 bin/prototype/example/shape/circle.dart
create mode 100644 bin/prototype/example/shape/rectangle.dart
create mode 100644 bin/prototype/example/shape/shape.dart
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d6fa168..f793aee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+## 0.1.0
+- Add pattern Prototype
+
## 0.0.1
- Initial project structure.
diff --git a/README.md b/README.md
index a314796..552c1cb 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ It contains **Dart** examples for all classic GoF design patterns.
- [ ] Abstract Factory
- [ ] Builder
- [ ] Factory Method
- - [ ] Prototype
+ - [x] Prototype
- [ ] Singleton
- [ ] **Behavioral**
- [ ] Chain of Responsibility
@@ -24,13 +24,13 @@ It contains **Dart** examples for all classic GoF design patterns.
- [ ] Visitor
- [ ] Strategy
- [ ] **Structural**
- - [ ] Adapter
- - [ ] Bridge
- - [ ] Composite
- - [ ] Decorator
- - [ ] Facade
- - [ ] Flyweight
- - [ ] Proxy
+ - [ ] Adapter
+ - [ ] Bridge
+ - [ ] Composite
+ - [ ] Decorator
+ - [ ] Facade
+ - [ ] Flyweight
+ - [ ] Proxy
## Requirements
diff --git a/bin/prototype/example/main.dart b/bin/prototype/example/main.dart
new file mode 100644
index 0000000..c1f7af4
--- /dev/null
+++ b/bin/prototype/example/main.dart
@@ -0,0 +1,27 @@
+import 'shape/circle.dart';
+import 'shape/rectangle.dart';
+
+void main() {
+ final originalShapes = [
+ Rectangle(
+ x: 100,
+ y: 100,
+ width: 500,
+ height: 100,
+ color: '0xfff',
+ ),
+ Circle(
+ x: 20,
+ y: 30,
+ radius: 100,
+ color: '0xf0f',
+ ),
+ ];
+
+ final cloningShapes = [
+ for (final shape in originalShapes) shape.clone(),
+ ];
+
+ print('Origin shapes: $originalShapes');
+ print('Cloning shapes: $cloningShapes');
+}
diff --git a/bin/prototype/example/output.txt b/bin/prototype/example/output.txt
new file mode 100644
index 0000000..14fb975
--- /dev/null
+++ b/bin/prototype/example/output.txt
@@ -0,0 +1,2 @@
+Origin shapes: [Rectangle(address: 0x2f7f544c), Circle(address: 0x3e59b487)]
+Cloning shapes: [Rectangle(address: 0x3eb0a110), Circle(address: 0x75e3636)]
diff --git a/bin/prototype/example/shape/circle.dart b/bin/prototype/example/shape/circle.dart
new file mode 100644
index 0000000..7ea1f13
--- /dev/null
+++ b/bin/prototype/example/shape/circle.dart
@@ -0,0 +1,28 @@
+import 'rectangle.dart';
+
+class Circle extends Rectangle {
+ final int radius;
+
+ Circle({
+ required int x,
+ required int y,
+ required this.radius,
+ required String color,
+ }) : super(
+ x: x,
+ y: y,
+ width: radius,
+ height: radius,
+ color: color,
+ );
+
+ @override
+ Circle clone() {
+ return Circle(
+ x: x,
+ y: y,
+ radius: radius,
+ color: color,
+ );
+ }
+}
diff --git a/bin/prototype/example/shape/rectangle.dart b/bin/prototype/example/shape/rectangle.dart
new file mode 100644
index 0000000..2631cbd
--- /dev/null
+++ b/bin/prototype/example/shape/rectangle.dart
@@ -0,0 +1,26 @@
+import 'shape.dart';
+
+class Rectangle extends Shape {
+ final int width;
+ final int height;
+ final String color;
+
+ Rectangle({
+ required int x,
+ required int y,
+ required this.width,
+ required this.height,
+ required this.color,
+ }) : super(x: x, y: y);
+
+ @override
+ Rectangle clone() {
+ return Rectangle(
+ x: x,
+ y: y,
+ width: width,
+ height: height,
+ color: color,
+ );
+ }
+}
diff --git a/bin/prototype/example/shape/shape.dart b/bin/prototype/example/shape/shape.dart
new file mode 100644
index 0000000..1f7a21f
--- /dev/null
+++ b/bin/prototype/example/shape/shape.dart
@@ -0,0 +1,14 @@
+abstract class Shape {
+ final int x;
+ final int y;
+
+ Shape({
+ required this.x,
+ required this.y,
+ });
+
+ Shape clone();
+
+ @override
+ String toString() => '$runtimeType(address: 0x${hashCode.toRadixString(16)})';
+}
From a7df9e54cbeecf1aae230797213b3c4a7a348dcb Mon Sep 17 00:00:00 2001
From: ilopX
Date: Wed, 8 Dec 2021 01:21:53 +0200
Subject: [PATCH 003/479] Bump version
---
pubspec.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pubspec.yaml b/pubspec.yaml
index 9ae6642..bb7e424 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.0.1
+version: 0.1.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
From 5d6c08ab80f9727b73424ffd425d33e5d8a628e0 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Wed, 8 Dec 2021 17:19:41 +0200
Subject: [PATCH 004/479] Add the Builder pattern using file format converters
as an example
* HtmlConverter
* ConsoleConverter
---
README.md | 4 +-
.../file_format/color_text_reader.dart | 38 +++++++++
.../converters/console_converter.dart | 17 ++++
.../converters/html_converter.dart | 24 ++++++
.../converters/json_converter.dart | 17 ++++
.../file_format/formats/console_format.dart | 24 ++++++
.../file_format/formats/html_format.dart | 84 +++++++++++++++++++
.../file_format/formats/json_format.dart | 16 ++++
bin/builder/file_format/main.dart | 20 +++++
9 files changed, 241 insertions(+), 3 deletions(-)
create mode 100644 bin/builder/file_format/color_text_reader.dart
create mode 100644 bin/builder/file_format/converters/console_converter.dart
create mode 100644 bin/builder/file_format/converters/html_converter.dart
create mode 100644 bin/builder/file_format/converters/json_converter.dart
create mode 100644 bin/builder/file_format/formats/console_format.dart
create mode 100644 bin/builder/file_format/formats/html_format.dart
create mode 100644 bin/builder/file_format/formats/json_format.dart
create mode 100644 bin/builder/file_format/main.dart
diff --git a/README.md b/README.md
index 552c1cb..9a98a91 100644
--- a/README.md
+++ b/README.md
@@ -50,9 +50,7 @@ Here's a style guide which might help you to keep your changes consistent with o
4. File names should match following convention: `some_class_name.dart`
-5. Places classes into separate files.
-
-6. Comments may or may not have language tags in them, such as this:
+5. Comments may or may not have language tags in them, such as this:
```dart
/**
diff --git a/bin/builder/file_format/color_text_reader.dart b/bin/builder/file_format/color_text_reader.dart
new file mode 100644
index 0000000..999cf61
--- /dev/null
+++ b/bin/builder/file_format/color_text_reader.dart
@@ -0,0 +1,38 @@
+/// Director
+class ColorTextReader {
+ final String text;
+
+ ColorTextReader({required this.text});
+
+ TextFormat convert(Converter converter) {
+ for (final word in text.split(' ')) {
+ if (supportedColors.contains(word)) {
+ converter.writeColor(word);
+ } else {
+ converter.writeWord(word);
+ }
+ }
+ return converter.result;
+ }
+
+ final supportedColors = Set.unmodifiable(['red', 'green', 'blue']);
+}
+
+/// Builder
+abstract class Converter {
+ void writeWord(String text);
+
+ void writeColor(String color);
+
+ T get result;
+}
+
+/// Product
+abstract class TextFormat {
+ String get content;
+
+ @override
+ String toString() => '$runtimeType(\n'
+ ' $content'
+ '\n)';
+}
diff --git a/bin/builder/file_format/converters/console_converter.dart b/bin/builder/file_format/converters/console_converter.dart
new file mode 100644
index 0000000..dd74523
--- /dev/null
+++ b/bin/builder/file_format/converters/console_converter.dart
@@ -0,0 +1,17 @@
+import '../formats/console_format.dart';
+import '../color_text_reader.dart';
+
+class ConsoleConverter extends Converter {
+ @override
+ final result = ConsoleFormat();
+
+ @override
+ void writeColor(String color) {
+ result.color = color;
+ result.write(color);
+ result.color = 'black';
+ }
+
+ @override
+ void writeWord(String text) => result.write(text);
+}
diff --git a/bin/builder/file_format/converters/html_converter.dart b/bin/builder/file_format/converters/html_converter.dart
new file mode 100644
index 0000000..e989e9f
--- /dev/null
+++ b/bin/builder/file_format/converters/html_converter.dart
@@ -0,0 +1,24 @@
+import '../formats/html_format.dart';
+import '../color_text_reader.dart';
+
+/// Builder
+class HtmlConverter extends Converter {
+ @override
+ HtmlFormat get result => _html..closeAllTags();
+
+ @override
+ void writeColor(String color) {
+ _html
+ .addStyle(name: color, color: color)
+ .openTagSpan(styleName: color)
+ .writeText(color)
+ .closeLastTag();
+ }
+
+ @override
+ void writeWord(String text) {
+ _html.writeText('$text ');
+ }
+
+ final _html = HtmlFormat()..openTagP();
+}
diff --git a/bin/builder/file_format/converters/json_converter.dart b/bin/builder/file_format/converters/json_converter.dart
new file mode 100644
index 0000000..71a14f5
--- /dev/null
+++ b/bin/builder/file_format/converters/json_converter.dart
@@ -0,0 +1,17 @@
+import '../formats/json_format.dart';
+import '../color_text_reader.dart';
+
+class JsonConverter extends Converter {
+ @override
+ final result = JsonFormat();
+
+ @override
+ void writeColor(String color) {
+ throw UnimplementedError();
+ }
+
+ @override
+ void writeWord(String text) {
+ throw UnimplementedError();
+ }
+}
diff --git a/bin/builder/file_format/formats/console_format.dart b/bin/builder/file_format/formats/console_format.dart
new file mode 100644
index 0000000..018d8cc
--- /dev/null
+++ b/bin/builder/file_format/formats/console_format.dart
@@ -0,0 +1,24 @@
+import '../color_text_reader.dart';
+
+class ConsoleFormat extends TextFormat {
+ final _buff = [];
+
+ static const colors = {
+ 'red': '\x1b[31m',
+ 'green': '\x1b[32m',
+ 'blue': '\x1b[34m',
+ };
+
+ var _fgColor = '';
+ var _end = '';
+
+ set color(String colorName) {
+ _fgColor = colors[colorName] ?? '';
+ _end = _fgColor == '' ? '' : '\x1B[0m';
+ }
+
+ void write(String text) => _buff.add('$_fgColor$text$_end');
+
+ @override
+ String get content => _buff.join(' ');
+}
diff --git a/bin/builder/file_format/formats/html_format.dart b/bin/builder/file_format/formats/html_format.dart
new file mode 100644
index 0000000..2f287c1
--- /dev/null
+++ b/bin/builder/file_format/formats/html_format.dart
@@ -0,0 +1,84 @@
+import 'dart:collection';
+
+import '../color_text_reader.dart';
+
+// product
+class HtmlFormat extends TextFormat {
+ final _openTag = Queue();
+ final _content = StringBuffer();
+
+ HtmlFormat openTagP() {
+ final tag = Tag('p');
+ _openTag.add(tag);
+ _content.write(' $tag\n ');
+ return this;
+ }
+
+ final _styles = {};
+
+ HtmlFormat addStyle({required String name, required String color}) {
+ _styles[name] = color;
+ return this;
+ }
+
+ HtmlFormat openTagSpan({String? styleName}) {
+ final tag = Tag('span', styleName);
+ _openTag.add(tag);
+ _content.write('\n $tag');
+ return this;
+ }
+
+ HtmlFormat writeText(String text) {
+ _content.write(text);
+ return this;
+ }
+
+ HtmlFormat closeLastTag() {
+ final tagName = _openTag.removeLast().name;
+ _content.write('$tagName> ');
+ return this;
+ }
+
+ HtmlFormat closeAllTags() {
+ while (_openTag.isNotEmpty) {
+ closeLastTag();
+ }
+ return this;
+ }
+
+ @override
+ String get content {
+ final styleContent = _styles.entries
+ .map((e) => '.${e.key} { color: ${e.value}; }')
+ .join('\n ');
+
+ return '''
+
+
+ Color text
+
+
+
+$_content
+
+''';
+ }
+}
+
+class Tag {
+ final String name;
+ final String? className;
+ final text = StringBuffer();
+
+ Tag(this.name, [this.className]);
+
+ @override
+ String toString() {
+ final cls = className != null ? ' class="$className"' : '';
+ return '<$name$cls>$text';
+ }
+}
+
+mixin MixinTag {}
diff --git a/bin/builder/file_format/formats/json_format.dart b/bin/builder/file_format/formats/json_format.dart
new file mode 100644
index 0000000..b4060e4
--- /dev/null
+++ b/bin/builder/file_format/formats/json_format.dart
@@ -0,0 +1,16 @@
+import 'dart:convert';
+
+import '../color_text_reader.dart';
+
+class JsonFormat extends TextFormat {
+ @override
+ String get content => jsonEncode([
+ {
+ 'text': 'some text',
+ },
+ {
+ 'text': 'some text',
+ 'color': 'red',
+ }
+ ],);
+}
diff --git a/bin/builder/file_format/main.dart b/bin/builder/file_format/main.dart
new file mode 100644
index 0000000..79f59c0
--- /dev/null
+++ b/bin/builder/file_format/main.dart
@@ -0,0 +1,20 @@
+import 'converters/html_converter.dart';
+import 'converters/json_converter.dart';
+import 'color_text_reader.dart';
+import 'converters/console_converter.dart';
+
+void main() {
+ final reader = ColorTextReader(
+ text: 'I love looking at the blue sky, '
+ 'eating red apples, '
+ 'sitting on the green grass.',
+ );
+ final html = reader.convert(HtmlConverter());
+ final json = reader.convert(JsonConverter());
+ final console = reader.convert(ConsoleConverter());
+ print(
+ '$html,\n\n'
+ '$json,\n\n'
+ '$console,\n\n',
+ );
+}
From de1a3e976c0a0c71b0ba939562bceb8225240ee9 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Thu, 9 Dec 2021 03:29:16 +0200
Subject: [PATCH 005/479] Add JsonConverter
---
.../file_format/color_text_reader.dart | 2 +-
.../converters/json_converter.dart | 21 ++++++++++++++-----
.../file_format/formats/console_format.dart | 2 +-
.../file_format/formats/json_format.dart | 16 +++++++-------
4 files changed, 25 insertions(+), 16 deletions(-)
diff --git a/bin/builder/file_format/color_text_reader.dart b/bin/builder/file_format/color_text_reader.dart
index 999cf61..df21d9a 100644
--- a/bin/builder/file_format/color_text_reader.dart
+++ b/bin/builder/file_format/color_text_reader.dart
@@ -33,6 +33,6 @@ abstract class TextFormat {
@override
String toString() => '$runtimeType(\n'
- ' $content'
+ '$content'
'\n)';
}
diff --git a/bin/builder/file_format/converters/json_converter.dart b/bin/builder/file_format/converters/json_converter.dart
index 71a14f5..c8c4f52 100644
--- a/bin/builder/file_format/converters/json_converter.dart
+++ b/bin/builder/file_format/converters/json_converter.dart
@@ -3,15 +3,26 @@ import '../color_text_reader.dart';
class JsonConverter extends Converter {
@override
- final result = JsonFormat();
+ JsonFormat get result => _json;
+
+ final _json = JsonFormat();
@override
void writeColor(String color) {
- throw UnimplementedError();
+ if (_textBuffer.isNotEmpty) {
+ _json.add({
+ 'text': _textBuffer.toString(),
+ });
+ _textBuffer.clear();
+ }
+ _json.add({
+ 'text': color,
+ 'color': color,
+ });
}
+ final _textBuffer = StringBuffer();
+
@override
- void writeWord(String text) {
- throw UnimplementedError();
- }
+ void writeWord(String text) => _textBuffer.write('$text ');
}
diff --git a/bin/builder/file_format/formats/console_format.dart b/bin/builder/file_format/formats/console_format.dart
index 018d8cc..5f9b20b 100644
--- a/bin/builder/file_format/formats/console_format.dart
+++ b/bin/builder/file_format/formats/console_format.dart
@@ -1,7 +1,7 @@
import '../color_text_reader.dart';
class ConsoleFormat extends TextFormat {
- final _buff = [];
+ final _buff = [' '];
static const colors = {
'red': '\x1b[31m',
diff --git a/bin/builder/file_format/formats/json_format.dart b/bin/builder/file_format/formats/json_format.dart
index b4060e4..b7fefb3 100644
--- a/bin/builder/file_format/formats/json_format.dart
+++ b/bin/builder/file_format/formats/json_format.dart
@@ -3,14 +3,12 @@ import 'dart:convert';
import '../color_text_reader.dart';
class JsonFormat extends TextFormat {
+ final _list =
-),
-
-JsonFormat(
+```
+```json
[
{
"text": "I love looking at the "
@@ -69,9 +76,6 @@ JsonFormat(
"color": "green"
}
]
-),
-
-ConsoleFormat(
- I love looking at the blue sky, eating red apples, sitting on the green grass.
-),
```
+
+
From b7cdfb2d7c6cbe2b0e14a291d6d5363802145ea9 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Mon, 24 Jan 2022 12:22:25 +0200
Subject: [PATCH 126/479] Fix json format.
---
patterns/builder/color_text_format/README.md | 6 ++---
.../converters/json_converter.dart | 26 ++++++++++++-------
patterns/builder/color_text_format/main.dart | 2 ++
3 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/patterns/builder/color_text_format/README.md b/patterns/builder/color_text_format/README.md
index ee5765e..fd8707a 100644
--- a/patterns/builder/color_text_format/README.md
+++ b/patterns/builder/color_text_format/README.md
@@ -58,22 +58,22 @@ main() {
"text": "I love looking at the "
},
{
- "text": "blue",
"color": "blue"
},
{
"text": "sky, eating "
},
{
- "text": "red",
"color": "red"
},
{
"text": "apples, sitting on the "
},
{
- "text": "green",
"color": "green"
+ },
+ {
+ "text": "grass. "
}
]
```
diff --git a/patterns/builder/color_text_format/converters/json_converter.dart b/patterns/builder/color_text_format/converters/json_converter.dart
index ab288de..f87a096 100644
--- a/patterns/builder/color_text_format/converters/json_converter.dart
+++ b/patterns/builder/color_text_format/converters/json_converter.dart
@@ -3,26 +3,32 @@ import 'converter.dart';
class JsonConverter extends Converter {
@override
- JsonFormat get result => _json;
+ JsonFormat get result {
+ _closeTextBuffer();
+ return _json;
+ }
final _json = JsonFormat();
@override
void writeColor(String color) {
- if (_textBuffer.isNotEmpty) {
- _json.add({
- 'text': _textBuffer.toString(),
- });
- _textBuffer.clear();
- }
+ _closeTextBuffer();
_json.add({
- 'text': color,
'color': color,
});
}
- final _textBuffer = StringBuffer();
-
@override
void writeWord(String text) => _textBuffer.write('$text ');
+
+ final _textBuffer = StringBuffer();
+
+ void _closeTextBuffer() {
+ if (_textBuffer.isNotEmpty) {
+ _json.add({
+ 'text': _textBuffer.toString(),
+ });
+ _textBuffer.clear();
+ }
+ }
}
diff --git a/patterns/builder/color_text_format/main.dart b/patterns/builder/color_text_format/main.dart
index a5ef75b..f74347f 100644
--- a/patterns/builder/color_text_format/main.dart
+++ b/patterns/builder/color_text_format/main.dart
@@ -9,9 +9,11 @@ void main() {
'eating red apples, '
'sitting on the green grass.',
);
+
final html = reader.convert(HtmlConverter());
final json = reader.convert(JsonConverter());
final console = reader.convert(ConsoleConverter());
+
print(
'$html,\n\n'
'$json,\n\n'
From e42a93d7985a70bd62d47a5836ec06324d2e29e0 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Mon, 24 Jan 2022 12:32:57 +0200
Subject: [PATCH 127/479] Reformatting "Prototype" pattern.
---
patterns/prototype/shapes/README.md | 36 ++++++++++++++++++++++++++++-
patterns/prototype/shapes/main.dart | 5 ++++
2 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/patterns/prototype/shapes/README.md b/patterns/prototype/shapes/README.md
index 97894a1..4c51949 100644
--- a/patterns/prototype/shapes/README.md
+++ b/patterns/prototype/shapes/README.md
@@ -1,10 +1,44 @@
# Prototype pattern
+Prototype is a creational design pattern that lets you copy existing objects without making your
+code dependent on their classes.
+## Copies of geometric objects. Example.
**Description:**
https://refactoring.guru/design-patterns/prototype?#pseudocode
-**Output:**
+## Client code:
+```dart
+void main() {
+ final originalShapes = [
+ Rectangle(
+ x: 100,
+ y: 100,
+ width: 500,
+ height: 100,
+ color: '0xfff',
+ ),
+ Circle(
+ x: 20,
+ y: 30,
+ radius: 100,
+ color: '0xf0f',
+ ),
+ ];
+
+ businessLogic(originalShapes);
+}
+
+void businessLogic(List originalShapes) {
+ final cloningShapes = [
+ for (final shape in originalShapes) shape.clone(),
+ ];
+ print('Origin shapes: $originalShapes');
+ print('Cloning shapes: $cloningShapes');
+}
+```
+
+**Output:**
```
Origin shapes: [Rectangle(address: 0x2f7f544c), Circle(address: 0x3e59b487)]
Cloning shapes: [Rectangle(address: 0x3eb0a110), Circle(address: 0x75e3636)]
diff --git a/patterns/prototype/shapes/main.dart b/patterns/prototype/shapes/main.dart
index c1f7af4..078411f 100644
--- a/patterns/prototype/shapes/main.dart
+++ b/patterns/prototype/shapes/main.dart
@@ -1,5 +1,6 @@
import 'shape/circle.dart';
import 'shape/rectangle.dart';
+import 'shape/shape.dart';
void main() {
final originalShapes = [
@@ -18,6 +19,10 @@ void main() {
),
];
+ businessLogic(originalShapes);
+}
+
+void businessLogic(List originalShapes) {
final cloningShapes = [
for (final shape in originalShapes) shape.clone(),
];
From ee69830c4b3aa353e836c8a2b83408b05ac01842 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Mon, 24 Jan 2022 12:43:27 +0200
Subject: [PATCH 128/479] Add description to "Adapter" pattern.
---
patterns/adapter/text_graphics/README.md | 8 +++++++-
patterns/adapter/text_graphics/main.dart | 4 ++--
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/patterns/adapter/text_graphics/README.md b/patterns/adapter/text_graphics/README.md
index df037cd..81c5424 100644
--- a/patterns/adapter/text_graphics/README.md
+++ b/patterns/adapter/text_graphics/README.md
@@ -1,8 +1,14 @@
# Adapter pattern
+Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.
+**General description of the pattern:** https://refactoring.guru/design-patterns/adapter
+## Example:
+Adding shapes from a third-party library.
+
+### Class Diagram:

-**Client code:**
+### Client code:
```dart
final renderContent = ShapeEngine(
width: 42,
diff --git a/patterns/adapter/text_graphics/main.dart b/patterns/adapter/text_graphics/main.dart
index 661fe4d..28adfca 100644
--- a/patterns/adapter/text_graphics/main.dart
+++ b/patterns/adapter/text_graphics/main.dart
@@ -46,6 +46,6 @@ void main() {
graphElement: third_party.Star(size: 20),
),
],
- ).render();
- print(renderContent);
+ );
+ print(renderContent.render());
}
From 162f43b4db2bb2c67d5701944e4b24d39b9b147a Mon Sep 17 00:00:00 2001
From: ilopX
Date: Mon, 24 Jan 2022 12:49:55 +0200
Subject: [PATCH 129/479] Add description & reformat square-round-conflict.
---
patterns/adapter/square_round_conflict/README.md | 12 +++++-------
patterns/adapter/square_round_conflict/main.dart | 3 +++
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/patterns/adapter/square_round_conflict/README.md b/patterns/adapter/square_round_conflict/README.md
index 37976d4..e6aa8e6 100644
--- a/patterns/adapter/square_round_conflict/README.md
+++ b/patterns/adapter/square_round_conflict/README.md
@@ -1,14 +1,13 @@
# Adapter pattern
+Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.
-**Diagram:**
+## Square and Round conflict example.
+**Description:** https://refactoring.guru/design-patterns/adapter?#pseudocode
+### Class Diagram:

-**Description:**
-https://refactoring.guru/design-patterns/adapter?#pseudocode
-
-**Client code:**
-
+### Client code:
```dart
void main() {
final hole = RoundHole(5);
@@ -33,7 +32,6 @@ void main() {
```
**Output:**
-
```
Round peg r5 fits round hole r5.
Square peg w2 fits round hole r5.
diff --git a/patterns/adapter/square_round_conflict/main.dart b/patterns/adapter/square_round_conflict/main.dart
index 483f158..aed8fb3 100644
--- a/patterns/adapter/square_round_conflict/main.dart
+++ b/patterns/adapter/square_round_conflict/main.dart
@@ -7,6 +7,7 @@ void main() {
// RU: Круглое к круглому — всё работает.
final hole = RoundHole(5);
final peg = RoundPeg(5);
+
if (hole.fits(peg)) {
print("Round peg r5 fits round hole r5.");
}
@@ -22,9 +23,11 @@ void main() {
// RU: Адаптер решит проблему.
final smallSqPegAdapter = SquarePegAdapter(smallSqPeg);
final largeSqPegAdapter = SquarePegAdapter(largeSqPeg);
+
if (hole.fits(smallSqPegAdapter)) {
print("Square peg w2 fits round hole r5.");
}
+
if (!hole.fits(largeSqPegAdapter)) {
print("Square peg w20 does not fit into round hole r5.");
}
From 9ef758b89da41b2a25debf805148f87d4604d3f8 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Mon, 24 Jan 2022 13:05:39 +0200
Subject: [PATCH 130/479] Fix "Clock" example README.
---
patterns/bridge/clock/README.md | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/patterns/bridge/clock/README.md b/patterns/bridge/clock/README.md
index df3a16c..51878dc 100644
--- a/patterns/bridge/clock/README.md
+++ b/patterns/bridge/clock/README.md
@@ -1,11 +1,14 @@
# Bridge pattern
-The idea for the bridge pattern example is taken from [here](https://habr.com/ru/post/85137/).
+Bridge is a structural design pattern that lets you split a large class or a set of closely related
+classes into two separate hierarchies—abstraction and implementation—which can be developed independently of each other.
-**Diagram:**
+## Example
+The idea for the bridge pattern example is taken from [here](https://habr.com/ru/post/85137/).
+### Diagram:

-**Client code:**
+### Client code:
```dart
void main() {
final melody = Melody();
From 46a14830c3ea7906c1413ef3ba684d50202caa07 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Mon, 24 Jan 2022 13:20:51 +0200
Subject: [PATCH 131/479] Fix "Shapes" README.
---
patterns/prototype/shapes/README.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/patterns/prototype/shapes/README.md b/patterns/prototype/shapes/README.md
index 4c51949..f4b77d3 100644
--- a/patterns/prototype/shapes/README.md
+++ b/patterns/prototype/shapes/README.md
@@ -2,7 +2,9 @@
Prototype is a creational design pattern that lets you copy existing objects without making your
code dependent on their classes.
-## Copies of geometric objects. Example.
+## Example
+Copies of geometric objects.
+
**Description:**
https://refactoring.guru/design-patterns/prototype?#pseudocode
From 9a9a9ca396bd864d4fc788deeae8c301eb92c3fa Mon Sep 17 00:00:00 2001
From: ilopX
Date: Mon, 24 Jan 2022 14:01:24 +0200
Subject: [PATCH 132/479] Raise the constructor to the very top of the class.
---
.../composite/products_and_boxes/diagram/diagram.dart | 4 ++--
.../composite/products_and_boxes/products/box.dart | 4 ++--
.../products_and_boxes/products/product_leaf.dart | 4 ++--
.../render_elements/render_connecting_lines.dart | 6 +++---
.../render_elements/render_layout.dart | 10 +++++-----
.../render_elements/render_position.dart | 8 ++++----
.../render_elements/render_text.dart | 4 ++--
7 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/patterns/composite/products_and_boxes/diagram/diagram.dart b/patterns/composite/products_and_boxes/diagram/diagram.dart
index ef6502b..097ab8b 100644
--- a/patterns/composite/products_and_boxes/diagram/diagram.dart
+++ b/patterns/composite/products_and_boxes/diagram/diagram.dart
@@ -7,10 +7,10 @@ import '../render_elements/render_element.dart';
import 'convert_product_to_render_element.dart';
class Diagram {
- final RenderElement rootRenderElement;
-
Diagram(this.rootRenderElement);
+ final RenderElement rootRenderElement;
+
String renderToText() {
const pixelWidth = 3;
final width = (rootRenderElement.width / pixelWidth).ceil();
diff --git a/patterns/composite/products_and_boxes/products/box.dart b/patterns/composite/products_and_boxes/products/box.dart
index 353eb93..db2c88a 100644
--- a/patterns/composite/products_and_boxes/products/box.dart
+++ b/patterns/composite/products_and_boxes/products/box.dart
@@ -2,10 +2,10 @@ import '../diagram/diagram.dart';
import 'product.dart';
class Box implements Product {
- final List children;
-
Box({required this.children});
+ final List children;
+
@override
String get content {
final places = size > 1 ? "places: $size, " : "";
diff --git a/patterns/composite/products_and_boxes/products/product_leaf.dart b/patterns/composite/products_and_boxes/products/product_leaf.dart
index 314d555..30ca1eb 100644
--- a/patterns/composite/products_and_boxes/products/product_leaf.dart
+++ b/patterns/composite/products_and_boxes/products/product_leaf.dart
@@ -2,6 +2,8 @@ import '../diagram/diagram.dart';
import 'product.dart';
class ProductLeaf implements Product {
+ ProductLeaf(this.name, this.price);
+
@override
String get content => '$name($price\$)';
@@ -13,8 +15,6 @@ class ProductLeaf implements Product {
@override
int get size => 1;
- ProductLeaf(this.name, this.price);
-
@override
Diagram toDiagram() {
return Diagram.node(this);
diff --git a/patterns/composite/products_and_boxes/render_elements/render_connecting_lines.dart b/patterns/composite/products_and_boxes/render_elements/render_connecting_lines.dart
index fce154f..1f3d0cc 100644
--- a/patterns/composite/products_and_boxes/render_elements/render_connecting_lines.dart
+++ b/patterns/composite/products_and_boxes/render_elements/render_connecting_lines.dart
@@ -10,9 +10,6 @@ import 'render_layout.dart';
import 'render_text.dart';
class RenderConnectingLines extends RenderElement {
- late final RenderLayout _child;
- late final _Lines _lines;
-
RenderConnectingLines({
required RenderElement parent,
required List children,
@@ -30,6 +27,9 @@ class RenderConnectingLines extends RenderElement {
_lines = _Lines(_child.positions.first, row.positions);
}
+ late final RenderLayout _child;
+ late final _Lines _lines;
+
@override
int get height => _child.height;
diff --git a/patterns/composite/products_and_boxes/render_elements/render_layout.dart b/patterns/composite/products_and_boxes/render_elements/render_layout.dart
index 3dc9d08..7fa4706 100644
--- a/patterns/composite/products_and_boxes/render_elements/render_layout.dart
+++ b/patterns/composite/products_and_boxes/render_elements/render_layout.dart
@@ -6,6 +6,11 @@ import 'render_element.dart';
import 'render_position.dart';
abstract class RenderLayout extends RenderElement {
+ RenderLayout({
+ required this.children,
+ this.space = 3,
+ });
+
late final List positions = compute();
List compute();
@@ -13,11 +18,6 @@ abstract class RenderLayout extends RenderElement {
final List children;
final int space;
- RenderLayout({
- required this.children,
- this.space = 3,
- });
-
int get childWidth => children.fold(
0, (width, renderElement) => width + renderElement.width);
diff --git a/patterns/composite/products_and_boxes/render_elements/render_position.dart b/patterns/composite/products_and_boxes/render_elements/render_position.dart
index 28bc7ed..3c90b88 100644
--- a/patterns/composite/products_and_boxes/render_elements/render_position.dart
+++ b/patterns/composite/products_and_boxes/render_elements/render_position.dart
@@ -5,16 +5,16 @@ import 'package:design_patterns_dart/text_canvas/canvas.dart';
import 'render_element.dart';
class RenderPosition extends RenderElement {
- final int x;
- final int y;
- final RenderElement child;
-
RenderPosition({
required this.x,
required this.y,
required this.child,
});
+ final int x;
+ final int y;
+ final RenderElement child;
+
@override
int get height => child.height;
diff --git a/patterns/composite/products_and_boxes/render_elements/render_text.dart b/patterns/composite/products_and_boxes/render_elements/render_text.dart
index 3bb3abb..e7d8d1c 100644
--- a/patterns/composite/products_and_boxes/render_elements/render_text.dart
+++ b/patterns/composite/products_and_boxes/render_elements/render_text.dart
@@ -4,11 +4,11 @@ import 'package:design_patterns_dart/text_canvas/primitives.dart';
import 'render_element.dart';
class RenderText extends RenderElement {
+ RenderText(this.text, {required this.borderStyle});
+
final String text;
final BorderStyle borderStyle;
- RenderText(this.text, {required this.borderStyle});
-
@override
int get width => text.length + 2 + 2;
From 4d2f5eab42bcd5ff4eab154d4aba492cceef31c3 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Mon, 24 Jan 2022 14:02:05 +0200
Subject: [PATCH 133/479] Reformat text_graphics.
---
patterns/adapter/text_graphics/README.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/patterns/adapter/text_graphics/README.md b/patterns/adapter/text_graphics/README.md
index 81c5424..25fd1fd 100644
--- a/patterns/adapter/text_graphics/README.md
+++ b/patterns/adapter/text_graphics/README.md
@@ -1,6 +1,8 @@
# Adapter pattern
Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.
-**General description of the pattern:** https://refactoring.guru/design-patterns/adapter
+
+**General description of the pattern:**
+https://refactoring.guru/design-patterns/adapter
## Example:
Adding shapes from a third-party library.
From 40926f823d29caef2ce8741d41bbadb4e46d24c7 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Mon, 24 Jan 2022 14:03:20 +0200
Subject: [PATCH 134/479] Replace header2 to header3.
---
.../server_middleware/README.md | 8 ++++----
patterns/composite/products_and_boxes/README.md | 13 ++++++-------
2 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/patterns/chain_of_responsibility/server_middleware/README.md b/patterns/chain_of_responsibility/server_middleware/README.md
index 7563881..aa38dc3 100644
--- a/patterns/chain_of_responsibility/server_middleware/README.md
+++ b/patterns/chain_of_responsibility/server_middleware/README.md
@@ -4,16 +4,16 @@ Chain of Responsibility is a behavioral design pattern that lets you pass reques
## Server middleware example

-## Problem description:
+### Problem description:
https://refactoring.guru/design-patterns/chain-of-responsibility?#problem
-## Origin source code:
+### Origin source code:
This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/master/src/refactoring_guru/chain_of_responsibility/example)
-## Diagram:
+### Diagram:

-## Client code:
+### Client code:
```dart
void main() {
final server = Server(
diff --git a/patterns/composite/products_and_boxes/README.md b/patterns/composite/products_and_boxes/README.md
index 2b6d21b..4d3c647 100644
--- a/patterns/composite/products_and_boxes/README.md
+++ b/patterns/composite/products_and_boxes/README.md
@@ -1,22 +1,21 @@
# Composite pattern
+Composite is a structural design pattern that lets you compose objects into tree structures and then
+work with these structures as if they were individual objects.
+## Problem description:

-
-**Problem description:**
-
https://refactoring.guru/design-patterns/composite?#problem
-**Folder description:**
-
+### Folder description:
- `/products` - represent product and box (composite pattern)
- `/diagram` - convert products to render elements
- `/render_elements` - classes for visualization (real-world composite pattern)
-**Diagram:**
+### Diagram:

-**Client code:**
+### Client code:
```dart
main() {
Box(
From 382fb09e1d794dd536183bc32148aa66bac4578c Mon Sep 17 00:00:00 2001
From: ilopX
Date: Mon, 24 Jan 2022 14:07:35 +0200
Subject: [PATCH 135/479] Add render method for ImageEditor.
---
patterns/composite/image_editor/editor/image_editor.dart | 4 +++-
patterns/composite/image_editor/main.dart | 4 +++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/patterns/composite/image_editor/editor/image_editor.dart b/patterns/composite/image_editor/editor/image_editor.dart
index 5c6de06..802b27e 100644
--- a/patterns/composite/image_editor/editor/image_editor.dart
+++ b/patterns/composite/image_editor/editor/image_editor.dart
@@ -12,9 +12,11 @@ class ImageEditor {
_allShapes
..clear()
..addAll(shapes);
+ }
+ String render() {
final graphics = Graphics(_allShapes.width + 2, _allShapes.height + 2);
_allShapes.paint(graphics);
- print(graphics.toString());
+ return graphics.toString();
}
}
diff --git a/patterns/composite/image_editor/main.dart b/patterns/composite/image_editor/main.dart
index dc62a6a..e9e4bdd 100644
--- a/patterns/composite/image_editor/main.dart
+++ b/patterns/composite/image_editor/main.dart
@@ -13,7 +13,8 @@ void main() {
CompoundShape([
Circle(12, 12, 6, Color.dark),
Dot(12 + 6, 12 + 6, Color.dark),
- ])..select(),
+ ])
+ ..select(),
CompoundShape([
Rectangle(31, 31, 10, 10, Color.black),
Dot(28, 28, Color.grey),
@@ -22,4 +23,5 @@ void main() {
Dot(28, 41, Color.grey),
]),
]);
+ print(editor.render());
}
From 365108fd41d813ec9431b32e9a11a6d6ceae3648 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Mon, 24 Jan 2022 14:11:29 +0200
Subject: [PATCH 136/479] Move description to top.
---
patterns/decorator/data_source_decoder/README.md | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/patterns/decorator/data_source_decoder/README.md b/patterns/decorator/data_source_decoder/README.md
index c50ea2e..7300eb0 100644
--- a/patterns/decorator/data_source_decoder/README.md
+++ b/patterns/decorator/data_source_decoder/README.md
@@ -1,13 +1,14 @@
# Pattern Decorator
Decorator is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors.
-## Diagram:
+## Example Description:
+https://refactoring.guru/design-patterns/decorator?#pseudocode
+
+### Diagram:

-## Description:
-https://refactoring.guru/design-patterns/decorator?#pseudocode
-## Client code:
+### Client code:
```dart
void main() {
final records = 'Name,Salary\nJohn Smith,100000\nSteven Jobs,912000';
@@ -30,7 +31,7 @@ void main() {
}
```
-## Output:
+**Output:**
```
- Input ----------------
Name,Salary
From d05c4b341dbd44f392d3992f026231a9b2d46392 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Mon, 24 Jan 2022 14:14:58 +0200
Subject: [PATCH 137/479] Add diagram to prototype.
---
patterns/prototype/shapes/README.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/patterns/prototype/shapes/README.md b/patterns/prototype/shapes/README.md
index f4b77d3..f5f794c 100644
--- a/patterns/prototype/shapes/README.md
+++ b/patterns/prototype/shapes/README.md
@@ -8,7 +8,11 @@ Copies of geometric objects.
**Description:**
https://refactoring.guru/design-patterns/prototype?#pseudocode
-## Client code:
+### Class Diagram:
+
+
+
+### Client code:
```dart
void main() {
final originalShapes = [
From 6d145e989c25664945b2f756990ed1daec731100 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Mon, 24 Jan 2022 14:23:02 +0200
Subject: [PATCH 138/479] Bump version 0.12.19.
---
CHANGELOG.md | 3 +++
pubspec.yaml | 2 +-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1eb5f13..5b058cf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+## 0.12.19
+- Refactoring: reformatting and minor changes
+
## 0.12.5
- Put "Shapes" prototype pattern to "Shapes" folder.
- The list of patterns has been updated. Added links to projects.
diff --git a/pubspec.yaml b/pubspec.yaml
index 5c0ea4c..6822ac1 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.12.5
+version: 0.12.19
homepage: https://refactoring.guru/design-patterns
repository: https://github.com/RefactoringGuru/design-patterns-dart
issue_tracker: https://github.com/RefactoringGuru/design-patterns-dart/issue
From f7886f062e381c257b10d6464bb5a3332ce875b4 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Tue, 25 Jan 2022 14:07:04 +0200
Subject: [PATCH 139/479] Add "Observer" pattern. Official example from java.
---
.../open_close_editor_events/README.md | 44 +++++++++++++++++++
.../editor/editor.dart | 22 ++++++++++
.../event_manager/event_manager.dart | 38 ++++++++++++++++
.../email_notification_listener.dart | 16 +++++++
.../listeners/event_listener.dart | 5 +++
.../listeners/log_open_listener.dart | 16 +++++++
.../open_close_editor_events/main.dart | 23 ++++++++++
7 files changed, 164 insertions(+)
create mode 100644 patterns/observer/open_close_editor_events/README.md
create mode 100644 patterns/observer/open_close_editor_events/editor/editor.dart
create mode 100644 patterns/observer/open_close_editor_events/event_manager/event_manager.dart
create mode 100644 patterns/observer/open_close_editor_events/listeners/email_notification_listener.dart
create mode 100644 patterns/observer/open_close_editor_events/listeners/event_listener.dart
create mode 100644 patterns/observer/open_close_editor_events/listeners/log_open_listener.dart
create mode 100644 patterns/observer/open_close_editor_events/main.dart
diff --git a/patterns/observer/open_close_editor_events/README.md b/patterns/observer/open_close_editor_events/README.md
new file mode 100644
index 0000000..fa69f69
--- /dev/null
+++ b/patterns/observer/open_close_editor_events/README.md
@@ -0,0 +1,44 @@
+# Observer pattern
+Observer is a behavioral design pattern that lets you define a subscription mechanism to notify
+multiple objects about any events that happen to the object they’re observing.
+
+## Editor example
+In this example, the Observer pattern lets the text editor object notify other service objects about
+changes in its state.
+
+More detailed explanation on [RefactoringGuru](https://refactoring.guru/design-patterns/observer?#pseudocode).
+
+### Origin source code:
+This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/master/src/refactoring_guru/observer/example).
+
+### Diagram:
+
+
+### Client code:
+```dart
+void main() {
+ final editor = Editor();
+ editor.events
+ ..subscribe(
+ 'open',
+ LogOpenListener('log.txt'),
+ )
+ ..subscribe(
+ 'save',
+ EmailNotificationListener('admin@example.com'),
+ );
+
+ try {
+ editor.openFile('test.txt');
+ editor.saveFile();
+ } catch (e) {
+ print(e);
+ }
+}
+```
+
+**Output:**
+```
+Save to log "log.txt": Someone has performed "open" operation with the following file: "test.txt"
+Email to "admin@example.com": Someone has performed "save" operation with the following file: "test.txt"
+```
diff --git a/patterns/observer/open_close_editor_events/editor/editor.dart b/patterns/observer/open_close_editor_events/editor/editor.dart
new file mode 100644
index 0000000..3411655
--- /dev/null
+++ b/patterns/observer/open_close_editor_events/editor/editor.dart
@@ -0,0 +1,22 @@
+import 'dart:io';
+
+import '../event_manager/event_manager.dart';
+
+class Editor {
+ final events = EventManager(['open', 'save']);
+
+ File? _file;
+
+ void openFile(String filePath) {
+ _file = File(filePath);
+ events.notify("open", _file!);
+ }
+
+ void saveFile() {
+ if (_file == null) {
+ throw Exception('Please open a file first.');
+ }
+
+ events.notify('save', _file!);
+ }
+}
diff --git a/patterns/observer/open_close_editor_events/event_manager/event_manager.dart b/patterns/observer/open_close_editor_events/event_manager/event_manager.dart
new file mode 100644
index 0000000..50d2371
--- /dev/null
+++ b/patterns/observer/open_close_editor_events/event_manager/event_manager.dart
@@ -0,0 +1,38 @@
+import 'dart:io';
+
+import '../listeners/event_listener.dart';
+
+class EventManager {
+ final _listeners = >{};
+
+ EventManager(List operations) {
+ for (final operation in operations) {
+ _listeners[operation] = [];
+ }
+ }
+
+ void subscribe(String eventType, EventListener listener) {
+ _usersBy(eventType).add(listener);
+ }
+
+ void unsubscribe(String eventType, EventListener listener) {
+ _usersBy(eventType).remove(listener);
+ }
+
+ void notify(String eventType, File file) {
+ final users = _usersBy(eventType);
+ for (final listener in users) {
+ listener.update(eventType, file);
+ }
+ }
+
+ List _usersBy(String eventType) {
+ final users = _listeners[eventType];
+
+ if (users == null) {
+ throw UnsupportedError('Event type "$eventType" do not support.');
+ }
+
+ return users;
+ }
+}
diff --git a/patterns/observer/open_close_editor_events/listeners/email_notification_listener.dart b/patterns/observer/open_close_editor_events/listeners/email_notification_listener.dart
new file mode 100644
index 0000000..0936e4f
--- /dev/null
+++ b/patterns/observer/open_close_editor_events/listeners/email_notification_listener.dart
@@ -0,0 +1,16 @@
+import 'dart:io';
+
+import 'event_listener.dart';
+
+class EmailNotificationListener implements EventListener {
+ String email;
+
+ EmailNotificationListener(this.email);
+
+ @override
+ void update(String eventType, File file) {
+ print('Email to "$email": '
+ 'Someone has performed "$eventType" '
+ 'operation with the following file: "${file.path}"');
+ }
+}
diff --git a/patterns/observer/open_close_editor_events/listeners/event_listener.dart b/patterns/observer/open_close_editor_events/listeners/event_listener.dart
new file mode 100644
index 0000000..252d64a
--- /dev/null
+++ b/patterns/observer/open_close_editor_events/listeners/event_listener.dart
@@ -0,0 +1,5 @@
+import 'dart:io';
+
+abstract class EventListener {
+ void update(String eventType, File file);
+}
diff --git a/patterns/observer/open_close_editor_events/listeners/log_open_listener.dart b/patterns/observer/open_close_editor_events/listeners/log_open_listener.dart
new file mode 100644
index 0000000..e44b1ae
--- /dev/null
+++ b/patterns/observer/open_close_editor_events/listeners/log_open_listener.dart
@@ -0,0 +1,16 @@
+import 'dart:io';
+
+import 'event_listener.dart';
+
+class LogOpenListener implements EventListener {
+ File logFile;
+
+ LogOpenListener(String logFileName) : logFile = File(logFileName);
+
+ @override
+ void update(String eventType, File file) {
+ print('Save to log "${logFile.path}": '
+ 'Someone has performed "$eventType" '
+ 'operation with the following file: "${file.path}"');
+ }
+}
diff --git a/patterns/observer/open_close_editor_events/main.dart b/patterns/observer/open_close_editor_events/main.dart
new file mode 100644
index 0000000..9951d3c
--- /dev/null
+++ b/patterns/observer/open_close_editor_events/main.dart
@@ -0,0 +1,23 @@
+import 'editor/editor.dart';
+import 'listeners/email_notification_listener.dart';
+import 'listeners/log_open_listener.dart';
+
+void main() {
+ final editor = Editor();
+ editor.events
+ ..subscribe(
+ 'open',
+ LogOpenListener('log.txt'),
+ )
+ ..subscribe(
+ 'save',
+ EmailNotificationListener('admin@example.com'),
+ );
+
+ try {
+ editor.openFile('test.txt');
+ editor.saveFile();
+ } catch (e) {
+ print(e);
+ }
+}
From e80a57b93a97f1cfc822c4d3a44a0f9bdf89675e Mon Sep 17 00:00:00 2001
From: ilopX
Date: Tue, 25 Jan 2022 14:10:42 +0200
Subject: [PATCH 140/479] Bump version 0.13.0.
---
CHANGELOG.md | 3 +++
README.md | 2 +-
pubspec.yaml | 2 +-
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5b058cf..4ab2f1e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+## 0.13.0
+- Add "Observer" pattern from official book, rewritten from Java example.
+
## 0.12.19
- Refactoring: reformatting and minor changes
diff --git a/README.md b/README.md
index 0e906c4..b28a31c 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ It contains **Dart** examples for all classic **GoF** design patterns.
- [ ] [**Iterator**](https://refactoring.guru/design-patterns/iterator)
- [ ] [**Mediator**](https://refactoring.guru/design-patterns/mediator)
- [ ] [**Memento**](https://refactoring.guru/design-patterns/memento)
- - [ ] [**Observer**](https://refactoring.guru/design-patterns/observer)
+ - [x] [**Observer**](https://refactoring.guru/design-patterns/observer) - [[Open-Close Editor Events](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/open_close_editor_events)]
- [ ] [**State**](https://refactoring.guru/design-patterns/state)
- [ ] [**Template Method**](https://refactoring.guru/design-patterns/template-method)
- [ ] [**Visitor**](https://refactoring.guru/design-patterns/visitor)
diff --git a/pubspec.yaml b/pubspec.yaml
index 6822ac1..662aa49 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.12.19
+version: 0.13.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
From d36abb86023cd2e70cc7ef9c170c0b95a66d9921 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Thu, 27 Jan 2022 12:15:05 +0200
Subject: [PATCH 141/479] Add conceptual "Memento" pattern example.
---
patterns/memento/conceptual/main.dart | 96 +++++++++++++++++++++++++++
1 file changed, 96 insertions(+)
create mode 100644 patterns/memento/conceptual/main.dart
diff --git a/patterns/memento/conceptual/main.dart b/patterns/memento/conceptual/main.dart
new file mode 100644
index 0000000..4590559
--- /dev/null
+++ b/patterns/memento/conceptual/main.dart
@@ -0,0 +1,96 @@
+void main() {
+ final editor = Editor('New Document');
+ final firstState = Command.makeBackup(editor);
+ editor.text += ' add text';
+ final secondState = Command.makeBackup(editor);
+
+ print('Current state: "${editor.text}"');
+
+ firstState.undo();
+ print('First state: "${editor.text}"');
+
+ secondState.undo();
+ print('Second state: "${editor.text}"');
+}
+
+/// EN: The originator holds some important data that may change over
+/// time. It also defines a method for saving its state inside a
+/// memento and another method for restoring the state from it.
+///
+/// RU: Класс создателя должен иметь специальный метод, который.
+/// сохраняет состояние создателя в новом объекте-снимке.
+class Editor {
+ var text = '';
+ var curX = 0;
+ var curY = 0;
+ var selectionWidth = 0;
+
+ Editor(this.text);
+
+ /// EN: Saves the current state inside a memento.
+ Snapshot createSnapshot() {
+ /// EN: Memento is an immutable object; that's why the
+ /// originator passes its state to the memento's
+ /// constructor parameters.
+ ///
+ /// RU: Снимок — неизменяемый объект, поэтому Создатель
+ /// передаёт все своё состояние через параметры
+ /// конструктора.
+ return Snapshot(this, text, curX, curY, selectionWidth);
+ }
+}
+
+/// EN: The memento class stores the past state of the editor.
+///
+/// RU: Снимок хранит прошлое состояние редактора.
+class Snapshot {
+ final Editor _editor;
+ final String _text;
+ final int _curX;
+ final int _curY;
+ final int _selectionWidth;
+
+ Snapshot(
+ this._editor,
+ this._text,
+ this._curX,
+ this._curY,
+ this._selectionWidth,
+ );
+
+ /// EN: At some point, a previous state of the editor can be
+ /// restored using a memento object.
+ ///
+ /// RU: В нужный момент владелец снимка может восстановить
+ /// состояние редактора.
+ void restore() {
+ _editor
+ ..text = _text
+ ..curX = _curX
+ ..curY = _curY
+ ..selectionWidth = _selectionWidth;
+ }
+}
+
+/// EN: A command object can act as a caretaker. In that case, the
+/// command gets a memento just before it changes the
+/// originator's state. When undo is requested, it restores the
+/// originator's state from a memento.
+///
+/// RU: Опекуном может выступать класс команд (см. паттерн Команда).
+/// В этом случае команда сохраняет снимок состояния объекта-
+/// получателя, перед тем как передать ему своё действие. А в
+/// случае отмены команда вернёт объект в прежнее состояние.
+class Command {
+ Snapshot _backup;
+
+ Command._(this._backup);
+
+ factory Command.makeBackup(Editor editor) {
+ return Command._(editor.createSnapshot());
+ }
+
+ void undo() {
+ _backup.restore();
+ }
+}
From 0b54d26b8d6c0615b1265d781fd332600732cf41 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Thu, 27 Jan 2022 13:45:40 +0200
Subject: [PATCH 142/479] Add README to "Memento" conceptual example.
---
patterns/memento/conceptual/README.md | 39 +++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
create mode 100644 patterns/memento/conceptual/README.md
diff --git a/patterns/memento/conceptual/README.md b/patterns/memento/conceptual/README.md
new file mode 100644
index 0000000..45811db
--- /dev/null
+++ b/patterns/memento/conceptual/README.md
@@ -0,0 +1,39 @@
+# Memento pattern
+Memento is a behavioral design pattern that lets you save and restore the previous state of an
+object without revealing the details of its implementation.
+
+Tutorial: [here](https://refactoring.guru/design-patterns/memento).
+
+## Conceptual Editor example
+This example uses the Memento pattern alongside the Command pattern for storing snapshots of the
+complex text editor’s state and restoring an earlier state from these snapshots when needed.
+
+More detailed explanation on [RefactoringGuru](https://refactoring.guru/design-patterns/memento?#pseudocode).
+
+### Diagram:
+
+
+### Client code:
+```dart
+void main() {
+ final editor = Editor('New Document');
+ final firstState = Command.makeBackup(editor);
+ editor.text += ' add text';
+ final secondState = Command.makeBackup(editor);
+
+ print('Current state: "${editor.text}"');
+
+ firstState.undo();
+ print('First state: "${editor.text}"');
+
+ secondState.undo();
+ print('Second state: "${editor.text}"');
+}
+```
+
+**Output:**
+```
+Current state: "New Document add text"
+First state: "New Document"
+Second state: "New Document add text"
+```
From 6e22bca748726dfb62c174505dd79a73f0e12a72 Mon Sep 17 00:00:00 2001
From: ilopX
Date: Thu, 27 Jan 2022 13:49:17 +0200
Subject: [PATCH 143/479] Bump version 0.14.0
---
CHANGELOG.md | 13 ++++++++-----
README.md | 2 +-
pubspec.yaml | 2 +-
3 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4ab2f1e..203262a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,15 +1,18 @@
+## 0.14.0
+- Add "Memento" conceptual pattern
+
## 0.13.0
-- Add "Observer" pattern from official book, rewritten from Java example.
+- Add "Observer" pattern from official book, rewritten from Java example
## 0.12.19
- Refactoring: reformatting and minor changes
## 0.12.5
-- Put "Shapes" prototype pattern to "Shapes" folder.
-- The list of patterns has been updated. Added links to projects.
+- Put "Shapes" prototype pattern to "Shapes" folder
+- The list of patterns has been updated. Added links to projects
## 0.12.0
-- Add "Command" pattern from official book, rewritten from Java example.
+- Add "Command" pattern from official book, rewritten from Java example
## 0.11.0
- Add "Chain of Responsibility" pattern from official book, rewritten from Java example
@@ -35,7 +38,7 @@
- Add bridge pattern. Device remote control
## 0.5.5
-- Add example "graphics engine" and "square round conflict" for adapter pattern
+- Add example "graphics engine" and "square round conflict" for adapter patter
- Add description to prototype pattern
- Fix class diagram for text graph
- Add description Builder pattern, text formats
diff --git a/README.md b/README.md
index b28a31c..8f0052c 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ It contains **Dart** examples for all classic **GoF** design patterns.
- [ ] Interpreter
- [ ] [**Iterator**](https://refactoring.guru/design-patterns/iterator)
- [ ] [**Mediator**](https://refactoring.guru/design-patterns/mediator)
- - [ ] [**Memento**](https://refactoring.guru/design-patterns/memento)
+ - [x] [**Memento**](https://refactoring.guru/design-patterns/memento) [[Conceptual](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/memento/conceptual)]
- [x] [**Observer**](https://refactoring.guru/design-patterns/observer) - [[Open-Close Editor Events](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/open_close_editor_events)]
- [ ] [**State**](https://refactoring.guru/design-patterns/state)
- [ ] [**Template Method**](https://refactoring.guru/design-patterns/template-method)
diff --git a/pubspec.yaml b/pubspec.yaml
index 662aa49..26fbabd 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.13.0
+version: 0.14.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
From b28efb90dff608a4ed3c79e0d7c181849a7e545a Mon Sep 17 00:00:00 2001
From: ilopX
Date: Fri, 28 Jan 2022 12:17:55 +0200
Subject: [PATCH 144/479] Add empty flutter launcher.
---
.gitignore | 1 +
bin/main.dart | 20 ++++++
pubspec.yaml | 4 ++
web/favicon.png | Bin 0 -> 917 bytes
web/icons/Icon-192.png | Bin 0 -> 5292 bytes
web/icons/Icon-512.png | Bin 0 -> 8252 bytes
web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes
web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes
web/index.html | 104 ++++++++++++++++++++++++++++++++
web/manifest.json | 35 +++++++++++
10 files changed, 164 insertions(+)
create mode 100644 bin/main.dart
create mode 100644 web/favicon.png
create mode 100644 web/icons/Icon-192.png
create mode 100644 web/icons/Icon-512.png
create mode 100644 web/icons/Icon-maskable-192.png
create mode 100644 web/icons/Icon-maskable-512.png
create mode 100644 web/index.html
create mode 100644 web/manifest.json
diff --git a/.gitignore b/.gitignore
index d111b3a..c282350 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
.dart_tool/
.idea/
build/
+windows/
.packages
pubspec.lock
diff --git a/bin/main.dart b/bin/main.dart
new file mode 100644
index 0000000..3865bb7
--- /dev/null
+++ b/bin/main.dart
@@ -0,0 +1,20 @@
+import 'package:flutter/material.dart';
+
+void main() {
+ runApp(const MyApp());
+}
+
+class MyApp extends StatelessWidget {
+ const MyApp({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: 'Refactoring Guru: Flutter launcher',
+ theme: ThemeData(
+ primarySwatch: Colors.pink
+ ),
+ home: Container(),
+ );
+ }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 26fbabd..6c85b87 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -10,6 +10,10 @@ environment:
dependencies:
collection: ^1.15.0
+ flutter:
+ sdk: flutter
dev_dependencies:
lints: ^1.0.0
+
+
diff --git a/web/favicon.png b/web/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1
GIT binary patch
literal 917
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7
zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2
zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK
zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o
z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo
zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8
zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr
zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf
zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od
z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz
oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM
literal 0
HcmV?d00001
diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png
new file mode 100644
index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa
GIT binary patch
literal 5292
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
literal 0
HcmV?d00001
diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png
new file mode 100644
index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6
GIT binary patch
literal 8252
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