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. + +Creative Commons 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(' '); + 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 = >[]; + + void add(Map item) { + _list.add(item); + } + @override - String get content => jsonEncode([ - { - 'text': 'some text', - }, - { - 'text': 'some text', - 'color': 'red', - } - ],); + String get content => JsonEncoder.withIndent(' ').convert(_list); } From a807909f779d10522841b50fb43f35b8efe5f891 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 9 Dec 2021 03:38:52 +0200 Subject: [PATCH 006/479] Bump version 0.2.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 f793aee..30b80db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.2.0 +Add the Builder pattern using file format converters as an example + ## 0.1.0 - Add pattern Prototype diff --git a/README.md b/README.md index 9a98a91..4d7229e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ It contains **Dart** examples for all classic GoF design patterns. # Implementation checklist: - [ ] **Creation** - [ ] Abstract Factory - - [ ] Builder + - [x] Builder - [ ] Factory Method - [x] Prototype - [ ] Singleton diff --git a/pubspec.yaml b/pubspec.yaml index bb7e424..a5111f0 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.1.0 +version: 0.2.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 725a36845db38149fe5d972641ff4e0c5427995e Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 9 Dec 2021 03:43:09 +0200 Subject: [PATCH 007/479] Add output.txt file for Builder pattern --- bin/builder/file_format/output.txt | 50 ++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 bin/builder/file_format/output.txt diff --git a/bin/builder/file_format/output.txt b/bin/builder/file_format/output.txt new file mode 100644 index 0000000..65d84e4 --- /dev/null +++ b/bin/builder/file_format/output.txt @@ -0,0 +1,50 @@ +HtmlFormat( + + + + Color text + + + +

+ I love looking at the + blue sky, eating + red apples, sitting on the + green grass.

+ + +), + +JsonFormat( +[ + { + "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" + } +] +), + +ConsoleFormat( + I love looking at the blue sky, eating red apples, sitting on the green grass. +), From d9300ddd48a476cc8e5691e8aca0a513a995bfa3 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 9 Dec 2021 16:09:08 +0200 Subject: [PATCH 008/479] Add the ability to return a specific TextFormat object * Put ColorTextReader to separate dir --- .../file_format/{ => color_reader}/color_text_reader.dart | 2 +- bin/builder/file_format/converters/console_converter.dart | 2 +- bin/builder/file_format/converters/html_converter.dart | 2 +- bin/builder/file_format/converters/json_converter.dart | 2 +- bin/builder/file_format/formats/console_format.dart | 2 +- bin/builder/file_format/formats/html_format.dart | 2 +- bin/builder/file_format/formats/json_format.dart | 2 +- bin/builder/file_format/main.dart | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) rename bin/builder/file_format/{ => color_reader}/color_text_reader.dart (91%) diff --git a/bin/builder/file_format/color_text_reader.dart b/bin/builder/file_format/color_reader/color_text_reader.dart similarity index 91% rename from bin/builder/file_format/color_text_reader.dart rename to bin/builder/file_format/color_reader/color_text_reader.dart index df21d9a..953328c 100644 --- a/bin/builder/file_format/color_text_reader.dart +++ b/bin/builder/file_format/color_reader/color_text_reader.dart @@ -4,7 +4,7 @@ class ColorTextReader { ColorTextReader({required this.text}); - TextFormat convert(Converter converter) { + T convert(Converter converter) { for (final word in text.split(' ')) { if (supportedColors.contains(word)) { converter.writeColor(word); diff --git a/bin/builder/file_format/converters/console_converter.dart b/bin/builder/file_format/converters/console_converter.dart index dd74523..d384873 100644 --- a/bin/builder/file_format/converters/console_converter.dart +++ b/bin/builder/file_format/converters/console_converter.dart @@ -1,5 +1,5 @@ import '../formats/console_format.dart'; -import '../color_text_reader.dart'; +import '../color_reader/color_text_reader.dart'; class ConsoleConverter extends Converter { @override diff --git a/bin/builder/file_format/converters/html_converter.dart b/bin/builder/file_format/converters/html_converter.dart index e989e9f..3887a32 100644 --- a/bin/builder/file_format/converters/html_converter.dart +++ b/bin/builder/file_format/converters/html_converter.dart @@ -1,5 +1,5 @@ import '../formats/html_format.dart'; -import '../color_text_reader.dart'; +import '../color_reader/color_text_reader.dart'; /// Builder class HtmlConverter extends Converter { diff --git a/bin/builder/file_format/converters/json_converter.dart b/bin/builder/file_format/converters/json_converter.dart index c8c4f52..763c5a4 100644 --- a/bin/builder/file_format/converters/json_converter.dart +++ b/bin/builder/file_format/converters/json_converter.dart @@ -1,5 +1,5 @@ import '../formats/json_format.dart'; -import '../color_text_reader.dart'; +import '../color_reader/color_text_reader.dart'; class JsonConverter extends Converter { @override diff --git a/bin/builder/file_format/formats/console_format.dart b/bin/builder/file_format/formats/console_format.dart index 5f9b20b..2400a3f 100644 --- a/bin/builder/file_format/formats/console_format.dart +++ b/bin/builder/file_format/formats/console_format.dart @@ -1,4 +1,4 @@ -import '../color_text_reader.dart'; +import '../color_reader/color_text_reader.dart'; class ConsoleFormat extends TextFormat { final _buff = [' ']; diff --git a/bin/builder/file_format/formats/html_format.dart b/bin/builder/file_format/formats/html_format.dart index 2f287c1..023db16 100644 --- a/bin/builder/file_format/formats/html_format.dart +++ b/bin/builder/file_format/formats/html_format.dart @@ -1,6 +1,6 @@ import 'dart:collection'; -import '../color_text_reader.dart'; +import '../color_reader/color_text_reader.dart'; // product class HtmlFormat extends TextFormat { diff --git a/bin/builder/file_format/formats/json_format.dart b/bin/builder/file_format/formats/json_format.dart index b7fefb3..fe6871e 100644 --- a/bin/builder/file_format/formats/json_format.dart +++ b/bin/builder/file_format/formats/json_format.dart @@ -1,6 +1,6 @@ import 'dart:convert'; -import '../color_text_reader.dart'; +import '../color_reader/color_text_reader.dart'; class JsonFormat extends TextFormat { final _list = >[]; diff --git a/bin/builder/file_format/main.dart b/bin/builder/file_format/main.dart index 79f59c0..3d54677 100644 --- a/bin/builder/file_format/main.dart +++ b/bin/builder/file_format/main.dart @@ -1,7 +1,7 @@ import 'converters/html_converter.dart'; import 'converters/json_converter.dart'; -import 'color_text_reader.dart'; import 'converters/console_converter.dart'; +import 'color_reader/color_text_reader.dart'; void main() { final reader = ColorTextReader( From 1a5609f5591b099b415c9e47fd1a08601a12d981 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 10 Dec 2021 00:48:55 +0200 Subject: [PATCH 009/479] Add the original Builder pattern from book example --- .../book_example/builders/builder.dart | 23 ++++++++ .../book_example/builders/car_builder.dart | 35 ++++++++++++ .../builders/car_manual_builder.dart | 45 ++++++++++++++++ bin/builder/book_example/cars/car.dart | 35 ++++++++++++ bin/builder/book_example/cars/car_manual.dart | 53 +++++++++++++++++++ bin/builder/book_example/cars/car_type.dart | 5 ++ .../book_example/components/engine.dart | 28 ++++++++++ .../book_example/director/director.dart | 44 +++++++++++++++ .../book_example/director/gps_navigation.dart | 16 ++++++ .../book_example/director/transmission.dart | 9 ++++ .../book_example/director/trip_computer.dart | 21 ++++++++ bin/builder/book_example/main.dart | 38 +++++++++++++ 12 files changed, 352 insertions(+) create mode 100644 bin/builder/book_example/builders/builder.dart create mode 100644 bin/builder/book_example/builders/car_builder.dart create mode 100644 bin/builder/book_example/builders/car_manual_builder.dart create mode 100644 bin/builder/book_example/cars/car.dart create mode 100644 bin/builder/book_example/cars/car_manual.dart create mode 100644 bin/builder/book_example/cars/car_type.dart create mode 100644 bin/builder/book_example/components/engine.dart create mode 100644 bin/builder/book_example/director/director.dart create mode 100644 bin/builder/book_example/director/gps_navigation.dart create mode 100644 bin/builder/book_example/director/transmission.dart create mode 100644 bin/builder/book_example/director/trip_computer.dart create mode 100644 bin/builder/book_example/main.dart diff --git a/bin/builder/book_example/builders/builder.dart b/bin/builder/book_example/builders/builder.dart new file mode 100644 index 0000000..3360805 --- /dev/null +++ b/bin/builder/book_example/builders/builder.dart @@ -0,0 +1,23 @@ +import '../cars/car_type.dart'; +import '../components/engine.dart'; +import '../director/gps_navigation.dart'; +import '../director/transmission.dart'; +import '../director/trip_computer.dart'; + +/// EN: Builder interface defines all possible ways to configure a product. +/// +/// RU: Интерфейс Строителя объявляет все возможные этапы и шаги конфигурации +/// продукта. +abstract class Builder { + set carType(CarType type); + + set seats(int seats); + + set engine(Engine engine); + + set transmission(Transmission transmission); + + set tripComputer(TripComputer tripComputer); + + set gpsNavigator(GPSNavigator gpsNavigator); +} diff --git a/bin/builder/book_example/builders/car_builder.dart b/bin/builder/book_example/builders/car_builder.dart new file mode 100644 index 0000000..19868ed --- /dev/null +++ b/bin/builder/book_example/builders/car_builder.dart @@ -0,0 +1,35 @@ +import '../cars/car.dart'; +import '../cars/car_type.dart'; +import '../components/engine.dart'; +import '../director/gps_navigation.dart'; +import '../director/transmission.dart'; +import '../director/trip_computer.dart'; +import 'builder.dart'; + +/// EN: Concrete builders implement steps defined in the common interface. +/// +/// RU: Конкретные строители реализуют шаги, объявленные в общем интерфейсе. +class CarBuilder implements Builder { + @override + CarType? carType; + + @override + int? seats; + + @override + Engine? engine; + + @override + Transmission? transmission; + + @override + TripComputer? tripComputer; + + @override + GPSNavigator? gpsNavigator; + + Car getResult() { + return Car( + carType!, seats!, engine!, transmission!, tripComputer!, gpsNavigator!); + } +} diff --git a/bin/builder/book_example/builders/car_manual_builder.dart b/bin/builder/book_example/builders/car_manual_builder.dart new file mode 100644 index 0000000..a8fdd62 --- /dev/null +++ b/bin/builder/book_example/builders/car_manual_builder.dart @@ -0,0 +1,45 @@ +import '../cars/car_manual.dart'; +import '../cars/car_type.dart'; +import '../components/engine.dart'; +import '../director/gps_navigation.dart'; +import '../director/transmission.dart'; +import '../director/trip_computer.dart'; +import 'builder.dart'; + +/// EN: Unlike other creational patterns, Builder can construct unrelated +/// products, which don't have the common interface. +/// +/// In this case we build a user manual for a car, using the same steps as we +/// built a car. This allows to produce manuals for specific car models, +/// configured with different features. +/// +/// RU: В отличие от других создающих паттернов, Строители могут создавать +/// совершенно разные продукты, не имеющие общего интерфейса. +/// +/// В данном случае мы производим руководство пользователя автомобиля с помощью +/// тех же шагов, что и сами автомобили. Это устройство позволит создавать +/// руководства под конкретные модели автомобилей, содержащие те или иные фичи. +class CarManualBuilder implements Builder { + @override + CarType? carType; + + @override + int? seats; + + @override + Engine? engine; + + @override + Transmission? transmission; + + @override + TripComputer? tripComputer; + + @override + GPSNavigator? gpsNavigator; + + Manual getResult() { + return Manual( + carType!, seats!, engine!, transmission!, tripComputer!, gpsNavigator!); + } +} diff --git a/bin/builder/book_example/cars/car.dart b/bin/builder/book_example/cars/car.dart new file mode 100644 index 0000000..7740b80 --- /dev/null +++ b/bin/builder/book_example/cars/car.dart @@ -0,0 +1,35 @@ +import '../components/engine.dart'; +import '../director/gps_navigation.dart'; +import '../director/transmission.dart'; +import '../director/trip_computer.dart'; +import 'car_type.dart'; + +/// EN: Car is a product class. +/// +/// RU: Автомобиль — это класс продукта. +class Car { + final CarType carType; + + final int seats; + + final Engine engine; + + final Transmission transmission; + + final TripComputer tripComputer; + + final GPSNavigator gpsNavigator; + + final double fuel = 0.0; + + Car( + this.carType, + this.seats, + this.engine, + this.transmission, + this.tripComputer, + this.gpsNavigator, + ) { + tripComputer.car = this; + } +} diff --git a/bin/builder/book_example/cars/car_manual.dart b/bin/builder/book_example/cars/car_manual.dart new file mode 100644 index 0000000..ec04cca --- /dev/null +++ b/bin/builder/book_example/cars/car_manual.dart @@ -0,0 +1,53 @@ +import '../components/engine.dart'; +import '../director/gps_navigation.dart'; +import '../director/transmission.dart'; +import '../director/trip_computer.dart'; +import 'car_type.dart'; + +/// EN: Car manual is another product. Note that it does not have the same +/// ancestor as a Car. They are not related. +/// +/// RU: Руководство автомобиля — это второй продукт. Заметьте, что руководство и +/// сам автомобиль не имеют общего родительского класса. По сути, они независимы. + +class Manual { + CarType carType; + + final int seats; + + final Engine engine; + + final Transmission transmission; + + final TripComputer? tripComputer; + + final GPSNavigator? gpsNavigator; + + Manual( + this.carType, + this.seats, + this.engine, + this.transmission, [ + this.tripComputer, + this.gpsNavigator, + ]); + + String print() { + var info = ''; + info += 'Type of car: $carType\n'; + info += 'Count of seats: $seats\n'; + info += 'Engine: volume - ${engine.volume}; mileage - ${engine.mileage}\n'; + info += 'Transmission: $transmission\n'; + if (tripComputer != null) { + info += 'Trip Computer: Functional\n'; + } else { + info += 'Trip Computer: N/A\n'; + } + if (gpsNavigator != null) { + info += 'GPS Navigator: Functional\n'; + } else { + info += 'GPS Navigator: N/A\n'; + } + return info; + } +} diff --git a/bin/builder/book_example/cars/car_type.dart b/bin/builder/book_example/cars/car_type.dart new file mode 100644 index 0000000..39a8c44 --- /dev/null +++ b/bin/builder/book_example/cars/car_type.dart @@ -0,0 +1,5 @@ +enum CarType { + cityCar, + sportCar, + suv, +} diff --git a/bin/builder/book_example/components/engine.dart b/bin/builder/book_example/components/engine.dart new file mode 100644 index 0000000..bd7f242 --- /dev/null +++ b/bin/builder/book_example/components/engine.dart @@ -0,0 +1,28 @@ +/// EN: Just another feature of a car. +/// +/// RU: Одна из фишек автомобиля. +class Engine { + final double volume; + + double _mileage; + + double get mileage => _mileage; + + var _started = false; + + Engine(this.volume, this._mileage); + + void on() => _started = true; + + void off() => _started = false; + + bool get isStarted => _started; + + void go(double mileage) { + if (_started) { + _mileage += mileage; + } else { + print('Cannot go(), you must start engine first!'); + } + } +} diff --git a/bin/builder/book_example/director/director.dart b/bin/builder/book_example/director/director.dart new file mode 100644 index 0000000..1588bf2 --- /dev/null +++ b/bin/builder/book_example/director/director.dart @@ -0,0 +1,44 @@ +import '../builders/builder.dart'; +import '../cars/car_type.dart'; +import '../components/engine.dart'; +import 'gps_navigation.dart'; +import 'transmission.dart'; +import 'trip_computer.dart'; + +/// EN: Director defines the order of building steps. It works with a builder +/// object through common Builder interface. Therefore it may not know what +/// product is being built. +/// +/// RU: Директор знает в какой последовательности заставлять работать строителя. +/// Он работает с ним через общий интерфейс Строителя. Из-за этого, он может не +/// знать какой конкретно продукт сейчас строится. +class Director { + void constructSportsCar(Builder builder) { + builder + ..carType = CarType.sportCar + ..seats = 2 + ..engine = Engine(3.0, 0) + ..transmission = Transmission.semiAutomatic + ..tripComputer = TripComputer() + ..gpsNavigator = GPSNavigator(); + } + + void constructCityCar(Builder builder) { + builder + ..carType = CarType.cityCar + ..seats = 2 + ..engine = Engine(1.2, 0) + ..transmission = Transmission.automatic + ..tripComputer = TripComputer() + ..gpsNavigator = GPSNavigator(); + } + + void constructSUV(Builder builder) { + builder + ..carType = CarType.suv + ..seats = 4 + ..engine = Engine(2.5, 0) + ..transmission = Transmission.manual + ..gpsNavigator = GPSNavigator(); + } +} diff --git a/bin/builder/book_example/director/gps_navigation.dart b/bin/builder/book_example/director/gps_navigation.dart new file mode 100644 index 0000000..144860a --- /dev/null +++ b/bin/builder/book_example/director/gps_navigation.dart @@ -0,0 +1,16 @@ +/// EN: Just another feature of a car. +/// +/// RU: Одна из фишек автомобиля. +class GPSNavigator { + late String _route; + + GPSNavigator() + : _route = "221b, Baker Street, London " + "to Scotland Yard, 8-10 Broadway, London"; + + GPSNavigator.fromRout(String manualRoute) { + _route = manualRoute; + } + + String get root => _route; +} diff --git a/bin/builder/book_example/director/transmission.dart b/bin/builder/book_example/director/transmission.dart new file mode 100644 index 0000000..311bc5a --- /dev/null +++ b/bin/builder/book_example/director/transmission.dart @@ -0,0 +1,9 @@ +/// EN: Just another feature of a car. +/// +/// RU: Одна из фишек автомобиля. +enum Transmission { + singleSpeed, + manual, + automatic, + semiAutomatic, +} diff --git a/bin/builder/book_example/director/trip_computer.dart b/bin/builder/book_example/director/trip_computer.dart new file mode 100644 index 0000000..341e318 --- /dev/null +++ b/bin/builder/book_example/director/trip_computer.dart @@ -0,0 +1,21 @@ +import '../cars/car.dart'; + +/// EN: Just another feature of a car. +/// +/// RU: Одна из фишек автомобиля. +class TripComputer { + Car? car; + + void showFuelLevel() { + print("Fuel level: ${car?.fuel}"); + } + + void showStatus() { + final engine = car?.engine; + if (engine != null && engine.isStarted) { + print("Car is started"); + } else { + print("Car isn't started"); + } + } +} diff --git a/bin/builder/book_example/main.dart b/bin/builder/book_example/main.dart new file mode 100644 index 0000000..a7a3bc3 --- /dev/null +++ b/bin/builder/book_example/main.dart @@ -0,0 +1,38 @@ +import 'builders/car_builder.dart'; +import 'builders/car_manual_builder.dart'; +import 'director/director.dart'; + +/// EN: Use-case. Everything comes together here. +/// +/// RU: Пример использования. Здесь всё сводится воедино. +void main() { + final director = Director(); + + // EN: Director gets the concrete builder object from the client + // (application code). That's because application knows better which + // builder to use to get a specific product. + // + // RU: Директор получает объект конкретного строителя от клиента + // (приложения). Приложение само знает какой строитель использовать, + // чтобы получить нужный продукт. + final builder = CarBuilder(); + director.constructSportsCar(builder); + + // EN: The final product is often retrieved from a builder object, since + // Director is not aware and not dependent on concrete builders and + // products. + // + // RU: Готовый продукт возвращает строитель, так как Директор чаще всего + // не знает и не зависит от конкретных классов строителей и продуктов. + final car = builder.getResult(); + print('Car built:\n${car.carType}\n'); + + final manualBuilder = CarManualBuilder(); + + // EN: Director may know several building recipes. + // + // RU: Директор может знать больше одного рецепта строительства + director.constructSportsCar(manualBuilder); + final carManual = manualBuilder.getResult(); + print("Car manual built:\n" + carManual.print()); +} From c417a9965b2469527c6c1cc55222d7c0dade41bd Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 11 Dec 2021 02:14:15 +0200 Subject: [PATCH 010/479] Add an example graphics engine for the adapter pattern --- bin/adapter/shape_engine/README.md | 35 ++++++ bin/adapter/shape_engine/engine/canvas.dart | 114 ++++++++++++++++++ .../shape_engine/engine/primitives.dart | 21 ++++ .../shape_engine/engine/shape_engine.dart | 26 ++++ bin/adapter/shape_engine/main.dart | 50 ++++++++ bin/adapter/shape_engine/shape/shape.dart | 42 +++++++ .../shape_engine/shape/shape_adapter.dart | 24 ++++ .../graph_element.dart | 5 + .../third_party_graphics_lib/star.dart | 29 +++++ .../third_party_graphics_lib/wave.dart | 24 ++++ 10 files changed, 370 insertions(+) create mode 100644 bin/adapter/shape_engine/README.md create mode 100644 bin/adapter/shape_engine/engine/canvas.dart create mode 100644 bin/adapter/shape_engine/engine/primitives.dart create mode 100644 bin/adapter/shape_engine/engine/shape_engine.dart create mode 100644 bin/adapter/shape_engine/main.dart create mode 100644 bin/adapter/shape_engine/shape/shape.dart create mode 100644 bin/adapter/shape_engine/shape/shape_adapter.dart create mode 100644 bin/adapter/shape_engine/third_party_graphics_lib/graph_element.dart create mode 100644 bin/adapter/shape_engine/third_party_graphics_lib/star.dart create mode 100644 bin/adapter/shape_engine/third_party_graphics_lib/wave.dart diff --git a/bin/adapter/shape_engine/README.md b/bin/adapter/shape_engine/README.md new file mode 100644 index 0000000..038d2d3 --- /dev/null +++ b/bin/adapter/shape_engine/README.md @@ -0,0 +1,35 @@ +**Output:** +``` +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░███████████████████████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░███░░░░░░ ░░░░░░███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░███░░░░░░░░░░░░░░░░░░░░░░░░ +░░░███░░░░░░ ░░░░░░░░░░░░░░░ ░░░░░░███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░███░░░░░░░░░░░░░░░░░░░░░░░░ +░░░███░░░░░░ ░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░███░░░░░░░░░░░░░░░░░░░░░░░░ +░░░███░░░░░░ ░░░░░░░░░▒▒▒▒▒▒ ░░░░░░███░░░▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░ +░░░█████████ ██████▒▒▒██████ █████████░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░███████████████████████████████████████████████████░░░ +░░░░░░░░░░░░ ░░░▒▒▒░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░███░░░░░░░░░░░░███░░░░░░░░░███░░░░░░░░░░░░███░░░░░░ +░░░░░░░░░░░░ ░░░▒▒▒░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░███░░░░░░███░░░░░░░░░░░░░░░███░░░░░░███░░░░░░░░░ +░░░░░░░░░░░░ ▒▒▒░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░█████████░░░░░░░░░░░░░░░█████████░░░░░░░░░░░░ +░░░░░░░░░░░░ ▒▒▒░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░ ▒▒▒ ░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░███░░░███░░░░░░░░░███░░░███░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░███░░░░░░███░░░███░░░░░░███░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░███░░░░░░░░░███░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░███░░░███░░░░░░░░░███░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░███░░░██████░░░░░░░░░██████░░░███░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░██████░░░░░░░░░░░░░░░░░░░░░██████░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒░░░░░░░░░░░░░░░▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░██████░░░░░░░░░░░░░░░░░░░░░░░░░░░██████░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓▓▓▓░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +``` diff --git a/bin/adapter/shape_engine/engine/canvas.dart b/bin/adapter/shape_engine/engine/canvas.dart new file mode 100644 index 0000000..acaded2 --- /dev/null +++ b/bin/adapter/shape_engine/engine/canvas.dart @@ -0,0 +1,114 @@ +import 'dart:typed_data'; + +import 'primitives.dart'; + +class Canvas { + final int width; + final int height; + late List _pixel; + final int lineStretch; + + Canvas( + this.width, + this.height, [ + this.lineStretch = 3, + ]) { + final realWidth = width * lineStretch; + _pixel = [ + for (var i = 0; i < height; i++) + Int16List.fromList( + List.filled(realWidth, Color.light.code), + ), + ]; + } + + var translate = Point(0, 0); + var penColor = Color.black; + + void setPixel(int x, int y) { + x += translate.x; + y += translate.y; + + if (x < 0 || x > width - 1 || y < 0 || y > height - 1) { + return; + } + + final realY = y; + final realX = (x * lineStretch) ~/ 1; + for (var i = 0; i < lineStretch; i++) { + _pixel[realY][realX + i] = penColor.code; + } + } + + var _currPos = Point(0, 0); + + void moveTo(int x, int y) => _currPos = Point(x, y); + + /// Bresenham's algorithm is taken from here + /// https://gist.github.com/bert/1085538#file-plot_line-c + void lineTo(final int x, final int y) { + final x1 = _currPos.x, y1 = _currPos.y; + + var x0 = x, y0 = y; + + final dx = (x1 - x0).abs(), + sx = x0 < x1 ? 1 : -1, + dy = -(y1 - y0).abs(), + sy = y0 < y1 ? 1 : -1; + + var err = dx + dy; + late int e2; + + while (true) { + setPixel(x0, y0); + if (x0 == x1 && y0 == y1) break; + e2 = 2 * err; + if (e2 >= dy) { + err += dy; + x0 += sx; + } + if (e2 <= dx) { + err += dx; + y0 += sy; + } + } + moveTo(x, y); + } + + void rectangle(int width, int height) { + final x = _currPos.x; + final y = _currPos.y; + lineTo(width + x, y); + lineTo(width + x, height + y); + lineTo(x, height + y); + lineTo(x, y); + } + + /// Bresenham's algorithm is taken from here + /// https://gist.github.com/bert/1085538#file-plot_circle-c + void circle(int radius) { + final xm = _currPos.x; + final ym = _currPos.y; + int x = -radius, y = 0, err = 2 - 2 * radius; + do { + setPixel(xm - x, ym + y); + setPixel(xm - y, ym - x); + setPixel(xm + x, ym - y); + setPixel(xm + y, ym + x); + radius = err; + if (radius > x) err += ++x * 2 + 1; + if (radius <= y) err += ++y * 2 + 1; + } while (x < 0); + } + + @override + String toString() { + return _pixel + .map((e) => e + .map( + (e) => String.fromCharCode(e), + ) + .join('')) + .join('\n'); + } +} diff --git a/bin/adapter/shape_engine/engine/primitives.dart b/bin/adapter/shape_engine/engine/primitives.dart new file mode 100644 index 0000000..24135c3 --- /dev/null +++ b/bin/adapter/shape_engine/engine/primitives.dart @@ -0,0 +1,21 @@ +class Color { + final int code; + + Color(String symbol) + : assert(symbol.length == 1), + code = symbol.codeUnitAt(0); + + static final Color black = Color('█'); + static final Color dark = Color('▓'); + static final Color grey = Color('▒'); + static final Color light = Color('░'); + static final Color white = Color(' '); + static final Color point = Color('■'); +} + +class Point { + final int x; + final int y; + + Point(this.x, this.y); +} diff --git a/bin/adapter/shape_engine/engine/shape_engine.dart b/bin/adapter/shape_engine/engine/shape_engine.dart new file mode 100644 index 0000000..33cb2ea --- /dev/null +++ b/bin/adapter/shape_engine/engine/shape_engine.dart @@ -0,0 +1,26 @@ +import '../shape/shape.dart'; +import 'canvas.dart'; +import 'primitives.dart'; + +class ShapeEngine { + final List shapes; + final int width; + final int height; + + ShapeEngine({ + required this.width, + required this.height, + required this.shapes, + }); + + String render() { + final can = Canvas(width, height, 3); + for (final Shape shape in shapes) { + can + ..translate = Point(shape.x, shape.y) + ..penColor = shape.color; + shape.draw(can); + } + return can.toString(); + } +} diff --git a/bin/adapter/shape_engine/main.dart b/bin/adapter/shape_engine/main.dart new file mode 100644 index 0000000..2bb8529 --- /dev/null +++ b/bin/adapter/shape_engine/main.dart @@ -0,0 +1,50 @@ +import 'engine/primitives.dart'; +import 'engine/shape_engine.dart'; +import 'shape/shape.dart'; +import 'shape/shape_adapter.dart'; +import 'third_party_graphics_lib/star.dart' as third_party; +import 'third_party_graphics_lib/wave.dart' as third_party; + +void main() { + final renderContent = ShapeEngine( + width: 42, + height: 32, + shapes: [ + // EN: Standard graphics primitives + Rectangle( + x: 1, + y: 1, + width: 12, + height: 6, + color: Color.black, + ), + Rectangle( + x: 4, + y: 3, + width: 6, + height: 9, + color: Color.white, + ), + Circle( + x: 12, + y: 12, + radius: 7, + color: Color.grey, + ), + // EN: Graphics primitives from a third party library + ShapeAdapter( + x: 11, + y: 20, + color: Color.dark, + graphElement: third_party.Wave(waveHeight: 5, points: 25), + ), + ShapeAdapter( + x: 23, + y: 1, + color: Color.black, + graphElement: third_party.Star(size: 20), + ), + ], + ).render(); + print(renderContent); +} diff --git a/bin/adapter/shape_engine/shape/shape.dart b/bin/adapter/shape_engine/shape/shape.dart new file mode 100644 index 0000000..09a61df --- /dev/null +++ b/bin/adapter/shape_engine/shape/shape.dart @@ -0,0 +1,42 @@ +import '../engine/canvas.dart'; +import '../engine/primitives.dart'; + +abstract class Shape { + final int x; + final int y; + final Color color; + + Shape(this.x, this.y, this.color); + + void draw(Canvas can); +} + +class Rectangle extends Shape { + final int width; + final int height; + + Rectangle({ + required int x, + required int y, + required this.width, + required this.height, + required Color color, + }) : super(x, y, color); + + @override + void draw(Canvas can) => can.rectangle(width, height); +} + +class Circle extends Shape { + final int radius; + + Circle({ + required int x, + required int y, + required this.radius, + required Color color, + }) : super(x, y, color); + + @override + void draw(Canvas can) => can.circle(radius); +} diff --git a/bin/adapter/shape_engine/shape/shape_adapter.dart b/bin/adapter/shape_engine/shape/shape_adapter.dart new file mode 100644 index 0000000..2189e7f --- /dev/null +++ b/bin/adapter/shape_engine/shape/shape_adapter.dart @@ -0,0 +1,24 @@ +import '../engine/canvas.dart'; +import '../engine/primitives.dart'; +import '../third_party_graphics_lib/graph_element.dart' as third_party; +import 'shape.dart'; + +class ShapeAdapter extends Shape { + final third_party.GraphElement graphElement; + + ShapeAdapter({ + required int x, + required int y, + required Color color, + required this.graphElement, + }) : super(x, y, color); + + @override + void draw(Canvas can) { + final points = graphElement.points; + can.moveTo(points[0], points[1]); + for (var i = 0; i < points.length; i += 2) { + can.lineTo(points[i], points[i + 1]); + } + } +} diff --git a/bin/adapter/shape_engine/third_party_graphics_lib/graph_element.dart b/bin/adapter/shape_engine/third_party_graphics_lib/graph_element.dart new file mode 100644 index 0000000..b011227 --- /dev/null +++ b/bin/adapter/shape_engine/third_party_graphics_lib/graph_element.dart @@ -0,0 +1,5 @@ +import 'dart:typed_data'; + +abstract class GraphElement { + Int32List get points; +} diff --git a/bin/adapter/shape_engine/third_party_graphics_lib/star.dart b/bin/adapter/shape_engine/third_party_graphics_lib/star.dart new file mode 100644 index 0000000..970d2e4 --- /dev/null +++ b/bin/adapter/shape_engine/third_party_graphics_lib/star.dart @@ -0,0 +1,29 @@ +import 'dart:typed_data'; + +import 'graph_element.dart'; + +class Star extends GraphElement { + final int size; + + Star({required this.size}) { + final list = []; + final p1 = (size * .1).toInt(), + p3 = (size * .3).toInt(), + p4 = (size * .4).toInt(), + p7 = (size * .7).toInt(), + p8 = (size * .8).toInt(), + p9 = (size * .9).toInt(); + list.addAll([p1, p9]); + list.addAll([p4, 0]); + list.addAll([p7, p9]); + list.addAll([0, p3]); + list.addAll([p8, p3]); + list.addAll([p1, p9]); + _points = Int32List.fromList(list); + } + + late Int32List _points; + + @override + Int32List get points => _points; +} diff --git a/bin/adapter/shape_engine/third_party_graphics_lib/wave.dart b/bin/adapter/shape_engine/third_party_graphics_lib/wave.dart new file mode 100644 index 0000000..3fa36f0 --- /dev/null +++ b/bin/adapter/shape_engine/third_party_graphics_lib/wave.dart @@ -0,0 +1,24 @@ +import 'dart:math'; +import 'dart:typed_data'; + +import 'graph_element.dart'; + +class Wave extends GraphElement { + Wave({ + required final int waveHeight, + final int points = 50, + final double waveStep = .8, + }) { + final list = []; + for (var x = 0; x < points; x++) { + final y = (waveHeight + cos(x / pi / waveStep) * waveHeight).toInt(); + list.addAll([x, y]); + } + _points = Int32List.fromList(list); + } + + late Int32List _points; + + @override + Int32List get points => _points; +} From 686b628e0b7a0d0a674b6682927669a7df1dd8f4 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 11 Dec 2021 17:02:02 +0200 Subject: [PATCH 011/479] Rename folder shape_engine to text_graphics --- bin/adapter/{shape_engine => text_graphics}/README.md | 0 bin/adapter/{shape_engine => text_graphics}/engine/canvas.dart | 0 .../{shape_engine => text_graphics}/engine/primitives.dart | 0 .../{shape_engine => text_graphics}/engine/shape_engine.dart | 2 +- bin/adapter/{shape_engine => text_graphics}/main.dart | 0 bin/adapter/{shape_engine => text_graphics}/shape/shape.dart | 0 .../{shape_engine => text_graphics}/shape/shape_adapter.dart | 0 .../third_party_graphics_lib/graph_element.dart | 0 .../third_party_graphics_lib/star.dart | 0 .../third_party_graphics_lib/wave.dart | 0 10 files changed, 1 insertion(+), 1 deletion(-) rename bin/adapter/{shape_engine => text_graphics}/README.md (100%) rename bin/adapter/{shape_engine => text_graphics}/engine/canvas.dart (100%) rename bin/adapter/{shape_engine => text_graphics}/engine/primitives.dart (100%) rename bin/adapter/{shape_engine => text_graphics}/engine/shape_engine.dart (91%) rename bin/adapter/{shape_engine => text_graphics}/main.dart (100%) rename bin/adapter/{shape_engine => text_graphics}/shape/shape.dart (100%) rename bin/adapter/{shape_engine => text_graphics}/shape/shape_adapter.dart (100%) rename bin/adapter/{shape_engine => text_graphics}/third_party_graphics_lib/graph_element.dart (100%) rename bin/adapter/{shape_engine => text_graphics}/third_party_graphics_lib/star.dart (100%) rename bin/adapter/{shape_engine => text_graphics}/third_party_graphics_lib/wave.dart (100%) diff --git a/bin/adapter/shape_engine/README.md b/bin/adapter/text_graphics/README.md similarity index 100% rename from bin/adapter/shape_engine/README.md rename to bin/adapter/text_graphics/README.md diff --git a/bin/adapter/shape_engine/engine/canvas.dart b/bin/adapter/text_graphics/engine/canvas.dart similarity index 100% rename from bin/adapter/shape_engine/engine/canvas.dart rename to bin/adapter/text_graphics/engine/canvas.dart diff --git a/bin/adapter/shape_engine/engine/primitives.dart b/bin/adapter/text_graphics/engine/primitives.dart similarity index 100% rename from bin/adapter/shape_engine/engine/primitives.dart rename to bin/adapter/text_graphics/engine/primitives.dart diff --git a/bin/adapter/shape_engine/engine/shape_engine.dart b/bin/adapter/text_graphics/engine/shape_engine.dart similarity index 91% rename from bin/adapter/shape_engine/engine/shape_engine.dart rename to bin/adapter/text_graphics/engine/shape_engine.dart index 33cb2ea..618175b 100644 --- a/bin/adapter/shape_engine/engine/shape_engine.dart +++ b/bin/adapter/text_graphics/engine/shape_engine.dart @@ -14,7 +14,7 @@ class ShapeEngine { }); String render() { - final can = Canvas(width, height, 3); + final can = Canvas(width, height, 2); for (final Shape shape in shapes) { can ..translate = Point(shape.x, shape.y) diff --git a/bin/adapter/shape_engine/main.dart b/bin/adapter/text_graphics/main.dart similarity index 100% rename from bin/adapter/shape_engine/main.dart rename to bin/adapter/text_graphics/main.dart diff --git a/bin/adapter/shape_engine/shape/shape.dart b/bin/adapter/text_graphics/shape/shape.dart similarity index 100% rename from bin/adapter/shape_engine/shape/shape.dart rename to bin/adapter/text_graphics/shape/shape.dart diff --git a/bin/adapter/shape_engine/shape/shape_adapter.dart b/bin/adapter/text_graphics/shape/shape_adapter.dart similarity index 100% rename from bin/adapter/shape_engine/shape/shape_adapter.dart rename to bin/adapter/text_graphics/shape/shape_adapter.dart diff --git a/bin/adapter/shape_engine/third_party_graphics_lib/graph_element.dart b/bin/adapter/text_graphics/third_party_graphics_lib/graph_element.dart similarity index 100% rename from bin/adapter/shape_engine/third_party_graphics_lib/graph_element.dart rename to bin/adapter/text_graphics/third_party_graphics_lib/graph_element.dart diff --git a/bin/adapter/shape_engine/third_party_graphics_lib/star.dart b/bin/adapter/text_graphics/third_party_graphics_lib/star.dart similarity index 100% rename from bin/adapter/shape_engine/third_party_graphics_lib/star.dart rename to bin/adapter/text_graphics/third_party_graphics_lib/star.dart diff --git a/bin/adapter/shape_engine/third_party_graphics_lib/wave.dart b/bin/adapter/text_graphics/third_party_graphics_lib/wave.dart similarity index 100% rename from bin/adapter/shape_engine/third_party_graphics_lib/wave.dart rename to bin/adapter/text_graphics/third_party_graphics_lib/wave.dart From 5fb89d7da25d9bb0acbcef7f54bfe056a7db7314 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 11 Dec 2021 17:04:07 +0200 Subject: [PATCH 012/479] Change lineStretch to 3 --- bin/adapter/text_graphics/engine/shape_engine.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/adapter/text_graphics/engine/shape_engine.dart b/bin/adapter/text_graphics/engine/shape_engine.dart index 618175b..33cb2ea 100644 --- a/bin/adapter/text_graphics/engine/shape_engine.dart +++ b/bin/adapter/text_graphics/engine/shape_engine.dart @@ -14,7 +14,7 @@ class ShapeEngine { }); String render() { - final can = Canvas(width, height, 2); + final can = Canvas(width, height, 3); for (final Shape shape in shapes) { can ..translate = Point(shape.x, shape.y) From 4c3d657f349a9bf714f5ef02564d6e100e726c48 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 11 Dec 2021 17:39:51 +0200 Subject: [PATCH 013/479] Add TextGraph class diagram --- bin/adapter/text_graphics/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/adapter/text_graphics/README.md b/bin/adapter/text_graphics/README.md index 038d2d3..2d20545 100644 --- a/bin/adapter/text_graphics/README.md +++ b/bin/adapter/text_graphics/README.md @@ -1,3 +1,5 @@ +![image](https://user-images.githubusercontent.com/8049534/145682370-a2cd3dfa-bac0-428e-8b6e-b16b85df27ac.png) + **Output:** ``` ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ From 8e05bd4b45c2fc98e824e418019582bae8f63000 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 11 Dec 2021 17:47:34 +0200 Subject: [PATCH 014/479] Bump version 0.3.0 --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30b80db..009b0a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.3.0 +Add an example graphics engine for the adapter pattern + ## 0.2.0 Add the Builder pattern using file format converters as an example diff --git a/pubspec.yaml b/pubspec.yaml index a5111f0..fbb258e 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.2.0 +version: 0.3.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 2974887327802587c61f64dfe5e127dcaafa8d67 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 12 Dec 2021 02:50:44 +0200 Subject: [PATCH 015/479] Add an adapter template, for example: conflict of round-squares --- bin/adapter/square_round_conflict/README.md | 12 +++++ bin/adapter/square_round_conflict/main.dart | 60 +++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 bin/adapter/square_round_conflict/README.md create mode 100644 bin/adapter/square_round_conflict/main.dart diff --git a/bin/adapter/square_round_conflict/README.md b/bin/adapter/square_round_conflict/README.md new file mode 100644 index 0000000..1a37840 --- /dev/null +++ b/bin/adapter/square_round_conflict/README.md @@ -0,0 +1,12 @@ +# Adapter pattern + +**Description:** +https://refactoring.guru/design-patterns/adapter?#pseudocode + +**Output:** + +``` +Round peg r5 fits round hole r5. +Square peg w2 fits round hole r5. +Square peg w20 does not fit into round hole r5. +``` diff --git a/bin/adapter/square_round_conflict/main.dart b/bin/adapter/square_round_conflict/main.dart new file mode 100644 index 0000000..3d5b7d2 --- /dev/null +++ b/bin/adapter/square_round_conflict/main.dart @@ -0,0 +1,60 @@ +/// EN: Somewhere in client code... +/// +/// RU: Где-то в клиентском коде... +void main() { + // EN: Round fits round, no surprise. + // + // RU: Круглое к круглому — всё работает. + final hole = RoundHole(5); + final rpeg = RoundPeg(5); + if (hole.fits(rpeg)) { + print("Round peg r5 fits round hole r5."); + } + + final smallSqPeg = SquarePeg(2); + final largeSqPeg = SquarePeg(20); + // EN: hole.fits(smallSqPeg); // Won't compile. + // + // RU: hole.fits(smallSqPeg); // Не скомпилируется. + + // EN: Adapter solves the problem. + // + // 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."); + } +} + +class RoundPeg { + final int radius; + + RoundPeg(this.radius); +} + +class SquarePegAdapter implements RoundPeg { + final SquarePeg squarePeg; + + SquarePegAdapter(this.squarePeg); + + @override + int get radius => squarePeg.width; +} + +class SquarePeg { + final int width; + + SquarePeg(this.width); +} + +class RoundHole { + final int radius; + + RoundHole(this.radius); + + bool fits(RoundPeg roundPeg) => radius >= roundPeg.radius; +} From 3debe44886e94c7336d88ce5dc6c9ab02ed03c45 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 12 Dec 2021 02:57:50 +0200 Subject: [PATCH 016/479] Add description to car builder pattern --- bin/builder/book_example/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 bin/builder/book_example/README.md diff --git a/bin/builder/book_example/README.md b/bin/builder/book_example/README.md new file mode 100644 index 0000000..61edc41 --- /dev/null +++ b/bin/builder/book_example/README.md @@ -0,0 +1,19 @@ +# Builder pattern + +**Description:** +https://refactoring.guru/design-patterns/builder?#pseudocode + +**Output:** + +``` +Car built: +CarType.sportCar + +Car manual built: +Type of car: CarType.sportCar +Count of seats: 2 +Engine: volume - 3.0; mileage - 0.0 +Transmission: Transmission.semiAutomatic +Trip Computer: Functional +GPS Navigator: Functional +``` From 4ff299d00eb18cd1109d5d76b971b8a20d298c46 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 12 Dec 2021 02:59:32 +0200 Subject: [PATCH 017/479] Rename builder pattern to cars --- bin/builder/{book_example => cars}/README.md | 0 bin/builder/{book_example => cars}/builders/builder.dart | 0 bin/builder/{book_example => cars}/builders/car_builder.dart | 0 .../{book_example => cars}/builders/car_manual_builder.dart | 0 bin/builder/{book_example => cars}/cars/car.dart | 0 bin/builder/{book_example => cars}/cars/car_manual.dart | 0 bin/builder/{book_example => cars}/cars/car_type.dart | 0 bin/builder/{book_example => cars}/components/engine.dart | 0 bin/builder/{book_example => cars}/director/director.dart | 0 bin/builder/{book_example => cars}/director/gps_navigation.dart | 0 bin/builder/{book_example => cars}/director/transmission.dart | 0 bin/builder/{book_example => cars}/director/trip_computer.dart | 0 bin/builder/{book_example => cars}/main.dart | 0 13 files changed, 0 insertions(+), 0 deletions(-) rename bin/builder/{book_example => cars}/README.md (100%) rename bin/builder/{book_example => cars}/builders/builder.dart (100%) rename bin/builder/{book_example => cars}/builders/car_builder.dart (100%) rename bin/builder/{book_example => cars}/builders/car_manual_builder.dart (100%) rename bin/builder/{book_example => cars}/cars/car.dart (100%) rename bin/builder/{book_example => cars}/cars/car_manual.dart (100%) rename bin/builder/{book_example => cars}/cars/car_type.dart (100%) rename bin/builder/{book_example => cars}/components/engine.dart (100%) rename bin/builder/{book_example => cars}/director/director.dart (100%) rename bin/builder/{book_example => cars}/director/gps_navigation.dart (100%) rename bin/builder/{book_example => cars}/director/transmission.dart (100%) rename bin/builder/{book_example => cars}/director/trip_computer.dart (100%) rename bin/builder/{book_example => cars}/main.dart (100%) diff --git a/bin/builder/book_example/README.md b/bin/builder/cars/README.md similarity index 100% rename from bin/builder/book_example/README.md rename to bin/builder/cars/README.md diff --git a/bin/builder/book_example/builders/builder.dart b/bin/builder/cars/builders/builder.dart similarity index 100% rename from bin/builder/book_example/builders/builder.dart rename to bin/builder/cars/builders/builder.dart diff --git a/bin/builder/book_example/builders/car_builder.dart b/bin/builder/cars/builders/car_builder.dart similarity index 100% rename from bin/builder/book_example/builders/car_builder.dart rename to bin/builder/cars/builders/car_builder.dart diff --git a/bin/builder/book_example/builders/car_manual_builder.dart b/bin/builder/cars/builders/car_manual_builder.dart similarity index 100% rename from bin/builder/book_example/builders/car_manual_builder.dart rename to bin/builder/cars/builders/car_manual_builder.dart diff --git a/bin/builder/book_example/cars/car.dart b/bin/builder/cars/cars/car.dart similarity index 100% rename from bin/builder/book_example/cars/car.dart rename to bin/builder/cars/cars/car.dart diff --git a/bin/builder/book_example/cars/car_manual.dart b/bin/builder/cars/cars/car_manual.dart similarity index 100% rename from bin/builder/book_example/cars/car_manual.dart rename to bin/builder/cars/cars/car_manual.dart diff --git a/bin/builder/book_example/cars/car_type.dart b/bin/builder/cars/cars/car_type.dart similarity index 100% rename from bin/builder/book_example/cars/car_type.dart rename to bin/builder/cars/cars/car_type.dart diff --git a/bin/builder/book_example/components/engine.dart b/bin/builder/cars/components/engine.dart similarity index 100% rename from bin/builder/book_example/components/engine.dart rename to bin/builder/cars/components/engine.dart diff --git a/bin/builder/book_example/director/director.dart b/bin/builder/cars/director/director.dart similarity index 100% rename from bin/builder/book_example/director/director.dart rename to bin/builder/cars/director/director.dart diff --git a/bin/builder/book_example/director/gps_navigation.dart b/bin/builder/cars/director/gps_navigation.dart similarity index 100% rename from bin/builder/book_example/director/gps_navigation.dart rename to bin/builder/cars/director/gps_navigation.dart diff --git a/bin/builder/book_example/director/transmission.dart b/bin/builder/cars/director/transmission.dart similarity index 100% rename from bin/builder/book_example/director/transmission.dart rename to bin/builder/cars/director/transmission.dart diff --git a/bin/builder/book_example/director/trip_computer.dart b/bin/builder/cars/director/trip_computer.dart similarity index 100% rename from bin/builder/book_example/director/trip_computer.dart rename to bin/builder/cars/director/trip_computer.dart diff --git a/bin/builder/book_example/main.dart b/bin/builder/cars/main.dart similarity index 100% rename from bin/builder/book_example/main.dart rename to bin/builder/cars/main.dart From 1fefd9ab3a919bbdeca3dfcee9f40bfa3114eb0e Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 12 Dec 2021 03:29:09 +0200 Subject: [PATCH 018/479] Add description Builder pattern, text formats --- bin/builder/file_format/{output.txt => README.md} | 7 +++++++ 1 file changed, 7 insertions(+) rename bin/builder/file_format/{output.txt => README.md} (80%) diff --git a/bin/builder/file_format/output.txt b/bin/builder/file_format/README.md similarity index 80% rename from bin/builder/file_format/output.txt rename to bin/builder/file_format/README.md index 65d84e4..bf96847 100644 --- a/bin/builder/file_format/output.txt +++ b/bin/builder/file_format/README.md @@ -1,3 +1,9 @@ +# Builder pattern using different text formats as an example + +![image](https://user-images.githubusercontent.com/8049534/145697044-7d3e59a9-9f28-4955-a9a2-fcb815f6fb71.png) + +**Output:** +``` HtmlFormat( @@ -48,3 +54,4 @@ JsonFormat( ConsoleFormat( I love looking at the blue sky, eating red apples, sitting on the green grass. ), +``` From 2cbee9fc051813fc3ccf89ea0758c6e394882ad4 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 12 Dec 2021 03:30:25 +0200 Subject: [PATCH 019/479] Rename file_format to color_text_format --- bin/builder/{file_format => color_text_format}/README.md | 0 .../color_reader/color_text_reader.dart | 0 .../converters/console_converter.dart | 0 .../converters/html_converter.dart | 0 .../converters/json_converter.dart | 0 .../formats/console_format.dart | 0 .../{file_format => color_text_format}/formats/html_format.dart | 0 .../{file_format => color_text_format}/formats/json_format.dart | 0 bin/builder/{file_format => color_text_format}/main.dart | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename bin/builder/{file_format => color_text_format}/README.md (100%) rename bin/builder/{file_format => color_text_format}/color_reader/color_text_reader.dart (100%) rename bin/builder/{file_format => color_text_format}/converters/console_converter.dart (100%) rename bin/builder/{file_format => color_text_format}/converters/html_converter.dart (100%) rename bin/builder/{file_format => color_text_format}/converters/json_converter.dart (100%) rename bin/builder/{file_format => color_text_format}/formats/console_format.dart (100%) rename bin/builder/{file_format => color_text_format}/formats/html_format.dart (100%) rename bin/builder/{file_format => color_text_format}/formats/json_format.dart (100%) rename bin/builder/{file_format => color_text_format}/main.dart (100%) diff --git a/bin/builder/file_format/README.md b/bin/builder/color_text_format/README.md similarity index 100% rename from bin/builder/file_format/README.md rename to bin/builder/color_text_format/README.md diff --git a/bin/builder/file_format/color_reader/color_text_reader.dart b/bin/builder/color_text_format/color_reader/color_text_reader.dart similarity index 100% rename from bin/builder/file_format/color_reader/color_text_reader.dart rename to bin/builder/color_text_format/color_reader/color_text_reader.dart diff --git a/bin/builder/file_format/converters/console_converter.dart b/bin/builder/color_text_format/converters/console_converter.dart similarity index 100% rename from bin/builder/file_format/converters/console_converter.dart rename to bin/builder/color_text_format/converters/console_converter.dart diff --git a/bin/builder/file_format/converters/html_converter.dart b/bin/builder/color_text_format/converters/html_converter.dart similarity index 100% rename from bin/builder/file_format/converters/html_converter.dart rename to bin/builder/color_text_format/converters/html_converter.dart diff --git a/bin/builder/file_format/converters/json_converter.dart b/bin/builder/color_text_format/converters/json_converter.dart similarity index 100% rename from bin/builder/file_format/converters/json_converter.dart rename to bin/builder/color_text_format/converters/json_converter.dart diff --git a/bin/builder/file_format/formats/console_format.dart b/bin/builder/color_text_format/formats/console_format.dart similarity index 100% rename from bin/builder/file_format/formats/console_format.dart rename to bin/builder/color_text_format/formats/console_format.dart diff --git a/bin/builder/file_format/formats/html_format.dart b/bin/builder/color_text_format/formats/html_format.dart similarity index 100% rename from bin/builder/file_format/formats/html_format.dart rename to bin/builder/color_text_format/formats/html_format.dart diff --git a/bin/builder/file_format/formats/json_format.dart b/bin/builder/color_text_format/formats/json_format.dart similarity index 100% rename from bin/builder/file_format/formats/json_format.dart rename to bin/builder/color_text_format/formats/json_format.dart diff --git a/bin/builder/file_format/main.dart b/bin/builder/color_text_format/main.dart similarity index 100% rename from bin/builder/file_format/main.dart rename to bin/builder/color_text_format/main.dart From 45b31fc11c2faf236e5c3716c5e144ddf9f831d0 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 12 Dec 2021 03:32:50 +0200 Subject: [PATCH 020/479] Fix class diagram for text graph --- bin/adapter/text_graphics/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/adapter/text_graphics/README.md b/bin/adapter/text_graphics/README.md index 2d20545..23e5549 100644 --- a/bin/adapter/text_graphics/README.md +++ b/bin/adapter/text_graphics/README.md @@ -1,4 +1,4 @@ -![image](https://user-images.githubusercontent.com/8049534/145682370-a2cd3dfa-bac0-428e-8b6e-b16b85df27ac.png) +![image](https://user-images.githubusercontent.com/8049534/145695548-ef1a0632-f11e-4f6d-aae5-db0a4e81838c.png) **Output:** ``` From 545d4cf86580972795045af652934b863b181622 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 12 Dec 2021 03:35:29 +0200 Subject: [PATCH 021/479] Add description to prototype pattern --- bin/prototype/example/{output.txt => README.md} | 9 +++++++++ 1 file changed, 9 insertions(+) rename bin/prototype/example/{output.txt => README.md} (55%) diff --git a/bin/prototype/example/output.txt b/bin/prototype/example/README.md similarity index 55% rename from bin/prototype/example/output.txt rename to bin/prototype/example/README.md index 14fb975..97894a1 100644 --- a/bin/prototype/example/output.txt +++ b/bin/prototype/example/README.md @@ -1,2 +1,11 @@ +# Prototype pattern + +**Description:** +https://refactoring.guru/design-patterns/prototype?#pseudocode + +**Output:** + +``` Origin shapes: [Rectangle(address: 0x2f7f544c), Circle(address: 0x3e59b487)] Cloning shapes: [Rectangle(address: 0x3eb0a110), Circle(address: 0x75e3636)] +``` From 0988b01984873f5395b5cde92bf59be1a8f029ac Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 12 Dec 2021 03:48:12 +0200 Subject: [PATCH 022/479] Bump version 0.5.5 --- CHANGELOG.md | 7 +++++++ pubspec.yaml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 009b0a9..78538c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.5.5 +Add example "graphics engine" and "square round conflict" for adapter pattern +Add description to prototype pattern +Fix class diagram for text graph +Add description Builder pattern, text formats +Add description to car builder pattern + ## 0.3.0 Add an example graphics engine for the adapter pattern diff --git a/pubspec.yaml b/pubspec.yaml index fbb258e..0cd8ff4 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.3.0 +version: 0.5.5 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 0c086f1f95b44ec29bb28366ca371a16f4fbd7e2 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 12 Dec 2021 04:15:04 +0200 Subject: [PATCH 023/479] Change main README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4d7229e..fae5cf2 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ It contains **Dart** examples for all classic GoF design patterns. - [ ] Visitor - [ ] Strategy - [ ] **Structural** - - [ ] Adapter + - [x] Adapter - [ ] Bridge - [ ] Composite - [ ] Decorator From e45121f2f2b62f9c9711577c5b81d5da71b76e2c Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 12 Dec 2021 17:03:46 +0200 Subject: [PATCH 024/479] Add bridge pattern. Device remote control --- .../devices/device.dart | 15 ++++ .../devices/empty_device.dart | 28 +++++++ .../devices_remote_control/devices/radio.dart | 43 +++++++++++ .../devices_remote_control/devices/tv.dart | 43 +++++++++++ bin/bridge/devices_remote_control/main.dart | 26 +++++++ .../remotes/basic_advance_remote.dart | 76 +++++++++++++++++++ .../remotes/remote.dart | 11 +++ 7 files changed, 242 insertions(+) create mode 100644 bin/bridge/devices_remote_control/devices/device.dart create mode 100644 bin/bridge/devices_remote_control/devices/empty_device.dart create mode 100644 bin/bridge/devices_remote_control/devices/radio.dart create mode 100644 bin/bridge/devices_remote_control/devices/tv.dart create mode 100644 bin/bridge/devices_remote_control/main.dart create mode 100644 bin/bridge/devices_remote_control/remotes/basic_advance_remote.dart create mode 100644 bin/bridge/devices_remote_control/remotes/remote.dart diff --git a/bin/bridge/devices_remote_control/devices/device.dart b/bin/bridge/devices_remote_control/devices/device.dart new file mode 100644 index 0000000..6dc18e6 --- /dev/null +++ b/bin/bridge/devices_remote_control/devices/device.dart @@ -0,0 +1,15 @@ +abstract class Device { + bool get isEnabled; + + set isEnabled(bool enabled); + + int get volume; + + set volume(int percent); + + int get channel; + + set channel(int channel); + + void printStatus(); +} diff --git a/bin/bridge/devices_remote_control/devices/empty_device.dart b/bin/bridge/devices_remote_control/devices/empty_device.dart new file mode 100644 index 0000000..04142cc --- /dev/null +++ b/bin/bridge/devices_remote_control/devices/empty_device.dart @@ -0,0 +1,28 @@ +import 'device.dart'; + +class EmptyDevice implements Device { + @override + int get channel => throw UnimplementedError('EmptyDevice'); + + @override + bool get isEnabled => throw UnimplementedError('EmptyDevice'); + + @override + int get volume => throw UnimplementedError('EmptyDevice'); + + @override + void printStatus() { + print('------------------------------------'); + print('| Device is Empty'); + print('------------------------------------\n'); + } + + @override + set channel(int channel) => throw UnimplementedError('EmptyDevice'); + + @override + set isEnabled(bool enabled) => throw UnimplementedError('EmptyDevice'); + + @override + set volume(int percent) => throw UnimplementedError('EmptyDevice'); +} diff --git a/bin/bridge/devices_remote_control/devices/radio.dart b/bin/bridge/devices_remote_control/devices/radio.dart new file mode 100644 index 0000000..7137bd1 --- /dev/null +++ b/bin/bridge/devices_remote_control/devices/radio.dart @@ -0,0 +1,43 @@ +import 'device.dart'; + +class Radio implements Device { + bool _on = false; + int _volume = 30; + int _channel = 1; + + @override + bool get isEnabled => _on; + + @override + set isEnabled(bool enabled) => _on = enabled; + + @override + int get volume => _volume; + + @override + set volume(int percent) { + if (volume > 100) { + _volume = 100; + } else if (volume < 0) { + _volume = 0; + } else { + _volume = volume; + } + } + + @override + int get channel => _channel; + + @override + set channel(int channel) => _channel = channel; + + @override + void printStatus() { + print('------------------------------------'); + print('| I\'m radio.'); + print('| I\'m ${_on ? 'enabled' : 'disabled'}'); + print('| Current volume is $_volume%'); + print('| Current channel is $_channel'); + print('------------------------------------\n'); + } +} diff --git a/bin/bridge/devices_remote_control/devices/tv.dart b/bin/bridge/devices_remote_control/devices/tv.dart new file mode 100644 index 0000000..0fc0cd1 --- /dev/null +++ b/bin/bridge/devices_remote_control/devices/tv.dart @@ -0,0 +1,43 @@ +import 'device.dart'; + +class Tv implements Device { + bool _on = false; + int _volume = 30; + int _channel = 1; + + @override + bool get isEnabled => _on; + + @override + set isEnabled(bool enabled) => _on = enabled; + + @override + int get volume => _volume; + + @override + set volume(int percent) { + if (volume > 100) { + _volume = 100; + } else if (volume < 0) { + _volume = 0; + } else { + _volume = volume; + } + } + + @override + int get channel => _channel; + + @override + set channel(int channel) => _channel = channel; + + @override + void printStatus() { + print('------------------------------------'); + print('| I\'m TV set.'); + print('| I\'m ${_on ? 'enabled' : 'disabled'}'); + print('| Current volume is $_volume%'); + print('| Current channel is $_channel'); + print('------------------------------------\n'); + } +} diff --git a/bin/bridge/devices_remote_control/main.dart b/bin/bridge/devices_remote_control/main.dart new file mode 100644 index 0000000..f35c767 --- /dev/null +++ b/bin/bridge/devices_remote_control/main.dart @@ -0,0 +1,26 @@ +import 'devices/device.dart'; +import 'devices/empty_device.dart'; +import 'devices/radio.dart'; +import 'devices/tv.dart'; +import 'remotes/basic_advance_remote.dart'; + +void main() { + testDevice(Tv()); + testDevice(Radio()); + testDevice(EmptyDevice()); +} + +void testDevice(Device device) { + print(''.padRight(36, '=')); + print(device.runtimeType); + print("Tests with basic remote."); + final basicRemote = BasicRemote.fromDevice(device); + basicRemote.power(); + device.printStatus(); + + print("Tests with advanced remote."); + final advancedRemote = AdvancedRemote.fromDevice(device); + advancedRemote.power(); + advancedRemote.mute(); + device.printStatus(); +} diff --git a/bin/bridge/devices_remote_control/remotes/basic_advance_remote.dart b/bin/bridge/devices_remote_control/remotes/basic_advance_remote.dart new file mode 100644 index 0000000..6c86de5 --- /dev/null +++ b/bin/bridge/devices_remote_control/remotes/basic_advance_remote.dart @@ -0,0 +1,76 @@ +import '../devices/device.dart'; +import '../devices/empty_device.dart'; +import 'remote.dart'; + +class BasicRemote implements Remote { + final Device _device; + + BasicRemote() : _device = EmptyDevice(); + + BasicRemote.fromDevice(this._device); + + @override + void power() { + if (_device is EmptyDevice) { + print('Remote: power(device not found)'); + return; + } + print('Remote: power toggle'); + _device.isEnabled = !_device.isEnabled; + } + + @override + void volumeDown() { + if (_device is EmptyDevice) { + print('Remote: volumeDown(device not found)'); + return; + } + print("Remote: volume down"); + _device.volume -= 10; + } + + @override + void volumeUp() { + if (_device is EmptyDevice) { + print('Remote: volumeDown(device not found)'); + return; + } + print("Remote: volume up"); + _device.volume += 10; + } + + @override + void channelDown() { + if (_device is EmptyDevice) { + print('Remote: channelDown(device not found)'); + return; + } + print("Remote: channel down"); + _device.channel -= 1; + } + + @override + void channelUp() { + if (_device is EmptyDevice) { + print('Remote: channelUp(device not found)'); + return; + } + print("Remote: channel up"); + _device.channel += 1; + } +} + +class AdvancedRemote extends BasicRemote { + AdvancedRemote() : super(); + + AdvancedRemote.fromDevice(Device device) : super.fromDevice(device); + + void mute() { + if (_device is EmptyDevice) { + print('Remote: mute(device not found)'); + return; + } + print("Remote: mute"); + _device.volume = 0; + } +} diff --git a/bin/bridge/devices_remote_control/remotes/remote.dart b/bin/bridge/devices_remote_control/remotes/remote.dart new file mode 100644 index 0000000..6f1ff2e --- /dev/null +++ b/bin/bridge/devices_remote_control/remotes/remote.dart @@ -0,0 +1,11 @@ +abstract class Remote { + void power(); + + void volumeDown(); + + void volumeUp(); + + void channelDown(); + + void channelUp(); +} From c28706ac851363b250970b48a64ac39d4e29c31a Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 12 Dec 2021 17:06:04 +0200 Subject: [PATCH 025/479] Add description to device remote example --- bin/bridge/devices_remote_control/README.md | 67 +++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 bin/bridge/devices_remote_control/README.md diff --git a/bin/bridge/devices_remote_control/README.md b/bin/bridge/devices_remote_control/README.md new file mode 100644 index 0000000..7767741 --- /dev/null +++ b/bin/bridge/devices_remote_control/README.md @@ -0,0 +1,67 @@ +# Adapter pattern +This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/master/src/refactoring_guru/bridge/example) +But removed the ability to use null for devices. Instead, the EmptyDevice class is used. + +**Description:** +https://refactoring.guru/design-patterns/adapter?#pseudocode + +**Output:** + +``` +==================================== +Tv +Tests with basic remote. +Remote: power toggle +------------------------------------ +| I'm TV set. +| I'm enabled +| Current volume is 30% +| Current channel is 1 +------------------------------------ + +Tests with advanced remote. +Remote: power toggle +Remote: mute +------------------------------------ +| I'm TV set. +| I'm disabled +| Current volume is 30% +| Current channel is 1 +------------------------------------ + +==================================== +Radio +Tests with basic remote. +Remote: power toggle +------------------------------------ +| I'm radio. +| I'm enabled +| Current volume is 30% +| Current channel is 1 +------------------------------------ + +Tests with advanced remote. +Remote: power toggle +Remote: mute +------------------------------------ +| I'm radio. +| I'm disabled +| Current volume is 30% +| Current channel is 1 +------------------------------------ + +==================================== +EmptyDevice +Tests with basic remote. +Remote: power(device not found) +------------------------------------ +| Device is Empty +------------------------------------ + +Tests with advanced remote. +Remote: power(device not found) +Remote: mute(device not found) +------------------------------------ +| Device is Empty +------------------------------------ +``` From 6887c909c29a65dabf972d2a2248d66d49f249fa Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 12 Dec 2021 17:17:03 +0200 Subject: [PATCH 026/479] Bump version 0.6.5 --- CHANGELOG.md | 17 ++++++++++------- README.md | 2 +- pubspec.yaml | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78538c8..202a37d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,18 @@ +## 0.6.5 +- Add bridge pattern. Device remote control + ## 0.5.5 -Add example "graphics engine" and "square round conflict" for adapter pattern -Add description to prototype pattern -Fix class diagram for text graph -Add description Builder pattern, text formats -Add description to car builder pattern +- Add example "graphics engine" and "square round conflict" for adapter pattern +- Add description to prototype pattern +- Fix class diagram for text graph +- Add description Builder pattern, text formats +- Add description to car builder pattern ## 0.3.0 -Add an example graphics engine for the adapter pattern +- Add an example graphics engine for the adapter pattern ## 0.2.0 -Add the Builder pattern using file format converters as an example +- Add the Builder pattern using file format converters as an example ## 0.1.0 - Add pattern Prototype diff --git a/README.md b/README.md index fae5cf2..0d3548a 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ It contains **Dart** examples for all classic GoF design patterns. - [ ] Strategy - [ ] **Structural** - [x] Adapter - - [ ] Bridge + - [x] Bridge - [ ] Composite - [ ] Decorator - [ ] Facade diff --git a/pubspec.yaml b/pubspec.yaml index 0cd8ff4..fb1bec3 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.5.5 +version: 0.6.5 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 75ce1190b444513739344a6a34f990a27da22c27 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 13 Dec 2021 15:04:36 +0200 Subject: [PATCH 027/479] Add bridge pattern. Clock example. --- CHANGELOG.md | 3 +++ bin/bridge/clock/README.md | 31 +++++++++++++++++++++++++++ bin/bridge/clock/bells/bell.dart | 5 +++++ bin/bridge/clock/bells/melody.dart | 11 ++++++++++ bin/bridge/clock/bells/signal.dart | 11 ++++++++++ bin/bridge/clock/clocks/clock.dart | 18 ++++++++++++++++ bin/bridge/clock/clocks/interval.dart | 19 ++++++++++++++++ bin/bridge/clock/clocks/once.dart | 15 +++++++++++++ bin/bridge/clock/main.dart | 24 +++++++++++++++++++++ pubspec.yaml | 2 +- 10 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 bin/bridge/clock/README.md create mode 100644 bin/bridge/clock/bells/bell.dart create mode 100644 bin/bridge/clock/bells/melody.dart create mode 100644 bin/bridge/clock/bells/signal.dart create mode 100644 bin/bridge/clock/clocks/clock.dart create mode 100644 bin/bridge/clock/clocks/interval.dart create mode 100644 bin/bridge/clock/clocks/once.dart create mode 100644 bin/bridge/clock/main.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 202a37d..97f138e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.7.0 +- Add bridge pattern. Clock example + ## 0.6.5 - Add bridge pattern. Device remote control diff --git a/bin/bridge/clock/README.md b/bin/bridge/clock/README.md new file mode 100644 index 0000000..b8dcd52 --- /dev/null +++ b/bin/bridge/clock/README.md @@ -0,0 +1,31 @@ +# Bridge pattern +The idea for the bridge pattern example is taken from [here](https://habr.com/ru/post/85137/). + +**Diagram:** +![image](https://user-images.githubusercontent.com/8049534/145851578-f6e95355-e2b3-4f94-bda2-c2d1d0de8935.png) + +**Client code:** +```dart +void main() { + final onceMelody = Once(seconds: 1, bell: Melody()); + final onceSignal = Once(seconds: 2, bell: Signal()); + final intervalMelody = Interval(seconds: 5, bell: Melody()); + final intervalSignal = Interval(seconds: 3, bell: Signal()); + startAlarm([ + onceMelody, + onceSignal, + intervalMelody, + intervalSignal, + ]); +} + +void startAlarm(List clocks) { + for (var i = 0; i < clocks.length; i++) { + clocks[i].start(); + } +} +``` + +**Output:** + +https://user-images.githubusercontent.com/8049534/145850512-27a5e9ea-4f76-4d52-b784-9bc88aee4de8.mp4 diff --git a/bin/bridge/clock/bells/bell.dart b/bin/bridge/clock/bells/bell.dart new file mode 100644 index 0000000..50e3cbc --- /dev/null +++ b/bin/bridge/clock/bells/bell.dart @@ -0,0 +1,5 @@ +abstract class Bell { + void ring(); + + void notify(String message); +} diff --git a/bin/bridge/clock/bells/melody.dart b/bin/bridge/clock/bells/melody.dart new file mode 100644 index 0000000..f33c7cf --- /dev/null +++ b/bin/bridge/clock/bells/melody.dart @@ -0,0 +1,11 @@ +import 'dart:io'; + +import 'bell.dart'; + +class Melody extends Bell { + @override + void notify(String message) => stdout.write('\x1b[32m$message\x1B[0m'); + + @override + void ring() => stdout.write('\x1b[32m🎵\x1B[0m'); +} diff --git a/bin/bridge/clock/bells/signal.dart b/bin/bridge/clock/bells/signal.dart new file mode 100644 index 0000000..bf8b733 --- /dev/null +++ b/bin/bridge/clock/bells/signal.dart @@ -0,0 +1,11 @@ +import 'dart:io'; + +import 'bell.dart'; + +class Signal extends Bell { + @override + void notify(String message) => stdout.write('\x1b[34m$message\x1B[0m'); + + @override + void ring() => stdout.write('\x1b[34m🔉\x1B[0m'); +} diff --git a/bin/bridge/clock/clocks/clock.dart b/bin/bridge/clock/clocks/clock.dart new file mode 100644 index 0000000..87c72cb --- /dev/null +++ b/bin/bridge/clock/clocks/clock.dart @@ -0,0 +1,18 @@ +import '../bells/bell.dart'; + +abstract class Clock { + final int seconds; + final Bell bell; + + Clock({ + required this.seconds, + required this.bell, + }); + + void start(); + + void showBell(String message) { + bell.notify('$message(sec: $seconds): '); + bell.ring(); + } +} diff --git a/bin/bridge/clock/clocks/interval.dart b/bin/bridge/clock/clocks/interval.dart new file mode 100644 index 0000000..a935489 --- /dev/null +++ b/bin/bridge/clock/clocks/interval.dart @@ -0,0 +1,19 @@ +import '../bells/bell.dart'; +import 'clock.dart'; + +class Interval extends Clock { + Interval({required int seconds, required Bell bell}) + : super(bell: bell, seconds: seconds); + + @override + void start() { + Future.delayed(Duration(seconds: seconds), () async { + showBell('Interval'); + for (var i = 0; i < 3 - 1; i++) { + await Future.delayed(Duration(milliseconds: 500)); + bell.ring(); + } + print(''); + }); + } +} diff --git a/bin/bridge/clock/clocks/once.dart b/bin/bridge/clock/clocks/once.dart new file mode 100644 index 0000000..02b8733 --- /dev/null +++ b/bin/bridge/clock/clocks/once.dart @@ -0,0 +1,15 @@ +import '../bells/bell.dart'; +import 'clock.dart'; + +class Once extends Clock { + Once({required int seconds, required Bell bell}) + : super(seconds: seconds, bell: bell); + + @override + void start() { + Future.delayed(Duration(seconds: seconds), () { + showBell('Once'); + print(''); + }); + } +} diff --git a/bin/bridge/clock/main.dart b/bin/bridge/clock/main.dart new file mode 100644 index 0000000..379bedc --- /dev/null +++ b/bin/bridge/clock/main.dart @@ -0,0 +1,24 @@ +import 'bells/melody.dart'; +import 'bells/signal.dart'; +import 'clocks/once.dart'; +import 'clocks/clock.dart'; +import 'clocks/interval.dart'; + +void main() { + final onceMelody = Once(seconds: 1, bell: Melody()); + final onceSignal = Once(seconds: 2, bell: Signal()); + final intervalMelody = Interval(seconds: 5, bell: Melody()); + final intervalSignal = Interval(seconds: 3, bell: Signal()); + startAlarm([ + onceMelody, + onceSignal, + intervalMelody, + intervalSignal, + ]); +} + +void startAlarm(List clocks) { + for (var i = 0; i < clocks.length; i++) { + clocks[i].start(); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index fb1bec3..9d41c85 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.6.5 +version: 0.7.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 492396924c99dc70b0fd8cc96c0e1d5bf2aeef8f Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 13 Dec 2021 19:57:46 +0200 Subject: [PATCH 028/479] Use Explicit Aggregation --- bin/bridge/clock/README.md | 18 ++++++++---------- bin/bridge/clock/main.dart | 18 ++++++++---------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/bin/bridge/clock/README.md b/bin/bridge/clock/README.md index b8dcd52..ae47b43 100644 --- a/bin/bridge/clock/README.md +++ b/bin/bridge/clock/README.md @@ -7,19 +7,17 @@ The idea for the bridge pattern example is taken from [here](https://habr.com/ru **Client code:** ```dart void main() { - final onceMelody = Once(seconds: 1, bell: Melody()); - final onceSignal = Once(seconds: 2, bell: Signal()); - final intervalMelody = Interval(seconds: 5, bell: Melody()); - final intervalSignal = Interval(seconds: 3, bell: Signal()); - startAlarm([ - onceMelody, - onceSignal, - intervalMelody, - intervalSignal, + final melody = Melody(); + final signal = Signal(); + startClocks([ + Once(seconds: 1, bell: melody), + Once(seconds: 2, bell: signal), + Interval(seconds: 5, bell: melody), + Interval(seconds: 3, bell: signal), ]); } -void startAlarm(List clocks) { +void startClocks(List clocks) { for (var i = 0; i < clocks.length; i++) { clocks[i].start(); } diff --git a/bin/bridge/clock/main.dart b/bin/bridge/clock/main.dart index 379bedc..60acf73 100644 --- a/bin/bridge/clock/main.dart +++ b/bin/bridge/clock/main.dart @@ -5,19 +5,17 @@ import 'clocks/clock.dart'; import 'clocks/interval.dart'; void main() { - final onceMelody = Once(seconds: 1, bell: Melody()); - final onceSignal = Once(seconds: 2, bell: Signal()); - final intervalMelody = Interval(seconds: 5, bell: Melody()); - final intervalSignal = Interval(seconds: 3, bell: Signal()); - startAlarm([ - onceMelody, - onceSignal, - intervalMelody, - intervalSignal, + final melody = Melody(); + final signal = Signal(); + startClocks([ + Once(seconds: 1, bell: melody), + Once(seconds: 2, bell: signal), + Interval(seconds: 5, bell: melody), + Interval(seconds: 3, bell: signal), ]); } -void startAlarm(List clocks) { +void startClocks(List clocks) { for (var i = 0; i < clocks.length; i++) { clocks[i].start(); } From 0cc55593a70c41e74d47e013b510f6b74c94820d Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 13 Dec 2021 20:07:06 +0200 Subject: [PATCH 029/479] Change clock start to iterable loop --- bin/bridge/clock/README.md | 4 ++-- bin/bridge/clock/main.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/bridge/clock/README.md b/bin/bridge/clock/README.md index ae47b43..29eb67f 100644 --- a/bin/bridge/clock/README.md +++ b/bin/bridge/clock/README.md @@ -18,8 +18,8 @@ void main() { } void startClocks(List clocks) { - for (var i = 0; i < clocks.length; i++) { - clocks[i].start(); + for (final clock in clocks) { + clock.start(); } } ``` diff --git a/bin/bridge/clock/main.dart b/bin/bridge/clock/main.dart index 60acf73..a2d80d9 100644 --- a/bin/bridge/clock/main.dart +++ b/bin/bridge/clock/main.dart @@ -16,7 +16,7 @@ void main() { } void startClocks(List clocks) { - for (var i = 0; i < clocks.length; i++) { - clocks[i].start(); + for (final clock in clocks) { + clock.start(); } } From 93be8b2856f42702d9ef6099d3a8ce87efff5c93 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 13 Dec 2021 21:44:23 +0200 Subject: [PATCH 030/479] Fix Bridge pattern name --- bin/bridge/devices_remote_control/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/bridge/devices_remote_control/README.md b/bin/bridge/devices_remote_control/README.md index 7767741..9e27299 100644 --- a/bin/bridge/devices_remote_control/README.md +++ b/bin/bridge/devices_remote_control/README.md @@ -1,4 +1,4 @@ -# Adapter pattern +# Bridge pattern This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/master/src/refactoring_guru/bridge/example) But removed the ability to use null for devices. Instead, the EmptyDevice class is used. From ea6a77dee43d1f394146ca7bd31739c9a552aa10 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 13 Dec 2021 21:52:15 +0200 Subject: [PATCH 031/479] Fix url & add diagram in remote control example --- bin/bridge/devices_remote_control/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/bridge/devices_remote_control/README.md b/bin/bridge/devices_remote_control/README.md index 9e27299..fd41f07 100644 --- a/bin/bridge/devices_remote_control/README.md +++ b/bin/bridge/devices_remote_control/README.md @@ -2,8 +2,11 @@ This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/master/src/refactoring_guru/bridge/example) But removed the ability to use null for devices. Instead, the EmptyDevice class is used. +**Diagram:** +![image](https://user-images.githubusercontent.com/8049534/145878324-3cbc52f5-51f4-4642-921d-69fbe2886f8c.png) + **Description:** -https://refactoring.guru/design-patterns/adapter?#pseudocode +https://refactoring.guru/design-patterns/bridge?#pseudocode **Output:** From 9d546b071eef3936dce87a343a0dd888cf560adb Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 14 Dec 2021 15:45:22 +0200 Subject: [PATCH 032/479] Remove unnecessary mixin --- bin/builder/color_text_format/formats/html_format.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/bin/builder/color_text_format/formats/html_format.dart b/bin/builder/color_text_format/formats/html_format.dart index 023db16..9ddab14 100644 --- a/bin/builder/color_text_format/formats/html_format.dart +++ b/bin/builder/color_text_format/formats/html_format.dart @@ -80,5 +80,3 @@ class Tag { return '<$name$cls>$text'; } } - -mixin MixinTag {} From e91ff3176a823d12ccc5853834e57670532fa457 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 14 Dec 2021 16:03:34 +0200 Subject: [PATCH 033/479] Add example code in the README of the text formats project of the builder pattern --- bin/builder/color_text_format/README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/bin/builder/color_text_format/README.md b/bin/builder/color_text_format/README.md index bf96847..85ad6e3 100644 --- a/bin/builder/color_text_format/README.md +++ b/bin/builder/color_text_format/README.md @@ -1,7 +1,25 @@ -# Builder pattern using different text formats as an example +# Builder pattern +Using different text formats ![image](https://user-images.githubusercontent.com/8049534/145697044-7d3e59a9-9f28-4955-a9a2-fcb815f6fb71.png) +**Client code:** +```dart + 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', + ); +``` + **Output:** ``` HtmlFormat( From 35269fb02c913d97da6882ab48c68dda05bb4d50 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 14 Dec 2021 17:01:04 +0200 Subject: [PATCH 034/479] Update class diagram for builder pattern --- bin/builder/color_text_format/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/builder/color_text_format/README.md b/bin/builder/color_text_format/README.md index 85ad6e3..28f5b8b 100644 --- a/bin/builder/color_text_format/README.md +++ b/bin/builder/color_text_format/README.md @@ -1,7 +1,7 @@ # Builder pattern Using different text formats -![image](https://user-images.githubusercontent.com/8049534/145697044-7d3e59a9-9f28-4955-a9a2-fcb815f6fb71.png) +![image](https://user-images.githubusercontent.com/8049534/146023073-5d7644a4-d3b9-4420-bffe-f72ac3fd83dd.png)) **Client code:** ```dart From 6ac181436d618aeac2ba3eb012b8c259f75fe716 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 14 Dec 2021 17:13:44 +0200 Subject: [PATCH 035/479] Put interfaces TextFormat & Converter to separate files --- .../color_reader/color_text_reader.dart | 22 +++---------------- .../converters/console_converter.dart | 2 +- .../converters/converter.dart | 10 +++++++++ .../converters/html_converter.dart | 2 +- .../converters/json_converter.dart | 2 +- .../formats/console_format.dart | 2 +- .../formats/html_format.dart | 3 +-- .../formats/json_format.dart | 3 +-- .../formats/text_format.dart | 9 ++++++++ bin/builder/color_text_format/main.dart | 2 +- 10 files changed, 29 insertions(+), 28 deletions(-) create mode 100644 bin/builder/color_text_format/converters/converter.dart create mode 100644 bin/builder/color_text_format/formats/text_format.dart diff --git a/bin/builder/color_text_format/color_reader/color_text_reader.dart b/bin/builder/color_text_format/color_reader/color_text_reader.dart index 953328c..adae573 100644 --- a/bin/builder/color_text_format/color_reader/color_text_reader.dart +++ b/bin/builder/color_text_format/color_reader/color_text_reader.dart @@ -1,3 +1,6 @@ +import '../converters/converter.dart'; +import '../formats/text_format.dart'; + /// Director class ColorTextReader { final String text; @@ -17,22 +20,3 @@ class ColorTextReader { 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/color_text_format/converters/console_converter.dart b/bin/builder/color_text_format/converters/console_converter.dart index d384873..972f51f 100644 --- a/bin/builder/color_text_format/converters/console_converter.dart +++ b/bin/builder/color_text_format/converters/console_converter.dart @@ -1,5 +1,5 @@ import '../formats/console_format.dart'; -import '../color_reader/color_text_reader.dart'; +import 'converter.dart'; class ConsoleConverter extends Converter { @override diff --git a/bin/builder/color_text_format/converters/converter.dart b/bin/builder/color_text_format/converters/converter.dart new file mode 100644 index 0000000..6f98ca3 --- /dev/null +++ b/bin/builder/color_text_format/converters/converter.dart @@ -0,0 +1,10 @@ +import '../formats/text_format.dart'; + +/// Builder +abstract class Converter { + void writeWord(String text); + + void writeColor(String color); + + T get result; +} diff --git a/bin/builder/color_text_format/converters/html_converter.dart b/bin/builder/color_text_format/converters/html_converter.dart index 3887a32..4f2c73b 100644 --- a/bin/builder/color_text_format/converters/html_converter.dart +++ b/bin/builder/color_text_format/converters/html_converter.dart @@ -1,5 +1,5 @@ import '../formats/html_format.dart'; -import '../color_reader/color_text_reader.dart'; +import 'converter.dart'; /// Builder class HtmlConverter extends Converter { diff --git a/bin/builder/color_text_format/converters/json_converter.dart b/bin/builder/color_text_format/converters/json_converter.dart index 763c5a4..ab288de 100644 --- a/bin/builder/color_text_format/converters/json_converter.dart +++ b/bin/builder/color_text_format/converters/json_converter.dart @@ -1,5 +1,5 @@ import '../formats/json_format.dart'; -import '../color_reader/color_text_reader.dart'; +import 'converter.dart'; class JsonConverter extends Converter { @override diff --git a/bin/builder/color_text_format/formats/console_format.dart b/bin/builder/color_text_format/formats/console_format.dart index 2400a3f..65451ca 100644 --- a/bin/builder/color_text_format/formats/console_format.dart +++ b/bin/builder/color_text_format/formats/console_format.dart @@ -1,4 +1,4 @@ -import '../color_reader/color_text_reader.dart'; +import 'text_format.dart'; class ConsoleFormat extends TextFormat { final _buff = [' ']; diff --git a/bin/builder/color_text_format/formats/html_format.dart b/bin/builder/color_text_format/formats/html_format.dart index 9ddab14..1848f3d 100644 --- a/bin/builder/color_text_format/formats/html_format.dart +++ b/bin/builder/color_text_format/formats/html_format.dart @@ -1,6 +1,5 @@ import 'dart:collection'; - -import '../color_reader/color_text_reader.dart'; +import 'text_format.dart'; // product class HtmlFormat extends TextFormat { diff --git a/bin/builder/color_text_format/formats/json_format.dart b/bin/builder/color_text_format/formats/json_format.dart index fe6871e..1dc3d1d 100644 --- a/bin/builder/color_text_format/formats/json_format.dart +++ b/bin/builder/color_text_format/formats/json_format.dart @@ -1,6 +1,5 @@ import 'dart:convert'; - -import '../color_reader/color_text_reader.dart'; +import 'text_format.dart'; class JsonFormat extends TextFormat { final _list = >[]; diff --git a/bin/builder/color_text_format/formats/text_format.dart b/bin/builder/color_text_format/formats/text_format.dart new file mode 100644 index 0000000..0b16113 --- /dev/null +++ b/bin/builder/color_text_format/formats/text_format.dart @@ -0,0 +1,9 @@ +/// Product +abstract class TextFormat { + String get content; + + @override + String toString() => '$runtimeType(\n' + '$content' + '\n)'; +} diff --git a/bin/builder/color_text_format/main.dart b/bin/builder/color_text_format/main.dart index 3d54677..a5ef75b 100644 --- a/bin/builder/color_text_format/main.dart +++ b/bin/builder/color_text_format/main.dart @@ -1,7 +1,7 @@ +import 'color_reader/color_text_reader.dart'; import 'converters/html_converter.dart'; import 'converters/json_converter.dart'; import 'converters/console_converter.dart'; -import 'color_reader/color_text_reader.dart'; void main() { final reader = ColorTextReader( From c73621ed5df388bc6760756f42e4a925877cb4f7 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 14 Dec 2021 17:35:34 +0200 Subject: [PATCH 036/479] Update diagram for text_graphics --- bin/adapter/text_graphics/README.md | 49 ++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/bin/adapter/text_graphics/README.md b/bin/adapter/text_graphics/README.md index 23e5549..df037cd 100644 --- a/bin/adapter/text_graphics/README.md +++ b/bin/adapter/text_graphics/README.md @@ -1,4 +1,51 @@ -![image](https://user-images.githubusercontent.com/8049534/145695548-ef1a0632-f11e-4f6d-aae5-db0a4e81838c.png) +# Adapter pattern + +![image](https://user-images.githubusercontent.com/8049534/146028651-1262b66d-b0ac-4fc9-9487-76a2d588f97e.png) + +**Client code:** +```dart +final renderContent = ShapeEngine( + width: 42, + height: 32, + shapes: [ + // EN: Standard graphics primitives + Rectangle( + x: 1, + y: 1, + width: 12, + height: 6, + color: Color.black, + ), + Rectangle( + x: 4, + y: 3, + width: 6, + height: 9, + color: Color.white, + ), + Circle( + x: 12, + y: 12, + radius: 7, + color: Color.grey, + ), + // EN: Graphics primitives from a third party library + GraphElementAdapter( + x: 11, + y: 20, + color: Color.dark, + graphElement: third_party.Wave(height: 5, length: 25), + ), + GraphElementAdapter( + x: 23, + y: 1, + color: Color.black, + graphElement: third_party.Star(size: 20), + ), + ], + ).render(); + print(renderContent); +``` **Output:** ``` From c71b460c8b1a42ffb86b51403df9847b241eb124 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 14 Dec 2021 17:38:44 +0200 Subject: [PATCH 037/479] Fix code to match with diagram --- bin/adapter/text_graphics/engine/shape_engine.dart | 2 +- bin/adapter/text_graphics/main.dart | 10 +++++----- bin/adapter/text_graphics/{shape => shapes}/shape.dart | 0 .../text_graphics/{shape => shapes}/shape_adapter.dart | 4 ++-- .../text_graphics/third_party_graphics_lib/wave.dart | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) rename bin/adapter/text_graphics/{shape => shapes}/shape.dart (100%) rename bin/adapter/text_graphics/{shape => shapes}/shape_adapter.dart (89%) diff --git a/bin/adapter/text_graphics/engine/shape_engine.dart b/bin/adapter/text_graphics/engine/shape_engine.dart index 33cb2ea..1b61e15 100644 --- a/bin/adapter/text_graphics/engine/shape_engine.dart +++ b/bin/adapter/text_graphics/engine/shape_engine.dart @@ -1,4 +1,4 @@ -import '../shape/shape.dart'; +import '../shapes/shape.dart'; import 'canvas.dart'; import 'primitives.dart'; diff --git a/bin/adapter/text_graphics/main.dart b/bin/adapter/text_graphics/main.dart index 2bb8529..1e1c018 100644 --- a/bin/adapter/text_graphics/main.dart +++ b/bin/adapter/text_graphics/main.dart @@ -1,7 +1,7 @@ import 'engine/primitives.dart'; import 'engine/shape_engine.dart'; -import 'shape/shape.dart'; -import 'shape/shape_adapter.dart'; +import 'shapes/shape.dart'; +import 'shapes/shape_adapter.dart'; import 'third_party_graphics_lib/star.dart' as third_party; import 'third_party_graphics_lib/wave.dart' as third_party; @@ -32,13 +32,13 @@ void main() { color: Color.grey, ), // EN: Graphics primitives from a third party library - ShapeAdapter( + GraphElementAdapter( x: 11, y: 20, color: Color.dark, - graphElement: third_party.Wave(waveHeight: 5, points: 25), + graphElement: third_party.Wave(height: 5, length: 25), ), - ShapeAdapter( + GraphElementAdapter( x: 23, y: 1, color: Color.black, diff --git a/bin/adapter/text_graphics/shape/shape.dart b/bin/adapter/text_graphics/shapes/shape.dart similarity index 100% rename from bin/adapter/text_graphics/shape/shape.dart rename to bin/adapter/text_graphics/shapes/shape.dart diff --git a/bin/adapter/text_graphics/shape/shape_adapter.dart b/bin/adapter/text_graphics/shapes/shape_adapter.dart similarity index 89% rename from bin/adapter/text_graphics/shape/shape_adapter.dart rename to bin/adapter/text_graphics/shapes/shape_adapter.dart index 2189e7f..e0115d8 100644 --- a/bin/adapter/text_graphics/shape/shape_adapter.dart +++ b/bin/adapter/text_graphics/shapes/shape_adapter.dart @@ -3,10 +3,10 @@ import '../engine/primitives.dart'; import '../third_party_graphics_lib/graph_element.dart' as third_party; import 'shape.dart'; -class ShapeAdapter extends Shape { +class GraphElementAdapter extends Shape { final third_party.GraphElement graphElement; - ShapeAdapter({ + GraphElementAdapter({ required int x, required int y, required Color color, diff --git a/bin/adapter/text_graphics/third_party_graphics_lib/wave.dart b/bin/adapter/text_graphics/third_party_graphics_lib/wave.dart index 3fa36f0..0f03434 100644 --- a/bin/adapter/text_graphics/third_party_graphics_lib/wave.dart +++ b/bin/adapter/text_graphics/third_party_graphics_lib/wave.dart @@ -5,13 +5,13 @@ import 'graph_element.dart'; class Wave extends GraphElement { Wave({ - required final int waveHeight, - final int points = 50, + required final int height, + final int length = 50, final double waveStep = .8, }) { final list = []; - for (var x = 0; x < points; x++) { - final y = (waveHeight + cos(x / pi / waveStep) * waveHeight).toInt(); + for (var x = 0; x < length; x++) { + final y = (height + cos(x / pi / waveStep) * height).toInt(); list.addAll([x, y]); } _points = Int32List.fromList(list); From 5c51f677cc6b76c8b5120bfbdaa558da7e40e685 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 14 Dec 2021 17:41:43 +0200 Subject: [PATCH 038/479] Fix clock diagram --- bin/bridge/clock/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/bridge/clock/README.md b/bin/bridge/clock/README.md index 29eb67f..df3a16c 100644 --- a/bin/bridge/clock/README.md +++ b/bin/bridge/clock/README.md @@ -2,6 +2,7 @@ The idea for the bridge pattern example is taken from [here](https://habr.com/ru/post/85137/). **Diagram:** + ![image](https://user-images.githubusercontent.com/8049534/145851578-f6e95355-e2b3-4f94-bda2-c2d1d0de8935.png) **Client code:** From a65f722830e563c191ecdf4a76ad8c5af773ca4d Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 14 Dec 2021 17:43:13 +0200 Subject: [PATCH 039/479] Fix remote_control diagram --- bin/bridge/devices_remote_control/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/bridge/devices_remote_control/README.md b/bin/bridge/devices_remote_control/README.md index fd41f07..51de5a2 100644 --- a/bin/bridge/devices_remote_control/README.md +++ b/bin/bridge/devices_remote_control/README.md @@ -3,6 +3,7 @@ This example rewrite from [java example](https://github.com/RefactoringGuru/desi But removed the ability to use null for devices. Instead, the EmptyDevice class is used. **Diagram:** + ![image](https://user-images.githubusercontent.com/8049534/145878324-3cbc52f5-51f4-4642-921d-69fbe2886f8c.png) **Description:** From 8ccad4657277c77252cb94b5c397f6a2a9e05180 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 14 Dec 2021 17:53:35 +0200 Subject: [PATCH 040/479] Bump version 0.7.8 --- CHANGELOG.md | 5 +++++ pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97f138e..3dbf391 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.7.8 +- Update diagram for text_graphics, clock, remote_control, color_text_format +- Code match with diagram in clock + + ## 0.7.0 - Add bridge pattern. Clock example diff --git a/pubspec.yaml b/pubspec.yaml index 9d41c85..0aed174 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.7.0 +version: 0.7.8 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 3629ceabb371f42f20e2db637822f96e3eda9c79 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 21 Dec 2021 12:46:52 +0200 Subject: [PATCH 041/479] Move canvas_text to lib folder, for reuse in other examples --- bin/adapter/text_graphics/engine/shape_engine.dart | 4 ++-- bin/adapter/text_graphics/main.dart | 3 ++- bin/adapter/text_graphics/shapes/shape.dart | 3 +-- bin/adapter/text_graphics/shapes/shape_adapter.dart | 4 ++-- .../text_graphics/engine => lib/text_canvas}/canvas.dart | 0 .../text_graphics/engine => lib/text_canvas}/primitives.dart | 0 pubspec.yaml | 3 +++ 7 files changed, 10 insertions(+), 7 deletions(-) rename {bin/adapter/text_graphics/engine => lib/text_canvas}/canvas.dart (100%) rename {bin/adapter/text_graphics/engine => lib/text_canvas}/primitives.dart (100%) diff --git a/bin/adapter/text_graphics/engine/shape_engine.dart b/bin/adapter/text_graphics/engine/shape_engine.dart index 1b61e15..9568bb4 100644 --- a/bin/adapter/text_graphics/engine/shape_engine.dart +++ b/bin/adapter/text_graphics/engine/shape_engine.dart @@ -1,6 +1,6 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + import '../shapes/shape.dart'; -import 'canvas.dart'; -import 'primitives.dart'; class ShapeEngine { final List shapes; diff --git a/bin/adapter/text_graphics/main.dart b/bin/adapter/text_graphics/main.dart index 1e1c018..61fdd84 100644 --- a/bin/adapter/text_graphics/main.dart +++ b/bin/adapter/text_graphics/main.dart @@ -1,4 +1,5 @@ -import 'engine/primitives.dart'; +import 'package:design_patterns_dart/text_canvas.dart'; + import 'engine/shape_engine.dart'; import 'shapes/shape.dart'; import 'shapes/shape_adapter.dart'; diff --git a/bin/adapter/text_graphics/shapes/shape.dart b/bin/adapter/text_graphics/shapes/shape.dart index 09a61df..c38c3d9 100644 --- a/bin/adapter/text_graphics/shapes/shape.dart +++ b/bin/adapter/text_graphics/shapes/shape.dart @@ -1,5 +1,4 @@ -import '../engine/canvas.dart'; -import '../engine/primitives.dart'; +import 'package:design_patterns_dart/text_canvas.dart'; abstract class Shape { final int x; diff --git a/bin/adapter/text_graphics/shapes/shape_adapter.dart b/bin/adapter/text_graphics/shapes/shape_adapter.dart index e0115d8..ee50eaf 100644 --- a/bin/adapter/text_graphics/shapes/shape_adapter.dart +++ b/bin/adapter/text_graphics/shapes/shape_adapter.dart @@ -1,5 +1,5 @@ -import '../engine/canvas.dart'; -import '../engine/primitives.dart'; +import 'package:design_patterns_dart/text_canvas.dart'; + import '../third_party_graphics_lib/graph_element.dart' as third_party; import 'shape.dart'; diff --git a/bin/adapter/text_graphics/engine/canvas.dart b/lib/text_canvas/canvas.dart similarity index 100% rename from bin/adapter/text_graphics/engine/canvas.dart rename to lib/text_canvas/canvas.dart diff --git a/bin/adapter/text_graphics/engine/primitives.dart b/lib/text_canvas/primitives.dart similarity index 100% rename from bin/adapter/text_graphics/engine/primitives.dart rename to lib/text_canvas/primitives.dart diff --git a/pubspec.yaml b/pubspec.yaml index 0aed174..9d86c12 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,5 +8,8 @@ issue_tracker: https://github.com/RefactoringGuru/design-patterns-dart/issue environment: sdk: '>=2.15.0-68.0.dev <3.0.0' +dependencies: + collection: ^1.15.0 + dev_dependencies: lints: ^1.0.0 From c5df1d58b62d74dfa062bd8349f369117188ec8a Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 21 Dec 2021 12:48:49 +0200 Subject: [PATCH 042/479] Rename 'bin' folder to 'pattern' folder --- {bin => patterns}/adapter/square_round_conflict/README.md | 0 {bin => patterns}/adapter/square_round_conflict/main.dart | 0 {bin => patterns}/adapter/text_graphics/README.md | 0 {bin => patterns}/adapter/text_graphics/engine/shape_engine.dart | 0 {bin => patterns}/adapter/text_graphics/main.dart | 0 {bin => patterns}/adapter/text_graphics/shapes/shape.dart | 0 {bin => patterns}/adapter/text_graphics/shapes/shape_adapter.dart | 0 .../text_graphics/third_party_graphics_lib/graph_element.dart | 0 .../adapter/text_graphics/third_party_graphics_lib/star.dart | 0 .../adapter/text_graphics/third_party_graphics_lib/wave.dart | 0 {bin => patterns}/bridge/clock/README.md | 0 {bin => patterns}/bridge/clock/bells/bell.dart | 0 {bin => patterns}/bridge/clock/bells/melody.dart | 0 {bin => patterns}/bridge/clock/bells/signal.dart | 0 {bin => patterns}/bridge/clock/clocks/clock.dart | 0 {bin => patterns}/bridge/clock/clocks/interval.dart | 0 {bin => patterns}/bridge/clock/clocks/once.dart | 0 {bin => patterns}/bridge/clock/main.dart | 0 {bin => patterns}/bridge/devices_remote_control/README.md | 0 .../bridge/devices_remote_control/devices/device.dart | 0 .../bridge/devices_remote_control/devices/empty_device.dart | 0 .../bridge/devices_remote_control/devices/radio.dart | 0 {bin => patterns}/bridge/devices_remote_control/devices/tv.dart | 0 {bin => patterns}/bridge/devices_remote_control/main.dart | 0 .../devices_remote_control/remotes/basic_advance_remote.dart | 0 .../bridge/devices_remote_control/remotes/remote.dart | 0 {bin => patterns}/builder/cars/README.md | 0 {bin => patterns}/builder/cars/builders/builder.dart | 0 {bin => patterns}/builder/cars/builders/car_builder.dart | 0 {bin => patterns}/builder/cars/builders/car_manual_builder.dart | 0 {bin => patterns}/builder/cars/cars/car.dart | 0 {bin => patterns}/builder/cars/cars/car_manual.dart | 0 {bin => patterns}/builder/cars/cars/car_type.dart | 0 {bin => patterns}/builder/cars/components/engine.dart | 0 {bin => patterns}/builder/cars/director/director.dart | 0 {bin => patterns}/builder/cars/director/gps_navigation.dart | 0 {bin => patterns}/builder/cars/director/transmission.dart | 0 {bin => patterns}/builder/cars/director/trip_computer.dart | 0 {bin => patterns}/builder/cars/main.dart | 0 {bin => patterns}/builder/color_text_format/README.md | 0 .../builder/color_text_format/color_reader/color_text_reader.dart | 0 .../builder/color_text_format/converters/console_converter.dart | 0 .../builder/color_text_format/converters/converter.dart | 0 .../builder/color_text_format/converters/html_converter.dart | 0 .../builder/color_text_format/converters/json_converter.dart | 0 .../builder/color_text_format/formats/console_format.dart | 0 .../builder/color_text_format/formats/html_format.dart | 0 .../builder/color_text_format/formats/json_format.dart | 0 .../builder/color_text_format/formats/text_format.dart | 0 {bin => patterns}/builder/color_text_format/main.dart | 0 {bin => patterns}/prototype/example/README.md | 0 {bin => patterns}/prototype/example/main.dart | 0 {bin => patterns}/prototype/example/shape/circle.dart | 0 {bin => patterns}/prototype/example/shape/rectangle.dart | 0 {bin => patterns}/prototype/example/shape/shape.dart | 0 55 files changed, 0 insertions(+), 0 deletions(-) rename {bin => patterns}/adapter/square_round_conflict/README.md (100%) rename {bin => patterns}/adapter/square_round_conflict/main.dart (100%) rename {bin => patterns}/adapter/text_graphics/README.md (100%) rename {bin => patterns}/adapter/text_graphics/engine/shape_engine.dart (100%) rename {bin => patterns}/adapter/text_graphics/main.dart (100%) rename {bin => patterns}/adapter/text_graphics/shapes/shape.dart (100%) rename {bin => patterns}/adapter/text_graphics/shapes/shape_adapter.dart (100%) rename {bin => patterns}/adapter/text_graphics/third_party_graphics_lib/graph_element.dart (100%) rename {bin => patterns}/adapter/text_graphics/third_party_graphics_lib/star.dart (100%) rename {bin => patterns}/adapter/text_graphics/third_party_graphics_lib/wave.dart (100%) rename {bin => patterns}/bridge/clock/README.md (100%) rename {bin => patterns}/bridge/clock/bells/bell.dart (100%) rename {bin => patterns}/bridge/clock/bells/melody.dart (100%) rename {bin => patterns}/bridge/clock/bells/signal.dart (100%) rename {bin => patterns}/bridge/clock/clocks/clock.dart (100%) rename {bin => patterns}/bridge/clock/clocks/interval.dart (100%) rename {bin => patterns}/bridge/clock/clocks/once.dart (100%) rename {bin => patterns}/bridge/clock/main.dart (100%) rename {bin => patterns}/bridge/devices_remote_control/README.md (100%) rename {bin => patterns}/bridge/devices_remote_control/devices/device.dart (100%) rename {bin => patterns}/bridge/devices_remote_control/devices/empty_device.dart (100%) rename {bin => patterns}/bridge/devices_remote_control/devices/radio.dart (100%) rename {bin => patterns}/bridge/devices_remote_control/devices/tv.dart (100%) rename {bin => patterns}/bridge/devices_remote_control/main.dart (100%) rename {bin => patterns}/bridge/devices_remote_control/remotes/basic_advance_remote.dart (100%) rename {bin => patterns}/bridge/devices_remote_control/remotes/remote.dart (100%) rename {bin => patterns}/builder/cars/README.md (100%) rename {bin => patterns}/builder/cars/builders/builder.dart (100%) rename {bin => patterns}/builder/cars/builders/car_builder.dart (100%) rename {bin => patterns}/builder/cars/builders/car_manual_builder.dart (100%) rename {bin => patterns}/builder/cars/cars/car.dart (100%) rename {bin => patterns}/builder/cars/cars/car_manual.dart (100%) rename {bin => patterns}/builder/cars/cars/car_type.dart (100%) rename {bin => patterns}/builder/cars/components/engine.dart (100%) rename {bin => patterns}/builder/cars/director/director.dart (100%) rename {bin => patterns}/builder/cars/director/gps_navigation.dart (100%) rename {bin => patterns}/builder/cars/director/transmission.dart (100%) rename {bin => patterns}/builder/cars/director/trip_computer.dart (100%) rename {bin => patterns}/builder/cars/main.dart (100%) rename {bin => patterns}/builder/color_text_format/README.md (100%) rename {bin => patterns}/builder/color_text_format/color_reader/color_text_reader.dart (100%) rename {bin => patterns}/builder/color_text_format/converters/console_converter.dart (100%) rename {bin => patterns}/builder/color_text_format/converters/converter.dart (100%) rename {bin => patterns}/builder/color_text_format/converters/html_converter.dart (100%) rename {bin => patterns}/builder/color_text_format/converters/json_converter.dart (100%) rename {bin => patterns}/builder/color_text_format/formats/console_format.dart (100%) rename {bin => patterns}/builder/color_text_format/formats/html_format.dart (100%) rename {bin => patterns}/builder/color_text_format/formats/json_format.dart (100%) rename {bin => patterns}/builder/color_text_format/formats/text_format.dart (100%) rename {bin => patterns}/builder/color_text_format/main.dart (100%) rename {bin => patterns}/prototype/example/README.md (100%) rename {bin => patterns}/prototype/example/main.dart (100%) rename {bin => patterns}/prototype/example/shape/circle.dart (100%) rename {bin => patterns}/prototype/example/shape/rectangle.dart (100%) rename {bin => patterns}/prototype/example/shape/shape.dart (100%) diff --git a/bin/adapter/square_round_conflict/README.md b/patterns/adapter/square_round_conflict/README.md similarity index 100% rename from bin/adapter/square_round_conflict/README.md rename to patterns/adapter/square_round_conflict/README.md diff --git a/bin/adapter/square_round_conflict/main.dart b/patterns/adapter/square_round_conflict/main.dart similarity index 100% rename from bin/adapter/square_round_conflict/main.dart rename to patterns/adapter/square_round_conflict/main.dart diff --git a/bin/adapter/text_graphics/README.md b/patterns/adapter/text_graphics/README.md similarity index 100% rename from bin/adapter/text_graphics/README.md rename to patterns/adapter/text_graphics/README.md diff --git a/bin/adapter/text_graphics/engine/shape_engine.dart b/patterns/adapter/text_graphics/engine/shape_engine.dart similarity index 100% rename from bin/adapter/text_graphics/engine/shape_engine.dart rename to patterns/adapter/text_graphics/engine/shape_engine.dart diff --git a/bin/adapter/text_graphics/main.dart b/patterns/adapter/text_graphics/main.dart similarity index 100% rename from bin/adapter/text_graphics/main.dart rename to patterns/adapter/text_graphics/main.dart diff --git a/bin/adapter/text_graphics/shapes/shape.dart b/patterns/adapter/text_graphics/shapes/shape.dart similarity index 100% rename from bin/adapter/text_graphics/shapes/shape.dart rename to patterns/adapter/text_graphics/shapes/shape.dart diff --git a/bin/adapter/text_graphics/shapes/shape_adapter.dart b/patterns/adapter/text_graphics/shapes/shape_adapter.dart similarity index 100% rename from bin/adapter/text_graphics/shapes/shape_adapter.dart rename to patterns/adapter/text_graphics/shapes/shape_adapter.dart diff --git a/bin/adapter/text_graphics/third_party_graphics_lib/graph_element.dart b/patterns/adapter/text_graphics/third_party_graphics_lib/graph_element.dart similarity index 100% rename from bin/adapter/text_graphics/third_party_graphics_lib/graph_element.dart rename to patterns/adapter/text_graphics/third_party_graphics_lib/graph_element.dart diff --git a/bin/adapter/text_graphics/third_party_graphics_lib/star.dart b/patterns/adapter/text_graphics/third_party_graphics_lib/star.dart similarity index 100% rename from bin/adapter/text_graphics/third_party_graphics_lib/star.dart rename to patterns/adapter/text_graphics/third_party_graphics_lib/star.dart diff --git a/bin/adapter/text_graphics/third_party_graphics_lib/wave.dart b/patterns/adapter/text_graphics/third_party_graphics_lib/wave.dart similarity index 100% rename from bin/adapter/text_graphics/third_party_graphics_lib/wave.dart rename to patterns/adapter/text_graphics/third_party_graphics_lib/wave.dart diff --git a/bin/bridge/clock/README.md b/patterns/bridge/clock/README.md similarity index 100% rename from bin/bridge/clock/README.md rename to patterns/bridge/clock/README.md diff --git a/bin/bridge/clock/bells/bell.dart b/patterns/bridge/clock/bells/bell.dart similarity index 100% rename from bin/bridge/clock/bells/bell.dart rename to patterns/bridge/clock/bells/bell.dart diff --git a/bin/bridge/clock/bells/melody.dart b/patterns/bridge/clock/bells/melody.dart similarity index 100% rename from bin/bridge/clock/bells/melody.dart rename to patterns/bridge/clock/bells/melody.dart diff --git a/bin/bridge/clock/bells/signal.dart b/patterns/bridge/clock/bells/signal.dart similarity index 100% rename from bin/bridge/clock/bells/signal.dart rename to patterns/bridge/clock/bells/signal.dart diff --git a/bin/bridge/clock/clocks/clock.dart b/patterns/bridge/clock/clocks/clock.dart similarity index 100% rename from bin/bridge/clock/clocks/clock.dart rename to patterns/bridge/clock/clocks/clock.dart diff --git a/bin/bridge/clock/clocks/interval.dart b/patterns/bridge/clock/clocks/interval.dart similarity index 100% rename from bin/bridge/clock/clocks/interval.dart rename to patterns/bridge/clock/clocks/interval.dart diff --git a/bin/bridge/clock/clocks/once.dart b/patterns/bridge/clock/clocks/once.dart similarity index 100% rename from bin/bridge/clock/clocks/once.dart rename to patterns/bridge/clock/clocks/once.dart diff --git a/bin/bridge/clock/main.dart b/patterns/bridge/clock/main.dart similarity index 100% rename from bin/bridge/clock/main.dart rename to patterns/bridge/clock/main.dart diff --git a/bin/bridge/devices_remote_control/README.md b/patterns/bridge/devices_remote_control/README.md similarity index 100% rename from bin/bridge/devices_remote_control/README.md rename to patterns/bridge/devices_remote_control/README.md diff --git a/bin/bridge/devices_remote_control/devices/device.dart b/patterns/bridge/devices_remote_control/devices/device.dart similarity index 100% rename from bin/bridge/devices_remote_control/devices/device.dart rename to patterns/bridge/devices_remote_control/devices/device.dart diff --git a/bin/bridge/devices_remote_control/devices/empty_device.dart b/patterns/bridge/devices_remote_control/devices/empty_device.dart similarity index 100% rename from bin/bridge/devices_remote_control/devices/empty_device.dart rename to patterns/bridge/devices_remote_control/devices/empty_device.dart diff --git a/bin/bridge/devices_remote_control/devices/radio.dart b/patterns/bridge/devices_remote_control/devices/radio.dart similarity index 100% rename from bin/bridge/devices_remote_control/devices/radio.dart rename to patterns/bridge/devices_remote_control/devices/radio.dart diff --git a/bin/bridge/devices_remote_control/devices/tv.dart b/patterns/bridge/devices_remote_control/devices/tv.dart similarity index 100% rename from bin/bridge/devices_remote_control/devices/tv.dart rename to patterns/bridge/devices_remote_control/devices/tv.dart diff --git a/bin/bridge/devices_remote_control/main.dart b/patterns/bridge/devices_remote_control/main.dart similarity index 100% rename from bin/bridge/devices_remote_control/main.dart rename to patterns/bridge/devices_remote_control/main.dart diff --git a/bin/bridge/devices_remote_control/remotes/basic_advance_remote.dart b/patterns/bridge/devices_remote_control/remotes/basic_advance_remote.dart similarity index 100% rename from bin/bridge/devices_remote_control/remotes/basic_advance_remote.dart rename to patterns/bridge/devices_remote_control/remotes/basic_advance_remote.dart diff --git a/bin/bridge/devices_remote_control/remotes/remote.dart b/patterns/bridge/devices_remote_control/remotes/remote.dart similarity index 100% rename from bin/bridge/devices_remote_control/remotes/remote.dart rename to patterns/bridge/devices_remote_control/remotes/remote.dart diff --git a/bin/builder/cars/README.md b/patterns/builder/cars/README.md similarity index 100% rename from bin/builder/cars/README.md rename to patterns/builder/cars/README.md diff --git a/bin/builder/cars/builders/builder.dart b/patterns/builder/cars/builders/builder.dart similarity index 100% rename from bin/builder/cars/builders/builder.dart rename to patterns/builder/cars/builders/builder.dart diff --git a/bin/builder/cars/builders/car_builder.dart b/patterns/builder/cars/builders/car_builder.dart similarity index 100% rename from bin/builder/cars/builders/car_builder.dart rename to patterns/builder/cars/builders/car_builder.dart diff --git a/bin/builder/cars/builders/car_manual_builder.dart b/patterns/builder/cars/builders/car_manual_builder.dart similarity index 100% rename from bin/builder/cars/builders/car_manual_builder.dart rename to patterns/builder/cars/builders/car_manual_builder.dart diff --git a/bin/builder/cars/cars/car.dart b/patterns/builder/cars/cars/car.dart similarity index 100% rename from bin/builder/cars/cars/car.dart rename to patterns/builder/cars/cars/car.dart diff --git a/bin/builder/cars/cars/car_manual.dart b/patterns/builder/cars/cars/car_manual.dart similarity index 100% rename from bin/builder/cars/cars/car_manual.dart rename to patterns/builder/cars/cars/car_manual.dart diff --git a/bin/builder/cars/cars/car_type.dart b/patterns/builder/cars/cars/car_type.dart similarity index 100% rename from bin/builder/cars/cars/car_type.dart rename to patterns/builder/cars/cars/car_type.dart diff --git a/bin/builder/cars/components/engine.dart b/patterns/builder/cars/components/engine.dart similarity index 100% rename from bin/builder/cars/components/engine.dart rename to patterns/builder/cars/components/engine.dart diff --git a/bin/builder/cars/director/director.dart b/patterns/builder/cars/director/director.dart similarity index 100% rename from bin/builder/cars/director/director.dart rename to patterns/builder/cars/director/director.dart diff --git a/bin/builder/cars/director/gps_navigation.dart b/patterns/builder/cars/director/gps_navigation.dart similarity index 100% rename from bin/builder/cars/director/gps_navigation.dart rename to patterns/builder/cars/director/gps_navigation.dart diff --git a/bin/builder/cars/director/transmission.dart b/patterns/builder/cars/director/transmission.dart similarity index 100% rename from bin/builder/cars/director/transmission.dart rename to patterns/builder/cars/director/transmission.dart diff --git a/bin/builder/cars/director/trip_computer.dart b/patterns/builder/cars/director/trip_computer.dart similarity index 100% rename from bin/builder/cars/director/trip_computer.dart rename to patterns/builder/cars/director/trip_computer.dart diff --git a/bin/builder/cars/main.dart b/patterns/builder/cars/main.dart similarity index 100% rename from bin/builder/cars/main.dart rename to patterns/builder/cars/main.dart diff --git a/bin/builder/color_text_format/README.md b/patterns/builder/color_text_format/README.md similarity index 100% rename from bin/builder/color_text_format/README.md rename to patterns/builder/color_text_format/README.md diff --git a/bin/builder/color_text_format/color_reader/color_text_reader.dart b/patterns/builder/color_text_format/color_reader/color_text_reader.dart similarity index 100% rename from bin/builder/color_text_format/color_reader/color_text_reader.dart rename to patterns/builder/color_text_format/color_reader/color_text_reader.dart diff --git a/bin/builder/color_text_format/converters/console_converter.dart b/patterns/builder/color_text_format/converters/console_converter.dart similarity index 100% rename from bin/builder/color_text_format/converters/console_converter.dart rename to patterns/builder/color_text_format/converters/console_converter.dart diff --git a/bin/builder/color_text_format/converters/converter.dart b/patterns/builder/color_text_format/converters/converter.dart similarity index 100% rename from bin/builder/color_text_format/converters/converter.dart rename to patterns/builder/color_text_format/converters/converter.dart diff --git a/bin/builder/color_text_format/converters/html_converter.dart b/patterns/builder/color_text_format/converters/html_converter.dart similarity index 100% rename from bin/builder/color_text_format/converters/html_converter.dart rename to patterns/builder/color_text_format/converters/html_converter.dart diff --git a/bin/builder/color_text_format/converters/json_converter.dart b/patterns/builder/color_text_format/converters/json_converter.dart similarity index 100% rename from bin/builder/color_text_format/converters/json_converter.dart rename to patterns/builder/color_text_format/converters/json_converter.dart diff --git a/bin/builder/color_text_format/formats/console_format.dart b/patterns/builder/color_text_format/formats/console_format.dart similarity index 100% rename from bin/builder/color_text_format/formats/console_format.dart rename to patterns/builder/color_text_format/formats/console_format.dart diff --git a/bin/builder/color_text_format/formats/html_format.dart b/patterns/builder/color_text_format/formats/html_format.dart similarity index 100% rename from bin/builder/color_text_format/formats/html_format.dart rename to patterns/builder/color_text_format/formats/html_format.dart diff --git a/bin/builder/color_text_format/formats/json_format.dart b/patterns/builder/color_text_format/formats/json_format.dart similarity index 100% rename from bin/builder/color_text_format/formats/json_format.dart rename to patterns/builder/color_text_format/formats/json_format.dart diff --git a/bin/builder/color_text_format/formats/text_format.dart b/patterns/builder/color_text_format/formats/text_format.dart similarity index 100% rename from bin/builder/color_text_format/formats/text_format.dart rename to patterns/builder/color_text_format/formats/text_format.dart diff --git a/bin/builder/color_text_format/main.dart b/patterns/builder/color_text_format/main.dart similarity index 100% rename from bin/builder/color_text_format/main.dart rename to patterns/builder/color_text_format/main.dart diff --git a/bin/prototype/example/README.md b/patterns/prototype/example/README.md similarity index 100% rename from bin/prototype/example/README.md rename to patterns/prototype/example/README.md diff --git a/bin/prototype/example/main.dart b/patterns/prototype/example/main.dart similarity index 100% rename from bin/prototype/example/main.dart rename to patterns/prototype/example/main.dart diff --git a/bin/prototype/example/shape/circle.dart b/patterns/prototype/example/shape/circle.dart similarity index 100% rename from bin/prototype/example/shape/circle.dart rename to patterns/prototype/example/shape/circle.dart diff --git a/bin/prototype/example/shape/rectangle.dart b/patterns/prototype/example/shape/rectangle.dart similarity index 100% rename from bin/prototype/example/shape/rectangle.dart rename to patterns/prototype/example/shape/rectangle.dart diff --git a/bin/prototype/example/shape/shape.dart b/patterns/prototype/example/shape/shape.dart similarity index 100% rename from bin/prototype/example/shape/shape.dart rename to patterns/prototype/example/shape/shape.dart From 43c1eb9765ca3eabfcdeaf27805c0ba959bb3370 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 21 Dec 2021 15:19:42 +0200 Subject: [PATCH 043/479] Move BorderStyle to primitives.dart --- lib/text_canvas/primitives.dart | 61 +++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/lib/text_canvas/primitives.dart b/lib/text_canvas/primitives.dart index 24135c3..b832a42 100644 --- a/lib/text_canvas/primitives.dart +++ b/lib/text_canvas/primitives.dart @@ -19,3 +19,64 @@ class Point { Point(this.x, this.y); } + +class BorderStyle { + static final BorderStyle bold = BorderStyle.fromBorderText( + '▄▄▄▄' + '█ █' + '▀▀▀▀', + ); + + static final BorderStyle double = BorderStyle.fromBorderText( + '╔══╗' + '║ ║' + '╚══╝', + ); + + static final BorderStyle single = BorderStyle.fromBorderText( + '┌──┐' + '│ │' + '└──┘', + ); + + static final BorderStyle round = BorderStyle.fromBorderText( + '╭━━╮' + ' ' + ' ', + ); + + final String topLeft; + final String topRight; + final String bottomRight; + final String bottomLeft; + + final String top; + final String bottom; + + final String left; + final String right; + + BorderStyle._( + this.topLeft, + this.topRight, + this.bottomRight, + this.bottomLeft, + this.top, + this.bottom, + this.left, + this.right, + ); + + factory BorderStyle.fromBorderText(String text) { + return BorderStyle._( + text[0], + text[3], + text[11], + text[8], + text[1], + text[9], + text[4], + text[7], + ); + } +} From 4fdf67e2b894adfd1c1808675747ea5e418d432a Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 21 Dec 2021 15:20:51 +0200 Subject: [PATCH 044/479] Add draw char and border to Canvas --- lib/text_canvas/canvas.dart | 41 ++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/text_canvas/canvas.dart b/lib/text_canvas/canvas.dart index acaded2..0c47c23 100644 --- a/lib/text_canvas/canvas.dart +++ b/lib/text_canvas/canvas.dart @@ -25,7 +25,7 @@ class Canvas { var translate = Point(0, 0); var penColor = Color.black; - void setPixel(int x, int y) { + void setPixel(int x, int y, [int? colorCode]) { x += translate.x; y += translate.y; @@ -40,6 +40,17 @@ class Canvas { } } + void char(int x, int y, String char) { + x += translate.x; + y += translate.y; + + if (x < 0 || x > width - 1 || y < 0 || y > height - 1) { + return; + } + + _pixel[y][x] = char.codeUnits[0]; + } + var _currPos = Point(0, 0); void moveTo(int x, int y) => _currPos = Point(x, y); @@ -101,6 +112,34 @@ class Canvas { } while (x < 0); } + void border(int width, int height, BorderStyle borderStyle) { + assert(width >= 4); + assert(height >= 4); + + char(0, 0, borderStyle.topLeft); + char(width - 1, 0, borderStyle.topRight); + char(width - 1, height - 1, borderStyle.bottomRight); + char(0, height - 1, borderStyle.bottomLeft); + + for (var x = 1; x < width - 1; x++) { + char(x, 0, borderStyle.top); + } + + for (var y = 1; y < height - 1; y++) { + char(width - 1, y, borderStyle.right); + } + + for (var y = 1; y < height - 1; y++) { + char(0, y, borderStyle.right); + } + + for (var x = 1; x < width - 1; x++) { + char(x, height - 1, borderStyle.bottom); + } + } + + void text(String text, {int widthCenter = -1, int heightCenter = -1}) {} + @override String toString() { return _pixel From 247f09c9593c7c5823e0e2087b469b596bd3ddb9 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 21 Dec 2021 15:21:33 +0200 Subject: [PATCH 045/479] Add canvas_text import file --- lib/text_canvas.dart | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 lib/text_canvas.dart diff --git a/lib/text_canvas.dart b/lib/text_canvas.dart new file mode 100644 index 0000000..0acce3d --- /dev/null +++ b/lib/text_canvas.dart @@ -0,0 +1,4 @@ +library design_pttern_dart; + +export 'text_canvas/canvas.dart'; +export 'text_canvas/primitives.dart'; From 83580abfcf45c62989caa3c32fcfa85558bca217 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 21 Dec 2021 15:54:31 +0200 Subject: [PATCH 046/479] Fix border drawer. Use real width --- lib/text_canvas/canvas.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/text_canvas/canvas.dart b/lib/text_canvas/canvas.dart index 0c47c23..67086f2 100644 --- a/lib/text_canvas/canvas.dart +++ b/lib/text_canvas/canvas.dart @@ -44,7 +44,7 @@ class Canvas { x += translate.x; y += translate.y; - if (x < 0 || x > width - 1 || y < 0 || y > height - 1) { + if (x < 0 || x > width * lineStretch - 1 || y < 0 || y > height - 1) { return; } @@ -113,8 +113,8 @@ class Canvas { } void border(int width, int height, BorderStyle borderStyle) { - assert(width >= 4); - assert(height >= 4); + assert(width >= 2); + assert(height >= 2); char(0, 0, borderStyle.topLeft); char(width - 1, 0, borderStyle.topRight); From 0a339335ed8fb77b1a8c44a7f5087e9fd9fd26bb Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 21 Dec 2021 16:11:16 +0200 Subject: [PATCH 047/479] Add drawing text to the canvas --- lib/text_canvas/canvas.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/text_canvas/canvas.dart b/lib/text_canvas/canvas.dart index 67086f2..e63a1ff 100644 --- a/lib/text_canvas/canvas.dart +++ b/lib/text_canvas/canvas.dart @@ -138,7 +138,17 @@ class Canvas { } } - void text(String text, {int widthCenter = -1, int heightCenter = -1}) {} + void text(String text, {int widthCenter = -1, int heightCenter = -1}) { + widthCenter = widthCenter < 0 ? text.length : widthCenter; + heightCenter = heightCenter < 0 ? 1 : heightCenter; + + var x = (widthCenter - text.length) ~/ 2; + var y = heightCenter ~/ 2; + + for (final c in text.split('')) { + char(x++, y, c); + } + } @override String toString() { From 59013c4cfa1a2aa69987145c0894e82f7edb5cc6 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 21 Dec 2021 16:21:37 +0200 Subject: [PATCH 048/479] Create new brabch --- lib/text_canvas.dart | 4 + lib/text_canvas/canvas.dart | 163 ++++++++++++++++++ lib/text_canvas/primitives.dart | 82 +++++++++ .../adapter/square_round_conflict/README.md | 12 ++ .../adapter/square_round_conflict/main.dart | 60 +++++++ patterns/adapter/text_graphics/README.md | 84 +++++++++ .../text_graphics/engine/shape_engine.dart | 26 +++ patterns/adapter/text_graphics/main.dart | 51 ++++++ .../adapter/text_graphics/shapes/shape.dart | 41 +++++ .../text_graphics/shapes/shape_adapter.dart | 24 +++ .../graph_element.dart | 5 + .../third_party_graphics_lib/star.dart | 29 ++++ .../third_party_graphics_lib/wave.dart | 24 +++ patterns/bridge/clock/README.md | 30 ++++ patterns/bridge/clock/bells/bell.dart | 5 + patterns/bridge/clock/bells/melody.dart | 11 ++ patterns/bridge/clock/bells/signal.dart | 11 ++ patterns/bridge/clock/clocks/clock.dart | 18 ++ patterns/bridge/clock/clocks/interval.dart | 19 ++ patterns/bridge/clock/clocks/once.dart | 15 ++ patterns/bridge/clock/main.dart | 22 +++ .../bridge/devices_remote_control/README.md | 71 ++++++++ .../devices/device.dart | 15 ++ .../devices/empty_device.dart | 28 +++ .../devices_remote_control/devices/radio.dart | 43 +++++ .../devices_remote_control/devices/tv.dart | 43 +++++ .../bridge/devices_remote_control/main.dart | 26 +++ .../remotes/basic_advance_remote.dart | 76 ++++++++ .../remotes/remote.dart | 11 ++ patterns/builder/cars/README.md | 19 ++ patterns/builder/cars/builders/builder.dart | 23 +++ .../builder/cars/builders/car_builder.dart | 35 ++++ .../cars/builders/car_manual_builder.dart | 45 +++++ patterns/builder/cars/cars/car.dart | 35 ++++ patterns/builder/cars/cars/car_manual.dart | 53 ++++++ patterns/builder/cars/cars/car_type.dart | 5 + patterns/builder/cars/components/engine.dart | 28 +++ patterns/builder/cars/director/director.dart | 44 +++++ .../builder/cars/director/gps_navigation.dart | 16 ++ .../builder/cars/director/transmission.dart | 9 + .../builder/cars/director/trip_computer.dart | 21 +++ patterns/builder/cars/main.dart | 38 ++++ patterns/builder/color_text_format/README.md | 75 ++++++++ .../color_reader/color_text_reader.dart | 22 +++ .../converters/console_converter.dart | 17 ++ .../converters/converter.dart | 10 ++ .../converters/html_converter.dart | 24 +++ .../converters/json_converter.dart | 28 +++ .../formats/console_format.dart | 24 +++ .../formats/html_format.dart | 81 +++++++++ .../formats/json_format.dart | 13 ++ .../formats/text_format.dart | 9 + patterns/builder/color_text_format/main.dart | 20 +++ .../products_and_boxes/diagram/diagram.dart | 32 ++++ .../diagram/render_element.dart | 30 ++++ .../composite/products_and_boxes/main.dart | 41 +++++ .../products_and_boxes/node/node.dart | 45 +++++ .../products_and_boxes/products/box.dart | 41 +++++ .../products_and_boxes/products/product.dart | 7 + .../products/product_leaf.dart | 21 +++ patterns/prototype/example/README.md | 11 ++ patterns/prototype/example/main.dart | 27 +++ patterns/prototype/example/shape/circle.dart | 28 +++ .../prototype/example/shape/rectangle.dart | 26 +++ patterns/prototype/example/shape/shape.dart | 14 ++ 65 files changed, 2066 insertions(+) create mode 100644 lib/text_canvas.dart create mode 100644 lib/text_canvas/canvas.dart create mode 100644 lib/text_canvas/primitives.dart create mode 100644 patterns/adapter/square_round_conflict/README.md create mode 100644 patterns/adapter/square_round_conflict/main.dart create mode 100644 patterns/adapter/text_graphics/README.md create mode 100644 patterns/adapter/text_graphics/engine/shape_engine.dart create mode 100644 patterns/adapter/text_graphics/main.dart create mode 100644 patterns/adapter/text_graphics/shapes/shape.dart create mode 100644 patterns/adapter/text_graphics/shapes/shape_adapter.dart create mode 100644 patterns/adapter/text_graphics/third_party_graphics_lib/graph_element.dart create mode 100644 patterns/adapter/text_graphics/third_party_graphics_lib/star.dart create mode 100644 patterns/adapter/text_graphics/third_party_graphics_lib/wave.dart create mode 100644 patterns/bridge/clock/README.md create mode 100644 patterns/bridge/clock/bells/bell.dart create mode 100644 patterns/bridge/clock/bells/melody.dart create mode 100644 patterns/bridge/clock/bells/signal.dart create mode 100644 patterns/bridge/clock/clocks/clock.dart create mode 100644 patterns/bridge/clock/clocks/interval.dart create mode 100644 patterns/bridge/clock/clocks/once.dart create mode 100644 patterns/bridge/clock/main.dart create mode 100644 patterns/bridge/devices_remote_control/README.md create mode 100644 patterns/bridge/devices_remote_control/devices/device.dart create mode 100644 patterns/bridge/devices_remote_control/devices/empty_device.dart create mode 100644 patterns/bridge/devices_remote_control/devices/radio.dart create mode 100644 patterns/bridge/devices_remote_control/devices/tv.dart create mode 100644 patterns/bridge/devices_remote_control/main.dart create mode 100644 patterns/bridge/devices_remote_control/remotes/basic_advance_remote.dart create mode 100644 patterns/bridge/devices_remote_control/remotes/remote.dart create mode 100644 patterns/builder/cars/README.md create mode 100644 patterns/builder/cars/builders/builder.dart create mode 100644 patterns/builder/cars/builders/car_builder.dart create mode 100644 patterns/builder/cars/builders/car_manual_builder.dart create mode 100644 patterns/builder/cars/cars/car.dart create mode 100644 patterns/builder/cars/cars/car_manual.dart create mode 100644 patterns/builder/cars/cars/car_type.dart create mode 100644 patterns/builder/cars/components/engine.dart create mode 100644 patterns/builder/cars/director/director.dart create mode 100644 patterns/builder/cars/director/gps_navigation.dart create mode 100644 patterns/builder/cars/director/transmission.dart create mode 100644 patterns/builder/cars/director/trip_computer.dart create mode 100644 patterns/builder/cars/main.dart create mode 100644 patterns/builder/color_text_format/README.md create mode 100644 patterns/builder/color_text_format/color_reader/color_text_reader.dart create mode 100644 patterns/builder/color_text_format/converters/console_converter.dart create mode 100644 patterns/builder/color_text_format/converters/converter.dart create mode 100644 patterns/builder/color_text_format/converters/html_converter.dart create mode 100644 patterns/builder/color_text_format/converters/json_converter.dart create mode 100644 patterns/builder/color_text_format/formats/console_format.dart create mode 100644 patterns/builder/color_text_format/formats/html_format.dart create mode 100644 patterns/builder/color_text_format/formats/json_format.dart create mode 100644 patterns/builder/color_text_format/formats/text_format.dart create mode 100644 patterns/builder/color_text_format/main.dart create mode 100644 patterns/composite/products_and_boxes/diagram/diagram.dart create mode 100644 patterns/composite/products_and_boxes/diagram/render_element.dart create mode 100644 patterns/composite/products_and_boxes/main.dart create mode 100644 patterns/composite/products_and_boxes/node/node.dart create mode 100644 patterns/composite/products_and_boxes/products/box.dart create mode 100644 patterns/composite/products_and_boxes/products/product.dart create mode 100644 patterns/composite/products_and_boxes/products/product_leaf.dart create mode 100644 patterns/prototype/example/README.md create mode 100644 patterns/prototype/example/main.dart create mode 100644 patterns/prototype/example/shape/circle.dart create mode 100644 patterns/prototype/example/shape/rectangle.dart create mode 100644 patterns/prototype/example/shape/shape.dart diff --git a/lib/text_canvas.dart b/lib/text_canvas.dart new file mode 100644 index 0000000..0acce3d --- /dev/null +++ b/lib/text_canvas.dart @@ -0,0 +1,4 @@ +library design_pttern_dart; + +export 'text_canvas/canvas.dart'; +export 'text_canvas/primitives.dart'; diff --git a/lib/text_canvas/canvas.dart b/lib/text_canvas/canvas.dart new file mode 100644 index 0000000..e63a1ff --- /dev/null +++ b/lib/text_canvas/canvas.dart @@ -0,0 +1,163 @@ +import 'dart:typed_data'; + +import 'primitives.dart'; + +class Canvas { + final int width; + final int height; + late List _pixel; + final int lineStretch; + + Canvas( + this.width, + this.height, [ + this.lineStretch = 3, + ]) { + final realWidth = width * lineStretch; + _pixel = [ + for (var i = 0; i < height; i++) + Int16List.fromList( + List.filled(realWidth, Color.light.code), + ), + ]; + } + + var translate = Point(0, 0); + var penColor = Color.black; + + void setPixel(int x, int y, [int? colorCode]) { + x += translate.x; + y += translate.y; + + if (x < 0 || x > width - 1 || y < 0 || y > height - 1) { + return; + } + + final realY = y; + final realX = (x * lineStretch) ~/ 1; + for (var i = 0; i < lineStretch; i++) { + _pixel[realY][realX + i] = penColor.code; + } + } + + void char(int x, int y, String char) { + x += translate.x; + y += translate.y; + + if (x < 0 || x > width * lineStretch - 1 || y < 0 || y > height - 1) { + return; + } + + _pixel[y][x] = char.codeUnits[0]; + } + + var _currPos = Point(0, 0); + + void moveTo(int x, int y) => _currPos = Point(x, y); + + /// Bresenham's algorithm is taken from here + /// https://gist.github.com/bert/1085538#file-plot_line-c + void lineTo(final int x, final int y) { + final x1 = _currPos.x, y1 = _currPos.y; + + var x0 = x, y0 = y; + + final dx = (x1 - x0).abs(), + sx = x0 < x1 ? 1 : -1, + dy = -(y1 - y0).abs(), + sy = y0 < y1 ? 1 : -1; + + var err = dx + dy; + late int e2; + + while (true) { + setPixel(x0, y0); + if (x0 == x1 && y0 == y1) break; + e2 = 2 * err; + if (e2 >= dy) { + err += dy; + x0 += sx; + } + if (e2 <= dx) { + err += dx; + y0 += sy; + } + } + moveTo(x, y); + } + + void rectangle(int width, int height) { + final x = _currPos.x; + final y = _currPos.y; + lineTo(width + x, y); + lineTo(width + x, height + y); + lineTo(x, height + y); + lineTo(x, y); + } + + /// Bresenham's algorithm is taken from here + /// https://gist.github.com/bert/1085538#file-plot_circle-c + void circle(int radius) { + final xm = _currPos.x; + final ym = _currPos.y; + int x = -radius, y = 0, err = 2 - 2 * radius; + do { + setPixel(xm - x, ym + y); + setPixel(xm - y, ym - x); + setPixel(xm + x, ym - y); + setPixel(xm + y, ym + x); + radius = err; + if (radius > x) err += ++x * 2 + 1; + if (radius <= y) err += ++y * 2 + 1; + } while (x < 0); + } + + void border(int width, int height, BorderStyle borderStyle) { + assert(width >= 2); + assert(height >= 2); + + char(0, 0, borderStyle.topLeft); + char(width - 1, 0, borderStyle.topRight); + char(width - 1, height - 1, borderStyle.bottomRight); + char(0, height - 1, borderStyle.bottomLeft); + + for (var x = 1; x < width - 1; x++) { + char(x, 0, borderStyle.top); + } + + for (var y = 1; y < height - 1; y++) { + char(width - 1, y, borderStyle.right); + } + + for (var y = 1; y < height - 1; y++) { + char(0, y, borderStyle.right); + } + + for (var x = 1; x < width - 1; x++) { + char(x, height - 1, borderStyle.bottom); + } + } + + void text(String text, {int widthCenter = -1, int heightCenter = -1}) { + widthCenter = widthCenter < 0 ? text.length : widthCenter; + heightCenter = heightCenter < 0 ? 1 : heightCenter; + + var x = (widthCenter - text.length) ~/ 2; + var y = heightCenter ~/ 2; + + for (final c in text.split('')) { + char(x++, y, c); + } + } + + @override + String toString() { + return _pixel + .map((e) => e + .map( + (e) => String.fromCharCode(e), + ) + .join('')) + .join('\n'); + } +} diff --git a/lib/text_canvas/primitives.dart b/lib/text_canvas/primitives.dart new file mode 100644 index 0000000..b832a42 --- /dev/null +++ b/lib/text_canvas/primitives.dart @@ -0,0 +1,82 @@ +class Color { + final int code; + + Color(String symbol) + : assert(symbol.length == 1), + code = symbol.codeUnitAt(0); + + static final Color black = Color('█'); + static final Color dark = Color('▓'); + static final Color grey = Color('▒'); + static final Color light = Color('░'); + static final Color white = Color(' '); + static final Color point = Color('■'); +} + +class Point { + final int x; + final int y; + + Point(this.x, this.y); +} + +class BorderStyle { + static final BorderStyle bold = BorderStyle.fromBorderText( + '▄▄▄▄' + '█ █' + '▀▀▀▀', + ); + + static final BorderStyle double = BorderStyle.fromBorderText( + '╔══╗' + '║ ║' + '╚══╝', + ); + + static final BorderStyle single = BorderStyle.fromBorderText( + '┌──┐' + '│ │' + '└──┘', + ); + + static final BorderStyle round = BorderStyle.fromBorderText( + '╭━━╮' + ' ' + ' ', + ); + + final String topLeft; + final String topRight; + final String bottomRight; + final String bottomLeft; + + final String top; + final String bottom; + + final String left; + final String right; + + BorderStyle._( + this.topLeft, + this.topRight, + this.bottomRight, + this.bottomLeft, + this.top, + this.bottom, + this.left, + this.right, + ); + + factory BorderStyle.fromBorderText(String text) { + return BorderStyle._( + text[0], + text[3], + text[11], + text[8], + text[1], + text[9], + text[4], + text[7], + ); + } +} diff --git a/patterns/adapter/square_round_conflict/README.md b/patterns/adapter/square_round_conflict/README.md new file mode 100644 index 0000000..1a37840 --- /dev/null +++ b/patterns/adapter/square_round_conflict/README.md @@ -0,0 +1,12 @@ +# Adapter pattern + +**Description:** +https://refactoring.guru/design-patterns/adapter?#pseudocode + +**Output:** + +``` +Round peg r5 fits round hole r5. +Square peg w2 fits round hole r5. +Square peg w20 does not fit into round hole r5. +``` diff --git a/patterns/adapter/square_round_conflict/main.dart b/patterns/adapter/square_round_conflict/main.dart new file mode 100644 index 0000000..3d5b7d2 --- /dev/null +++ b/patterns/adapter/square_round_conflict/main.dart @@ -0,0 +1,60 @@ +/// EN: Somewhere in client code... +/// +/// RU: Где-то в клиентском коде... +void main() { + // EN: Round fits round, no surprise. + // + // RU: Круглое к круглому — всё работает. + final hole = RoundHole(5); + final rpeg = RoundPeg(5); + if (hole.fits(rpeg)) { + print("Round peg r5 fits round hole r5."); + } + + final smallSqPeg = SquarePeg(2); + final largeSqPeg = SquarePeg(20); + // EN: hole.fits(smallSqPeg); // Won't compile. + // + // RU: hole.fits(smallSqPeg); // Не скомпилируется. + + // EN: Adapter solves the problem. + // + // 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."); + } +} + +class RoundPeg { + final int radius; + + RoundPeg(this.radius); +} + +class SquarePegAdapter implements RoundPeg { + final SquarePeg squarePeg; + + SquarePegAdapter(this.squarePeg); + + @override + int get radius => squarePeg.width; +} + +class SquarePeg { + final int width; + + SquarePeg(this.width); +} + +class RoundHole { + final int radius; + + RoundHole(this.radius); + + bool fits(RoundPeg roundPeg) => radius >= roundPeg.radius; +} diff --git a/patterns/adapter/text_graphics/README.md b/patterns/adapter/text_graphics/README.md new file mode 100644 index 0000000..df037cd --- /dev/null +++ b/patterns/adapter/text_graphics/README.md @@ -0,0 +1,84 @@ +# Adapter pattern + +![image](https://user-images.githubusercontent.com/8049534/146028651-1262b66d-b0ac-4fc9-9487-76a2d588f97e.png) + +**Client code:** +```dart +final renderContent = ShapeEngine( + width: 42, + height: 32, + shapes: [ + // EN: Standard graphics primitives + Rectangle( + x: 1, + y: 1, + width: 12, + height: 6, + color: Color.black, + ), + Rectangle( + x: 4, + y: 3, + width: 6, + height: 9, + color: Color.white, + ), + Circle( + x: 12, + y: 12, + radius: 7, + color: Color.grey, + ), + // EN: Graphics primitives from a third party library + GraphElementAdapter( + x: 11, + y: 20, + color: Color.dark, + graphElement: third_party.Wave(height: 5, length: 25), + ), + GraphElementAdapter( + x: 23, + y: 1, + color: Color.black, + graphElement: third_party.Star(size: 20), + ), + ], + ).render(); + print(renderContent); +``` + +**Output:** +``` +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░███████████████████████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░███░░░░░░ ░░░░░░███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░███░░░░░░░░░░░░░░░░░░░░░░░░ +░░░███░░░░░░ ░░░░░░░░░░░░░░░ ░░░░░░███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░███░░░░░░░░░░░░░░░░░░░░░░░░ +░░░███░░░░░░ ░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░███░░░░░░░░░░░░░░░░░░░░░░░░ +░░░███░░░░░░ ░░░░░░░░░▒▒▒▒▒▒ ░░░░░░███░░░▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░ +░░░█████████ ██████▒▒▒██████ █████████░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░███████████████████████████████████████████████████░░░ +░░░░░░░░░░░░ ░░░▒▒▒░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░███░░░░░░░░░░░░███░░░░░░░░░███░░░░░░░░░░░░███░░░░░░ +░░░░░░░░░░░░ ░░░▒▒▒░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░███░░░░░░███░░░░░░░░░░░░░░░███░░░░░░███░░░░░░░░░ +░░░░░░░░░░░░ ▒▒▒░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░█████████░░░░░░░░░░░░░░░█████████░░░░░░░░░░░░ +░░░░░░░░░░░░ ▒▒▒░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░ ▒▒▒ ░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░███░░░███░░░░░░░░░███░░░███░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░███░░░░░░███░░░███░░░░░░███░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░███░░░░░░░░░███░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░███░░░███░░░░░░░░░███░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░███░░░██████░░░░░░░░░██████░░░███░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░██████░░░░░░░░░░░░░░░░░░░░░██████░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒░░░░░░░░░░░░░░░▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░██████░░░░░░░░░░░░░░░░░░░░░░░░░░░██████░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓▓▓▓░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +``` diff --git a/patterns/adapter/text_graphics/engine/shape_engine.dart b/patterns/adapter/text_graphics/engine/shape_engine.dart new file mode 100644 index 0000000..9568bb4 --- /dev/null +++ b/patterns/adapter/text_graphics/engine/shape_engine.dart @@ -0,0 +1,26 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import '../shapes/shape.dart'; + +class ShapeEngine { + final List shapes; + final int width; + final int height; + + ShapeEngine({ + required this.width, + required this.height, + required this.shapes, + }); + + String render() { + final can = Canvas(width, height, 3); + for (final Shape shape in shapes) { + can + ..translate = Point(shape.x, shape.y) + ..penColor = shape.color; + shape.draw(can); + } + return can.toString(); + } +} diff --git a/patterns/adapter/text_graphics/main.dart b/patterns/adapter/text_graphics/main.dart new file mode 100644 index 0000000..61fdd84 --- /dev/null +++ b/patterns/adapter/text_graphics/main.dart @@ -0,0 +1,51 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import 'engine/shape_engine.dart'; +import 'shapes/shape.dart'; +import 'shapes/shape_adapter.dart'; +import 'third_party_graphics_lib/star.dart' as third_party; +import 'third_party_graphics_lib/wave.dart' as third_party; + +void main() { + final renderContent = ShapeEngine( + width: 42, + height: 32, + shapes: [ + // EN: Standard graphics primitives + Rectangle( + x: 1, + y: 1, + width: 12, + height: 6, + color: Color.black, + ), + Rectangle( + x: 4, + y: 3, + width: 6, + height: 9, + color: Color.white, + ), + Circle( + x: 12, + y: 12, + radius: 7, + color: Color.grey, + ), + // EN: Graphics primitives from a third party library + GraphElementAdapter( + x: 11, + y: 20, + color: Color.dark, + graphElement: third_party.Wave(height: 5, length: 25), + ), + GraphElementAdapter( + x: 23, + y: 1, + color: Color.black, + graphElement: third_party.Star(size: 20), + ), + ], + ).render(); + print(renderContent); +} diff --git a/patterns/adapter/text_graphics/shapes/shape.dart b/patterns/adapter/text_graphics/shapes/shape.dart new file mode 100644 index 0000000..c38c3d9 --- /dev/null +++ b/patterns/adapter/text_graphics/shapes/shape.dart @@ -0,0 +1,41 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +abstract class Shape { + final int x; + final int y; + final Color color; + + Shape(this.x, this.y, this.color); + + void draw(Canvas can); +} + +class Rectangle extends Shape { + final int width; + final int height; + + Rectangle({ + required int x, + required int y, + required this.width, + required this.height, + required Color color, + }) : super(x, y, color); + + @override + void draw(Canvas can) => can.rectangle(width, height); +} + +class Circle extends Shape { + final int radius; + + Circle({ + required int x, + required int y, + required this.radius, + required Color color, + }) : super(x, y, color); + + @override + void draw(Canvas can) => can.circle(radius); +} diff --git a/patterns/adapter/text_graphics/shapes/shape_adapter.dart b/patterns/adapter/text_graphics/shapes/shape_adapter.dart new file mode 100644 index 0000000..ee50eaf --- /dev/null +++ b/patterns/adapter/text_graphics/shapes/shape_adapter.dart @@ -0,0 +1,24 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import '../third_party_graphics_lib/graph_element.dart' as third_party; +import 'shape.dart'; + +class GraphElementAdapter extends Shape { + final third_party.GraphElement graphElement; + + GraphElementAdapter({ + required int x, + required int y, + required Color color, + required this.graphElement, + }) : super(x, y, color); + + @override + void draw(Canvas can) { + final points = graphElement.points; + can.moveTo(points[0], points[1]); + for (var i = 0; i < points.length; i += 2) { + can.lineTo(points[i], points[i + 1]); + } + } +} diff --git a/patterns/adapter/text_graphics/third_party_graphics_lib/graph_element.dart b/patterns/adapter/text_graphics/third_party_graphics_lib/graph_element.dart new file mode 100644 index 0000000..b011227 --- /dev/null +++ b/patterns/adapter/text_graphics/third_party_graphics_lib/graph_element.dart @@ -0,0 +1,5 @@ +import 'dart:typed_data'; + +abstract class GraphElement { + Int32List get points; +} diff --git a/patterns/adapter/text_graphics/third_party_graphics_lib/star.dart b/patterns/adapter/text_graphics/third_party_graphics_lib/star.dart new file mode 100644 index 0000000..970d2e4 --- /dev/null +++ b/patterns/adapter/text_graphics/third_party_graphics_lib/star.dart @@ -0,0 +1,29 @@ +import 'dart:typed_data'; + +import 'graph_element.dart'; + +class Star extends GraphElement { + final int size; + + Star({required this.size}) { + final list = []; + final p1 = (size * .1).toInt(), + p3 = (size * .3).toInt(), + p4 = (size * .4).toInt(), + p7 = (size * .7).toInt(), + p8 = (size * .8).toInt(), + p9 = (size * .9).toInt(); + list.addAll([p1, p9]); + list.addAll([p4, 0]); + list.addAll([p7, p9]); + list.addAll([0, p3]); + list.addAll([p8, p3]); + list.addAll([p1, p9]); + _points = Int32List.fromList(list); + } + + late Int32List _points; + + @override + Int32List get points => _points; +} diff --git a/patterns/adapter/text_graphics/third_party_graphics_lib/wave.dart b/patterns/adapter/text_graphics/third_party_graphics_lib/wave.dart new file mode 100644 index 0000000..0f03434 --- /dev/null +++ b/patterns/adapter/text_graphics/third_party_graphics_lib/wave.dart @@ -0,0 +1,24 @@ +import 'dart:math'; +import 'dart:typed_data'; + +import 'graph_element.dart'; + +class Wave extends GraphElement { + Wave({ + required final int height, + final int length = 50, + final double waveStep = .8, + }) { + final list = []; + for (var x = 0; x < length; x++) { + final y = (height + cos(x / pi / waveStep) * height).toInt(); + list.addAll([x, y]); + } + _points = Int32List.fromList(list); + } + + late Int32List _points; + + @override + Int32List get points => _points; +} diff --git a/patterns/bridge/clock/README.md b/patterns/bridge/clock/README.md new file mode 100644 index 0000000..df3a16c --- /dev/null +++ b/patterns/bridge/clock/README.md @@ -0,0 +1,30 @@ +# Bridge pattern +The idea for the bridge pattern example is taken from [here](https://habr.com/ru/post/85137/). + +**Diagram:** + +![image](https://user-images.githubusercontent.com/8049534/145851578-f6e95355-e2b3-4f94-bda2-c2d1d0de8935.png) + +**Client code:** +```dart +void main() { + final melody = Melody(); + final signal = Signal(); + startClocks([ + Once(seconds: 1, bell: melody), + Once(seconds: 2, bell: signal), + Interval(seconds: 5, bell: melody), + Interval(seconds: 3, bell: signal), + ]); +} + +void startClocks(List clocks) { + for (final clock in clocks) { + clock.start(); + } +} +``` + +**Output:** + +https://user-images.githubusercontent.com/8049534/145850512-27a5e9ea-4f76-4d52-b784-9bc88aee4de8.mp4 diff --git a/patterns/bridge/clock/bells/bell.dart b/patterns/bridge/clock/bells/bell.dart new file mode 100644 index 0000000..50e3cbc --- /dev/null +++ b/patterns/bridge/clock/bells/bell.dart @@ -0,0 +1,5 @@ +abstract class Bell { + void ring(); + + void notify(String message); +} diff --git a/patterns/bridge/clock/bells/melody.dart b/patterns/bridge/clock/bells/melody.dart new file mode 100644 index 0000000..f33c7cf --- /dev/null +++ b/patterns/bridge/clock/bells/melody.dart @@ -0,0 +1,11 @@ +import 'dart:io'; + +import 'bell.dart'; + +class Melody extends Bell { + @override + void notify(String message) => stdout.write('\x1b[32m$message\x1B[0m'); + + @override + void ring() => stdout.write('\x1b[32m🎵\x1B[0m'); +} diff --git a/patterns/bridge/clock/bells/signal.dart b/patterns/bridge/clock/bells/signal.dart new file mode 100644 index 0000000..bf8b733 --- /dev/null +++ b/patterns/bridge/clock/bells/signal.dart @@ -0,0 +1,11 @@ +import 'dart:io'; + +import 'bell.dart'; + +class Signal extends Bell { + @override + void notify(String message) => stdout.write('\x1b[34m$message\x1B[0m'); + + @override + void ring() => stdout.write('\x1b[34m🔉\x1B[0m'); +} diff --git a/patterns/bridge/clock/clocks/clock.dart b/patterns/bridge/clock/clocks/clock.dart new file mode 100644 index 0000000..87c72cb --- /dev/null +++ b/patterns/bridge/clock/clocks/clock.dart @@ -0,0 +1,18 @@ +import '../bells/bell.dart'; + +abstract class Clock { + final int seconds; + final Bell bell; + + Clock({ + required this.seconds, + required this.bell, + }); + + void start(); + + void showBell(String message) { + bell.notify('$message(sec: $seconds): '); + bell.ring(); + } +} diff --git a/patterns/bridge/clock/clocks/interval.dart b/patterns/bridge/clock/clocks/interval.dart new file mode 100644 index 0000000..a935489 --- /dev/null +++ b/patterns/bridge/clock/clocks/interval.dart @@ -0,0 +1,19 @@ +import '../bells/bell.dart'; +import 'clock.dart'; + +class Interval extends Clock { + Interval({required int seconds, required Bell bell}) + : super(bell: bell, seconds: seconds); + + @override + void start() { + Future.delayed(Duration(seconds: seconds), () async { + showBell('Interval'); + for (var i = 0; i < 3 - 1; i++) { + await Future.delayed(Duration(milliseconds: 500)); + bell.ring(); + } + print(''); + }); + } +} diff --git a/patterns/bridge/clock/clocks/once.dart b/patterns/bridge/clock/clocks/once.dart new file mode 100644 index 0000000..02b8733 --- /dev/null +++ b/patterns/bridge/clock/clocks/once.dart @@ -0,0 +1,15 @@ +import '../bells/bell.dart'; +import 'clock.dart'; + +class Once extends Clock { + Once({required int seconds, required Bell bell}) + : super(seconds: seconds, bell: bell); + + @override + void start() { + Future.delayed(Duration(seconds: seconds), () { + showBell('Once'); + print(''); + }); + } +} diff --git a/patterns/bridge/clock/main.dart b/patterns/bridge/clock/main.dart new file mode 100644 index 0000000..a2d80d9 --- /dev/null +++ b/patterns/bridge/clock/main.dart @@ -0,0 +1,22 @@ +import 'bells/melody.dart'; +import 'bells/signal.dart'; +import 'clocks/once.dart'; +import 'clocks/clock.dart'; +import 'clocks/interval.dart'; + +void main() { + final melody = Melody(); + final signal = Signal(); + startClocks([ + Once(seconds: 1, bell: melody), + Once(seconds: 2, bell: signal), + Interval(seconds: 5, bell: melody), + Interval(seconds: 3, bell: signal), + ]); +} + +void startClocks(List clocks) { + for (final clock in clocks) { + clock.start(); + } +} diff --git a/patterns/bridge/devices_remote_control/README.md b/patterns/bridge/devices_remote_control/README.md new file mode 100644 index 0000000..51de5a2 --- /dev/null +++ b/patterns/bridge/devices_remote_control/README.md @@ -0,0 +1,71 @@ +# Bridge pattern +This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/master/src/refactoring_guru/bridge/example) +But removed the ability to use null for devices. Instead, the EmptyDevice class is used. + +**Diagram:** + +![image](https://user-images.githubusercontent.com/8049534/145878324-3cbc52f5-51f4-4642-921d-69fbe2886f8c.png) + +**Description:** +https://refactoring.guru/design-patterns/bridge?#pseudocode + +**Output:** + +``` +==================================== +Tv +Tests with basic remote. +Remote: power toggle +------------------------------------ +| I'm TV set. +| I'm enabled +| Current volume is 30% +| Current channel is 1 +------------------------------------ + +Tests with advanced remote. +Remote: power toggle +Remote: mute +------------------------------------ +| I'm TV set. +| I'm disabled +| Current volume is 30% +| Current channel is 1 +------------------------------------ + +==================================== +Radio +Tests with basic remote. +Remote: power toggle +------------------------------------ +| I'm radio. +| I'm enabled +| Current volume is 30% +| Current channel is 1 +------------------------------------ + +Tests with advanced remote. +Remote: power toggle +Remote: mute +------------------------------------ +| I'm radio. +| I'm disabled +| Current volume is 30% +| Current channel is 1 +------------------------------------ + +==================================== +EmptyDevice +Tests with basic remote. +Remote: power(device not found) +------------------------------------ +| Device is Empty +------------------------------------ + +Tests with advanced remote. +Remote: power(device not found) +Remote: mute(device not found) +------------------------------------ +| Device is Empty +------------------------------------ +``` diff --git a/patterns/bridge/devices_remote_control/devices/device.dart b/patterns/bridge/devices_remote_control/devices/device.dart new file mode 100644 index 0000000..6dc18e6 --- /dev/null +++ b/patterns/bridge/devices_remote_control/devices/device.dart @@ -0,0 +1,15 @@ +abstract class Device { + bool get isEnabled; + + set isEnabled(bool enabled); + + int get volume; + + set volume(int percent); + + int get channel; + + set channel(int channel); + + void printStatus(); +} diff --git a/patterns/bridge/devices_remote_control/devices/empty_device.dart b/patterns/bridge/devices_remote_control/devices/empty_device.dart new file mode 100644 index 0000000..04142cc --- /dev/null +++ b/patterns/bridge/devices_remote_control/devices/empty_device.dart @@ -0,0 +1,28 @@ +import 'device.dart'; + +class EmptyDevice implements Device { + @override + int get channel => throw UnimplementedError('EmptyDevice'); + + @override + bool get isEnabled => throw UnimplementedError('EmptyDevice'); + + @override + int get volume => throw UnimplementedError('EmptyDevice'); + + @override + void printStatus() { + print('------------------------------------'); + print('| Device is Empty'); + print('------------------------------------\n'); + } + + @override + set channel(int channel) => throw UnimplementedError('EmptyDevice'); + + @override + set isEnabled(bool enabled) => throw UnimplementedError('EmptyDevice'); + + @override + set volume(int percent) => throw UnimplementedError('EmptyDevice'); +} diff --git a/patterns/bridge/devices_remote_control/devices/radio.dart b/patterns/bridge/devices_remote_control/devices/radio.dart new file mode 100644 index 0000000..7137bd1 --- /dev/null +++ b/patterns/bridge/devices_remote_control/devices/radio.dart @@ -0,0 +1,43 @@ +import 'device.dart'; + +class Radio implements Device { + bool _on = false; + int _volume = 30; + int _channel = 1; + + @override + bool get isEnabled => _on; + + @override + set isEnabled(bool enabled) => _on = enabled; + + @override + int get volume => _volume; + + @override + set volume(int percent) { + if (volume > 100) { + _volume = 100; + } else if (volume < 0) { + _volume = 0; + } else { + _volume = volume; + } + } + + @override + int get channel => _channel; + + @override + set channel(int channel) => _channel = channel; + + @override + void printStatus() { + print('------------------------------------'); + print('| I\'m radio.'); + print('| I\'m ${_on ? 'enabled' : 'disabled'}'); + print('| Current volume is $_volume%'); + print('| Current channel is $_channel'); + print('------------------------------------\n'); + } +} diff --git a/patterns/bridge/devices_remote_control/devices/tv.dart b/patterns/bridge/devices_remote_control/devices/tv.dart new file mode 100644 index 0000000..0fc0cd1 --- /dev/null +++ b/patterns/bridge/devices_remote_control/devices/tv.dart @@ -0,0 +1,43 @@ +import 'device.dart'; + +class Tv implements Device { + bool _on = false; + int _volume = 30; + int _channel = 1; + + @override + bool get isEnabled => _on; + + @override + set isEnabled(bool enabled) => _on = enabled; + + @override + int get volume => _volume; + + @override + set volume(int percent) { + if (volume > 100) { + _volume = 100; + } else if (volume < 0) { + _volume = 0; + } else { + _volume = volume; + } + } + + @override + int get channel => _channel; + + @override + set channel(int channel) => _channel = channel; + + @override + void printStatus() { + print('------------------------------------'); + print('| I\'m TV set.'); + print('| I\'m ${_on ? 'enabled' : 'disabled'}'); + print('| Current volume is $_volume%'); + print('| Current channel is $_channel'); + print('------------------------------------\n'); + } +} diff --git a/patterns/bridge/devices_remote_control/main.dart b/patterns/bridge/devices_remote_control/main.dart new file mode 100644 index 0000000..f35c767 --- /dev/null +++ b/patterns/bridge/devices_remote_control/main.dart @@ -0,0 +1,26 @@ +import 'devices/device.dart'; +import 'devices/empty_device.dart'; +import 'devices/radio.dart'; +import 'devices/tv.dart'; +import 'remotes/basic_advance_remote.dart'; + +void main() { + testDevice(Tv()); + testDevice(Radio()); + testDevice(EmptyDevice()); +} + +void testDevice(Device device) { + print(''.padRight(36, '=')); + print(device.runtimeType); + print("Tests with basic remote."); + final basicRemote = BasicRemote.fromDevice(device); + basicRemote.power(); + device.printStatus(); + + print("Tests with advanced remote."); + final advancedRemote = AdvancedRemote.fromDevice(device); + advancedRemote.power(); + advancedRemote.mute(); + device.printStatus(); +} diff --git a/patterns/bridge/devices_remote_control/remotes/basic_advance_remote.dart b/patterns/bridge/devices_remote_control/remotes/basic_advance_remote.dart new file mode 100644 index 0000000..6c86de5 --- /dev/null +++ b/patterns/bridge/devices_remote_control/remotes/basic_advance_remote.dart @@ -0,0 +1,76 @@ +import '../devices/device.dart'; +import '../devices/empty_device.dart'; +import 'remote.dart'; + +class BasicRemote implements Remote { + final Device _device; + + BasicRemote() : _device = EmptyDevice(); + + BasicRemote.fromDevice(this._device); + + @override + void power() { + if (_device is EmptyDevice) { + print('Remote: power(device not found)'); + return; + } + print('Remote: power toggle'); + _device.isEnabled = !_device.isEnabled; + } + + @override + void volumeDown() { + if (_device is EmptyDevice) { + print('Remote: volumeDown(device not found)'); + return; + } + print("Remote: volume down"); + _device.volume -= 10; + } + + @override + void volumeUp() { + if (_device is EmptyDevice) { + print('Remote: volumeDown(device not found)'); + return; + } + print("Remote: volume up"); + _device.volume += 10; + } + + @override + void channelDown() { + if (_device is EmptyDevice) { + print('Remote: channelDown(device not found)'); + return; + } + print("Remote: channel down"); + _device.channel -= 1; + } + + @override + void channelUp() { + if (_device is EmptyDevice) { + print('Remote: channelUp(device not found)'); + return; + } + print("Remote: channel up"); + _device.channel += 1; + } +} + +class AdvancedRemote extends BasicRemote { + AdvancedRemote() : super(); + + AdvancedRemote.fromDevice(Device device) : super.fromDevice(device); + + void mute() { + if (_device is EmptyDevice) { + print('Remote: mute(device not found)'); + return; + } + print("Remote: mute"); + _device.volume = 0; + } +} diff --git a/patterns/bridge/devices_remote_control/remotes/remote.dart b/patterns/bridge/devices_remote_control/remotes/remote.dart new file mode 100644 index 0000000..6f1ff2e --- /dev/null +++ b/patterns/bridge/devices_remote_control/remotes/remote.dart @@ -0,0 +1,11 @@ +abstract class Remote { + void power(); + + void volumeDown(); + + void volumeUp(); + + void channelDown(); + + void channelUp(); +} diff --git a/patterns/builder/cars/README.md b/patterns/builder/cars/README.md new file mode 100644 index 0000000..61edc41 --- /dev/null +++ b/patterns/builder/cars/README.md @@ -0,0 +1,19 @@ +# Builder pattern + +**Description:** +https://refactoring.guru/design-patterns/builder?#pseudocode + +**Output:** + +``` +Car built: +CarType.sportCar + +Car manual built: +Type of car: CarType.sportCar +Count of seats: 2 +Engine: volume - 3.0; mileage - 0.0 +Transmission: Transmission.semiAutomatic +Trip Computer: Functional +GPS Navigator: Functional +``` diff --git a/patterns/builder/cars/builders/builder.dart b/patterns/builder/cars/builders/builder.dart new file mode 100644 index 0000000..3360805 --- /dev/null +++ b/patterns/builder/cars/builders/builder.dart @@ -0,0 +1,23 @@ +import '../cars/car_type.dart'; +import '../components/engine.dart'; +import '../director/gps_navigation.dart'; +import '../director/transmission.dart'; +import '../director/trip_computer.dart'; + +/// EN: Builder interface defines all possible ways to configure a product. +/// +/// RU: Интерфейс Строителя объявляет все возможные этапы и шаги конфигурации +/// продукта. +abstract class Builder { + set carType(CarType type); + + set seats(int seats); + + set engine(Engine engine); + + set transmission(Transmission transmission); + + set tripComputer(TripComputer tripComputer); + + set gpsNavigator(GPSNavigator gpsNavigator); +} diff --git a/patterns/builder/cars/builders/car_builder.dart b/patterns/builder/cars/builders/car_builder.dart new file mode 100644 index 0000000..19868ed --- /dev/null +++ b/patterns/builder/cars/builders/car_builder.dart @@ -0,0 +1,35 @@ +import '../cars/car.dart'; +import '../cars/car_type.dart'; +import '../components/engine.dart'; +import '../director/gps_navigation.dart'; +import '../director/transmission.dart'; +import '../director/trip_computer.dart'; +import 'builder.dart'; + +/// EN: Concrete builders implement steps defined in the common interface. +/// +/// RU: Конкретные строители реализуют шаги, объявленные в общем интерфейсе. +class CarBuilder implements Builder { + @override + CarType? carType; + + @override + int? seats; + + @override + Engine? engine; + + @override + Transmission? transmission; + + @override + TripComputer? tripComputer; + + @override + GPSNavigator? gpsNavigator; + + Car getResult() { + return Car( + carType!, seats!, engine!, transmission!, tripComputer!, gpsNavigator!); + } +} diff --git a/patterns/builder/cars/builders/car_manual_builder.dart b/patterns/builder/cars/builders/car_manual_builder.dart new file mode 100644 index 0000000..a8fdd62 --- /dev/null +++ b/patterns/builder/cars/builders/car_manual_builder.dart @@ -0,0 +1,45 @@ +import '../cars/car_manual.dart'; +import '../cars/car_type.dart'; +import '../components/engine.dart'; +import '../director/gps_navigation.dart'; +import '../director/transmission.dart'; +import '../director/trip_computer.dart'; +import 'builder.dart'; + +/// EN: Unlike other creational patterns, Builder can construct unrelated +/// products, which don't have the common interface. +/// +/// In this case we build a user manual for a car, using the same steps as we +/// built a car. This allows to produce manuals for specific car models, +/// configured with different features. +/// +/// RU: В отличие от других создающих паттернов, Строители могут создавать +/// совершенно разные продукты, не имеющие общего интерфейса. +/// +/// В данном случае мы производим руководство пользователя автомобиля с помощью +/// тех же шагов, что и сами автомобили. Это устройство позволит создавать +/// руководства под конкретные модели автомобилей, содержащие те или иные фичи. +class CarManualBuilder implements Builder { + @override + CarType? carType; + + @override + int? seats; + + @override + Engine? engine; + + @override + Transmission? transmission; + + @override + TripComputer? tripComputer; + + @override + GPSNavigator? gpsNavigator; + + Manual getResult() { + return Manual( + carType!, seats!, engine!, transmission!, tripComputer!, gpsNavigator!); + } +} diff --git a/patterns/builder/cars/cars/car.dart b/patterns/builder/cars/cars/car.dart new file mode 100644 index 0000000..7740b80 --- /dev/null +++ b/patterns/builder/cars/cars/car.dart @@ -0,0 +1,35 @@ +import '../components/engine.dart'; +import '../director/gps_navigation.dart'; +import '../director/transmission.dart'; +import '../director/trip_computer.dart'; +import 'car_type.dart'; + +/// EN: Car is a product class. +/// +/// RU: Автомобиль — это класс продукта. +class Car { + final CarType carType; + + final int seats; + + final Engine engine; + + final Transmission transmission; + + final TripComputer tripComputer; + + final GPSNavigator gpsNavigator; + + final double fuel = 0.0; + + Car( + this.carType, + this.seats, + this.engine, + this.transmission, + this.tripComputer, + this.gpsNavigator, + ) { + tripComputer.car = this; + } +} diff --git a/patterns/builder/cars/cars/car_manual.dart b/patterns/builder/cars/cars/car_manual.dart new file mode 100644 index 0000000..ec04cca --- /dev/null +++ b/patterns/builder/cars/cars/car_manual.dart @@ -0,0 +1,53 @@ +import '../components/engine.dart'; +import '../director/gps_navigation.dart'; +import '../director/transmission.dart'; +import '../director/trip_computer.dart'; +import 'car_type.dart'; + +/// EN: Car manual is another product. Note that it does not have the same +/// ancestor as a Car. They are not related. +/// +/// RU: Руководство автомобиля — это второй продукт. Заметьте, что руководство и +/// сам автомобиль не имеют общего родительского класса. По сути, они независимы. + +class Manual { + CarType carType; + + final int seats; + + final Engine engine; + + final Transmission transmission; + + final TripComputer? tripComputer; + + final GPSNavigator? gpsNavigator; + + Manual( + this.carType, + this.seats, + this.engine, + this.transmission, [ + this.tripComputer, + this.gpsNavigator, + ]); + + String print() { + var info = ''; + info += 'Type of car: $carType\n'; + info += 'Count of seats: $seats\n'; + info += 'Engine: volume - ${engine.volume}; mileage - ${engine.mileage}\n'; + info += 'Transmission: $transmission\n'; + if (tripComputer != null) { + info += 'Trip Computer: Functional\n'; + } else { + info += 'Trip Computer: N/A\n'; + } + if (gpsNavigator != null) { + info += 'GPS Navigator: Functional\n'; + } else { + info += 'GPS Navigator: N/A\n'; + } + return info; + } +} diff --git a/patterns/builder/cars/cars/car_type.dart b/patterns/builder/cars/cars/car_type.dart new file mode 100644 index 0000000..39a8c44 --- /dev/null +++ b/patterns/builder/cars/cars/car_type.dart @@ -0,0 +1,5 @@ +enum CarType { + cityCar, + sportCar, + suv, +} diff --git a/patterns/builder/cars/components/engine.dart b/patterns/builder/cars/components/engine.dart new file mode 100644 index 0000000..bd7f242 --- /dev/null +++ b/patterns/builder/cars/components/engine.dart @@ -0,0 +1,28 @@ +/// EN: Just another feature of a car. +/// +/// RU: Одна из фишек автомобиля. +class Engine { + final double volume; + + double _mileage; + + double get mileage => _mileage; + + var _started = false; + + Engine(this.volume, this._mileage); + + void on() => _started = true; + + void off() => _started = false; + + bool get isStarted => _started; + + void go(double mileage) { + if (_started) { + _mileage += mileage; + } else { + print('Cannot go(), you must start engine first!'); + } + } +} diff --git a/patterns/builder/cars/director/director.dart b/patterns/builder/cars/director/director.dart new file mode 100644 index 0000000..1588bf2 --- /dev/null +++ b/patterns/builder/cars/director/director.dart @@ -0,0 +1,44 @@ +import '../builders/builder.dart'; +import '../cars/car_type.dart'; +import '../components/engine.dart'; +import 'gps_navigation.dart'; +import 'transmission.dart'; +import 'trip_computer.dart'; + +/// EN: Director defines the order of building steps. It works with a builder +/// object through common Builder interface. Therefore it may not know what +/// product is being built. +/// +/// RU: Директор знает в какой последовательности заставлять работать строителя. +/// Он работает с ним через общий интерфейс Строителя. Из-за этого, он может не +/// знать какой конкретно продукт сейчас строится. +class Director { + void constructSportsCar(Builder builder) { + builder + ..carType = CarType.sportCar + ..seats = 2 + ..engine = Engine(3.0, 0) + ..transmission = Transmission.semiAutomatic + ..tripComputer = TripComputer() + ..gpsNavigator = GPSNavigator(); + } + + void constructCityCar(Builder builder) { + builder + ..carType = CarType.cityCar + ..seats = 2 + ..engine = Engine(1.2, 0) + ..transmission = Transmission.automatic + ..tripComputer = TripComputer() + ..gpsNavigator = GPSNavigator(); + } + + void constructSUV(Builder builder) { + builder + ..carType = CarType.suv + ..seats = 4 + ..engine = Engine(2.5, 0) + ..transmission = Transmission.manual + ..gpsNavigator = GPSNavigator(); + } +} diff --git a/patterns/builder/cars/director/gps_navigation.dart b/patterns/builder/cars/director/gps_navigation.dart new file mode 100644 index 0000000..144860a --- /dev/null +++ b/patterns/builder/cars/director/gps_navigation.dart @@ -0,0 +1,16 @@ +/// EN: Just another feature of a car. +/// +/// RU: Одна из фишек автомобиля. +class GPSNavigator { + late String _route; + + GPSNavigator() + : _route = "221b, Baker Street, London " + "to Scotland Yard, 8-10 Broadway, London"; + + GPSNavigator.fromRout(String manualRoute) { + _route = manualRoute; + } + + String get root => _route; +} diff --git a/patterns/builder/cars/director/transmission.dart b/patterns/builder/cars/director/transmission.dart new file mode 100644 index 0000000..311bc5a --- /dev/null +++ b/patterns/builder/cars/director/transmission.dart @@ -0,0 +1,9 @@ +/// EN: Just another feature of a car. +/// +/// RU: Одна из фишек автомобиля. +enum Transmission { + singleSpeed, + manual, + automatic, + semiAutomatic, +} diff --git a/patterns/builder/cars/director/trip_computer.dart b/patterns/builder/cars/director/trip_computer.dart new file mode 100644 index 0000000..341e318 --- /dev/null +++ b/patterns/builder/cars/director/trip_computer.dart @@ -0,0 +1,21 @@ +import '../cars/car.dart'; + +/// EN: Just another feature of a car. +/// +/// RU: Одна из фишек автомобиля. +class TripComputer { + Car? car; + + void showFuelLevel() { + print("Fuel level: ${car?.fuel}"); + } + + void showStatus() { + final engine = car?.engine; + if (engine != null && engine.isStarted) { + print("Car is started"); + } else { + print("Car isn't started"); + } + } +} diff --git a/patterns/builder/cars/main.dart b/patterns/builder/cars/main.dart new file mode 100644 index 0000000..a7a3bc3 --- /dev/null +++ b/patterns/builder/cars/main.dart @@ -0,0 +1,38 @@ +import 'builders/car_builder.dart'; +import 'builders/car_manual_builder.dart'; +import 'director/director.dart'; + +/// EN: Use-case. Everything comes together here. +/// +/// RU: Пример использования. Здесь всё сводится воедино. +void main() { + final director = Director(); + + // EN: Director gets the concrete builder object from the client + // (application code). That's because application knows better which + // builder to use to get a specific product. + // + // RU: Директор получает объект конкретного строителя от клиента + // (приложения). Приложение само знает какой строитель использовать, + // чтобы получить нужный продукт. + final builder = CarBuilder(); + director.constructSportsCar(builder); + + // EN: The final product is often retrieved from a builder object, since + // Director is not aware and not dependent on concrete builders and + // products. + // + // RU: Готовый продукт возвращает строитель, так как Директор чаще всего + // не знает и не зависит от конкретных классов строителей и продуктов. + final car = builder.getResult(); + print('Car built:\n${car.carType}\n'); + + final manualBuilder = CarManualBuilder(); + + // EN: Director may know several building recipes. + // + // RU: Директор может знать больше одного рецепта строительства + director.constructSportsCar(manualBuilder); + final carManual = manualBuilder.getResult(); + print("Car manual built:\n" + carManual.print()); +} diff --git a/patterns/builder/color_text_format/README.md b/patterns/builder/color_text_format/README.md new file mode 100644 index 0000000..28f5b8b --- /dev/null +++ b/patterns/builder/color_text_format/README.md @@ -0,0 +1,75 @@ +# Builder pattern +Using different text formats + +![image](https://user-images.githubusercontent.com/8049534/146023073-5d7644a4-d3b9-4420-bffe-f72ac3fd83dd.png)) + +**Client code:** +```dart + 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', + ); +``` + +**Output:** +``` +HtmlFormat( + + + + Color text + + + +

+ I love looking at the + blue sky, eating + red apples, sitting on the + green grass.

+ + +), + +JsonFormat( +[ + { + "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" + } +] +), + +ConsoleFormat( + I love looking at the blue sky, eating red apples, sitting on the green grass. +), +``` diff --git a/patterns/builder/color_text_format/color_reader/color_text_reader.dart b/patterns/builder/color_text_format/color_reader/color_text_reader.dart new file mode 100644 index 0000000..adae573 --- /dev/null +++ b/patterns/builder/color_text_format/color_reader/color_text_reader.dart @@ -0,0 +1,22 @@ +import '../converters/converter.dart'; +import '../formats/text_format.dart'; + +/// Director +class ColorTextReader { + final String text; + + ColorTextReader({required this.text}); + + T 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']); +} diff --git a/patterns/builder/color_text_format/converters/console_converter.dart b/patterns/builder/color_text_format/converters/console_converter.dart new file mode 100644 index 0000000..972f51f --- /dev/null +++ b/patterns/builder/color_text_format/converters/console_converter.dart @@ -0,0 +1,17 @@ +import '../formats/console_format.dart'; +import 'converter.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/patterns/builder/color_text_format/converters/converter.dart b/patterns/builder/color_text_format/converters/converter.dart new file mode 100644 index 0000000..6f98ca3 --- /dev/null +++ b/patterns/builder/color_text_format/converters/converter.dart @@ -0,0 +1,10 @@ +import '../formats/text_format.dart'; + +/// Builder +abstract class Converter { + void writeWord(String text); + + void writeColor(String color); + + T get result; +} diff --git a/patterns/builder/color_text_format/converters/html_converter.dart b/patterns/builder/color_text_format/converters/html_converter.dart new file mode 100644 index 0000000..4f2c73b --- /dev/null +++ b/patterns/builder/color_text_format/converters/html_converter.dart @@ -0,0 +1,24 @@ +import '../formats/html_format.dart'; +import 'converter.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/patterns/builder/color_text_format/converters/json_converter.dart b/patterns/builder/color_text_format/converters/json_converter.dart new file mode 100644 index 0000000..ab288de --- /dev/null +++ b/patterns/builder/color_text_format/converters/json_converter.dart @@ -0,0 +1,28 @@ +import '../formats/json_format.dart'; +import 'converter.dart'; + +class JsonConverter extends Converter { + @override + JsonFormat get result => _json; + + final _json = JsonFormat(); + + @override + void writeColor(String color) { + if (_textBuffer.isNotEmpty) { + _json.add({ + 'text': _textBuffer.toString(), + }); + _textBuffer.clear(); + } + _json.add({ + 'text': color, + 'color': color, + }); + } + + final _textBuffer = StringBuffer(); + + @override + void writeWord(String text) => _textBuffer.write('$text '); +} diff --git a/patterns/builder/color_text_format/formats/console_format.dart b/patterns/builder/color_text_format/formats/console_format.dart new file mode 100644 index 0000000..65451ca --- /dev/null +++ b/patterns/builder/color_text_format/formats/console_format.dart @@ -0,0 +1,24 @@ +import 'text_format.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/patterns/builder/color_text_format/formats/html_format.dart b/patterns/builder/color_text_format/formats/html_format.dart new file mode 100644 index 0000000..1848f3d --- /dev/null +++ b/patterns/builder/color_text_format/formats/html_format.dart @@ -0,0 +1,81 @@ +import 'dart:collection'; +import 'text_format.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(' '); + 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'; + } +} diff --git a/patterns/builder/color_text_format/formats/json_format.dart b/patterns/builder/color_text_format/formats/json_format.dart new file mode 100644 index 0000000..1dc3d1d --- /dev/null +++ b/patterns/builder/color_text_format/formats/json_format.dart @@ -0,0 +1,13 @@ +import 'dart:convert'; +import 'text_format.dart'; + +class JsonFormat extends TextFormat { + final _list = >[]; + + void add(Map item) { + _list.add(item); + } + + @override + String get content => JsonEncoder.withIndent(' ').convert(_list); +} diff --git a/patterns/builder/color_text_format/formats/text_format.dart b/patterns/builder/color_text_format/formats/text_format.dart new file mode 100644 index 0000000..0b16113 --- /dev/null +++ b/patterns/builder/color_text_format/formats/text_format.dart @@ -0,0 +1,9 @@ +/// Product +abstract class TextFormat { + String get content; + + @override + String toString() => '$runtimeType(\n' + '$content' + '\n)'; +} diff --git a/patterns/builder/color_text_format/main.dart b/patterns/builder/color_text_format/main.dart new file mode 100644 index 0000000..a5ef75b --- /dev/null +++ b/patterns/builder/color_text_format/main.dart @@ -0,0 +1,20 @@ +import 'color_reader/color_text_reader.dart'; +import 'converters/html_converter.dart'; +import 'converters/json_converter.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', + ); +} diff --git a/patterns/composite/products_and_boxes/diagram/diagram.dart b/patterns/composite/products_and_boxes/diagram/diagram.dart new file mode 100644 index 0000000..a8fa9c8 --- /dev/null +++ b/patterns/composite/products_and_boxes/diagram/diagram.dart @@ -0,0 +1,32 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import 'render_element.dart'; + +class PositionRenderElement { + final int x; + final int y; + final RenderElement renderElement; + + PositionRenderElement(this.x, this.y, this.renderElement); +} + +class Diagram extends RenderElement { + final _graph = []; + + var _w = 0; + void addRenderElement(int x, int y, RenderElement renderElement) { + final w = x + renderElement.width; + if (w > _w) { + _w = w; + } + _graph.add(PositionRenderElement(x, y, renderElement)); + } + + @override + int get width => _w; + + @override + String render(Canvas dc) { + return ''; + } +} diff --git a/patterns/composite/products_and_boxes/diagram/render_element.dart b/patterns/composite/products_and_boxes/diagram/render_element.dart new file mode 100644 index 0000000..022bcca --- /dev/null +++ b/patterns/composite/products_and_boxes/diagram/render_element.dart @@ -0,0 +1,30 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +abstract class RenderElement { + int get width; + + final int height = 0; + + String render(Canvas dc); +} + +class BorderText extends RenderElement { + @override + int get width => text.length + 2 + 2; + + @override + int get height => 3; + + final String text; + final BorderStyle borderStyle; + + BorderText(this.text, this.borderStyle); + + @override + String render(Canvas dc) { + return (dc + ..text(text, widthCenter: width, heightCenter: height) + ..border(width, height, borderStyle)) + .toString(); + } +} diff --git a/patterns/composite/products_and_boxes/main.dart b/patterns/composite/products_and_boxes/main.dart new file mode 100644 index 0000000..a181cc8 --- /dev/null +++ b/patterns/composite/products_and_boxes/main.dart @@ -0,0 +1,41 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import 'diagram/render_element.dart'; +import 'products/box.dart'; +import 'products/product.dart'; +import 'products/product_leaf.dart'; + +void main() { + final dc = Canvas(6, 6); +print( + BorderText('Node', BorderStyle.single).render(dc) +); +} + +Product createParcel() { + return Box( + children: [ + Box.single( + ProductLeaf('🔨'), + ), + Box( + children: [ + Box( + children: [ + Box.single( + ProductLeaf('📱'), + ), + Box.single( + ProductLeaf('🎧'), + ), + ], + ), + Box.single( + ProductLeaf('🔌'), + ), + ], + ), + ProductLeaf('🧾'), + ], + ); +} diff --git a/patterns/composite/products_and_boxes/node/node.dart b/patterns/composite/products_and_boxes/node/node.dart new file mode 100644 index 0000000..6aa8c8d --- /dev/null +++ b/patterns/composite/products_and_boxes/node/node.dart @@ -0,0 +1,45 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import '../diagram/diagram.dart'; +import '../diagram/render_element.dart'; + +class Node { + final String text; + final List children; + final BorderStyle borderStyle; + + Node({ + required this.text, + required this.borderStyle, + this.children = const [], + }); + + Diagram toDiagram() { + // get size & position + final spaceBetweenChildren = 2; + final childDiagrams = [ + for (final child in children) child.toDiagram(), + ].toList(); + final childXPosition = [ + for (final child in childDiagrams) child.width + spaceBetweenChildren, + ]; + final childWidth = childXPosition.fold(0, (a, b) => a + b); + final thisBox = BorderText(text, borderStyle); + final thisBoxCenterX = (childWidth + thisBox.width) ~/ 2; + + final resultDiagram = Diagram(); + resultDiagram.addRenderElement(thisBoxCenterX, 0, thisBox); + + final y = thisBox.height; + var x = 0; + + for (final childDiagram in childDiagrams) { + resultDiagram.addRenderElement(x, y, childDiagram); + // resultDiagram.drawLine((childDiagram.width / 2) + x, y, x2, y2); + x += childDiagram.width; + } + + return resultDiagram; + } +} + diff --git a/patterns/composite/products_and_boxes/products/box.dart b/patterns/composite/products_and_boxes/products/box.dart new file mode 100644 index 0000000..f8c481b --- /dev/null +++ b/patterns/composite/products_and_boxes/products/box.dart @@ -0,0 +1,41 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import '../node/node.dart'; +import 'product.dart'; + +class Box implements Product { + final List children; + + Box({required this.children}); + + factory Box.single(Product productLeaf) { + return Box(children: [productLeaf]); + } + + @override + Node toNode() { + return Node( + text: 'Box($size)', + borderStyle: _borderStyleBySize, + children: [ + for (final Product childNode in children) childNode.toNode(), + ], + ); + } + + BorderStyle get _borderStyleBySize { + final currSize = size; + if (currSize > 4) { + return BorderStyle.bold; + } else if (currSize >= 2) { + return BorderStyle.double; + } else if (currSize == 1) { + return BorderStyle.single; + } else { + return BorderStyle.single; // todo: + } + } + + @override + int get size => children.fold(0, (sum, product) => sum + product.size); +} diff --git a/patterns/composite/products_and_boxes/products/product.dart b/patterns/composite/products_and_boxes/products/product.dart new file mode 100644 index 0000000..93bd3fe --- /dev/null +++ b/patterns/composite/products_and_boxes/products/product.dart @@ -0,0 +1,7 @@ +import '../node/node.dart'; + +abstract class Product { + Node toNode(); + + int get size; +} diff --git a/patterns/composite/products_and_boxes/products/product_leaf.dart b/patterns/composite/products_and_boxes/products/product_leaf.dart new file mode 100644 index 0000000..d6df22c --- /dev/null +++ b/patterns/composite/products_and_boxes/products/product_leaf.dart @@ -0,0 +1,21 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import '../node/node.dart'; +import 'product.dart'; + +class ProductLeaf implements Product { + final String content; + + ProductLeaf(this.content); + + @override + Node toNode() { + return Node( + text: content, + borderStyle: BorderStyle.round, + ); + } + + @override + int get size => 1; +} diff --git a/patterns/prototype/example/README.md b/patterns/prototype/example/README.md new file mode 100644 index 0000000..97894a1 --- /dev/null +++ b/patterns/prototype/example/README.md @@ -0,0 +1,11 @@ +# Prototype pattern + +**Description:** +https://refactoring.guru/design-patterns/prototype?#pseudocode + +**Output:** + +``` +Origin shapes: [Rectangle(address: 0x2f7f544c), Circle(address: 0x3e59b487)] +Cloning shapes: [Rectangle(address: 0x3eb0a110), Circle(address: 0x75e3636)] +``` diff --git a/patterns/prototype/example/main.dart b/patterns/prototype/example/main.dart new file mode 100644 index 0000000..c1f7af4 --- /dev/null +++ b/patterns/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/patterns/prototype/example/shape/circle.dart b/patterns/prototype/example/shape/circle.dart new file mode 100644 index 0000000..7ea1f13 --- /dev/null +++ b/patterns/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/patterns/prototype/example/shape/rectangle.dart b/patterns/prototype/example/shape/rectangle.dart new file mode 100644 index 0000000..2631cbd --- /dev/null +++ b/patterns/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/patterns/prototype/example/shape/shape.dart b/patterns/prototype/example/shape/shape.dart new file mode 100644 index 0000000..1f7a21f --- /dev/null +++ b/patterns/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 19326f2a271a847ecef93bc9a76342d8c5ba7177 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 24 Dec 2021 21:26:48 +0200 Subject: [PATCH 049/479] Don't paint transparent colors --- lib/text_canvas/canvas.dart | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/text_canvas/canvas.dart b/lib/text_canvas/canvas.dart index e63a1ff..aca74e7 100644 --- a/lib/text_canvas/canvas.dart +++ b/lib/text_canvas/canvas.dart @@ -1,11 +1,9 @@ -import 'dart:typed_data'; - import 'primitives.dart'; class Canvas { final int width; final int height; - late List _pixel; + late List>> _pixel; final int lineStretch; Canvas( @@ -16,8 +14,8 @@ class Canvas { final realWidth = width * lineStretch; _pixel = [ for (var i = 0; i < height; i++) - Int16List.fromList( - List.filled(realWidth, Color.light.code), + List.from( + List.filled(realWidth, Color.light.units), ), ]; } @@ -33,10 +31,14 @@ class Canvas { return; } + if (penColor == Color.transparent) { + return; + } + final realY = y; final realX = (x * lineStretch) ~/ 1; for (var i = 0; i < lineStretch; i++) { - _pixel[realY][realX + i] = penColor.code; + _pixel[realY][realX + i] = penColor.units; } } @@ -48,7 +50,11 @@ class Canvas { return; } - _pixel[y][x] = char.codeUnits[0]; + if (Color(char) == Color.transparent) { + return; + } + + _pixel[y][x] = char.codeUnits; } var _currPos = Point(0, 0); @@ -155,7 +161,7 @@ class Canvas { return _pixel .map((e) => e .map( - (e) => String.fromCharCode(e), + (e) => String.fromCharCodes(e), ) .join('')) .join('\n'); From 4edb4651a3eb4f9e2cf88ec90aa2ddeb2f4019c8 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 24 Dec 2021 21:35:02 +0200 Subject: [PATCH 050/479] Don't paint transparent colors --- lib/text_canvas/canvas.dart | 13 +++++++++--- lib/text_canvas/primitives.dart | 20 +++++++++++++++---- .../text_graphics/engine/shape_engine.dart | 2 +- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/lib/text_canvas/canvas.dart b/lib/text_canvas/canvas.dart index aca74e7..7ffd661 100644 --- a/lib/text_canvas/canvas.dart +++ b/lib/text_canvas/canvas.dart @@ -8,14 +8,21 @@ class Canvas { Canvas( this.width, - this.height, [ + this.height, { this.lineStretch = 3, - ]) { + Color? fillColor, + }) { final realWidth = width * lineStretch; + + fillColor ??= Color.light; + if (fillColor == Color.transparent) { + fillColor = Color.white; + } + _pixel = [ for (var i = 0; i < height; i++) List.from( - List.filled(realWidth, Color.light.units), + List.filled(realWidth, fillColor.units), ), ]; } diff --git a/lib/text_canvas/primitives.dart b/lib/text_canvas/primitives.dart index b832a42..4a64852 100644 --- a/lib/text_canvas/primitives.dart +++ b/lib/text_canvas/primitives.dart @@ -1,9 +1,11 @@ +import 'package:collection/collection.dart'; + class Color { - final int code; + final List units; Color(String symbol) : assert(symbol.length == 1), - code = symbol.codeUnitAt(0); + units = symbol.codeUnits; static final Color black = Color('█'); static final Color dark = Color('▓'); @@ -11,6 +13,16 @@ class Color { static final Color light = Color('░'); static final Color white = Color(' '); static final Color point = Color('■'); + static final Color transparent = Color('`'); + + @override + bool operator ==(Object other) { + return other is Color && ListEquality().equals(other.units, units); + } + + @override + int get hashCode => ListEquality().hash(units); + } class Point { @@ -41,8 +53,8 @@ class BorderStyle { static final BorderStyle round = BorderStyle.fromBorderText( '╭━━╮' - ' ' - ' ', + '` `' + '````', ); final String topLeft; diff --git a/patterns/adapter/text_graphics/engine/shape_engine.dart b/patterns/adapter/text_graphics/engine/shape_engine.dart index 9568bb4..a7a75c9 100644 --- a/patterns/adapter/text_graphics/engine/shape_engine.dart +++ b/patterns/adapter/text_graphics/engine/shape_engine.dart @@ -14,7 +14,7 @@ class ShapeEngine { }); String render() { - final can = Canvas(width, height, 3); + final can = Canvas(width, height, lineStretch: 3); for (final Shape shape in shapes) { can ..translate = Point(shape.x, shape.y) From 7d4f5fecc84be5a2c06fe1ebfd67ec0925bcdfc9 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 24 Dec 2021 21:42:08 +0200 Subject: [PATCH 051/479] Add diagram & render elements --- .../products_and_boxes/diagram/diagram.dart | 82 ++++++++++++++----- .../diagram/layout_render.dart | 21 +++++ .../diagram/render_column.dart | 35 ++++++++ .../diagram/render_element.dart | 25 +----- .../diagram/render_position.dart | 25 ++++++ .../diagram/render_row.dart | 32 ++++++++ .../diagram/render_text.dart | 24 ++++++ .../products_and_boxes/node/node.dart | 45 ---------- .../products_and_boxes/products/box.dart | 39 +++------ .../products_and_boxes/products/product.dart | 6 +- .../products/product_leaf.dart | 18 ++-- 11 files changed, 225 insertions(+), 127 deletions(-) create mode 100644 patterns/composite/products_and_boxes/diagram/layout_render.dart create mode 100644 patterns/composite/products_and_boxes/diagram/render_column.dart create mode 100644 patterns/composite/products_and_boxes/diagram/render_position.dart create mode 100644 patterns/composite/products_and_boxes/diagram/render_row.dart create mode 100644 patterns/composite/products_and_boxes/diagram/render_text.dart delete mode 100644 patterns/composite/products_and_boxes/node/node.dart diff --git a/patterns/composite/products_and_boxes/diagram/diagram.dart b/patterns/composite/products_and_boxes/diagram/diagram.dart index a8fa9c8..0683167 100644 --- a/patterns/composite/products_and_boxes/diagram/diagram.dart +++ b/patterns/composite/products_and_boxes/diagram/diagram.dart @@ -1,32 +1,74 @@ import 'package:design_patterns_dart/text_canvas.dart'; +import '../products/product.dart'; +import 'render_text.dart'; +import 'render_column.dart'; import 'render_element.dart'; - -class PositionRenderElement { - final int x; - final int y; - final RenderElement renderElement; - - PositionRenderElement(this.x, this.y, this.renderElement); -} +import 'render_row.dart'; class Diagram extends RenderElement { - final _graph = []; + final RenderElement _rootRenderElement; - var _w = 0; - void addRenderElement(int x, int y, RenderElement renderElement) { - final w = x + renderElement.width; - if (w > _w) { - _w = w; - } - _graph.add(PositionRenderElement(x, y, renderElement)); - } + Diagram(RenderElement root) + : _rootRenderElement = (root is Diagram) ? root._rootRenderElement : root; @override - int get width => _w; + int get width => _rootRenderElement.width; @override - String render(Canvas dc) { - return ''; + int get height => _rootRenderElement.height; + + @override + void render(Canvas dc) => _rootRenderElement.render(dc); + + String renderToText() { + final dc = Canvas(_rootRenderElement.width, 10); + render(dc); + return dc.toString(); + } + + factory Diagram.node(Product product) { + return Diagram( + product.toRenderElement(), + ); + } + + factory Diagram.parentNode(Product product, List children) { + return Diagram( + RenderColumn( + children: [ + // Root node + product.toRenderElement(), + + // Children nodes + RenderRow( + children: [ + for (final child in children) child.toRenderElement(), + ], + ), + ], + ), + ); + } +} + +extension ExtConvertProductToRenderElement on Product { + RenderElement toRenderElement() { + return RenderText( + content, + borderStyleBySize(size), + ); + } + + static BorderStyle borderStyleBySize(int size) { + if (size > 4) { + return BorderStyle.bold; + } else if (size >= 2) { + return BorderStyle.double; + } else if (size == 1) { + return BorderStyle.single; + } else { + return BorderStyle.single; // todo: + } } } diff --git a/patterns/composite/products_and_boxes/diagram/layout_render.dart b/patterns/composite/products_and_boxes/diagram/layout_render.dart new file mode 100644 index 0000000..6010983 --- /dev/null +++ b/patterns/composite/products_and_boxes/diagram/layout_render.dart @@ -0,0 +1,21 @@ +import 'dart:math'; + +import 'render_element.dart'; + +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); + + int get childHeight => children.fold( + 0, (height, renderElement) => height + renderElement.height); + + int get spacesSum => max(0, (children.length - 1) * space); +} diff --git a/patterns/composite/products_and_boxes/diagram/render_column.dart b/patterns/composite/products_and_boxes/diagram/render_column.dart new file mode 100644 index 0000000..1f20123 --- /dev/null +++ b/patterns/composite/products_and_boxes/diagram/render_column.dart @@ -0,0 +1,35 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import 'layout_render.dart'; +import 'render_element.dart'; + +class RenderColumn extends RenderLayout { + RenderColumn({ + required List children, + int space = 1, + }) : super(children: children, space: space); + + @override + void render(Canvas dc) { + final restoreTranslate = dc.translate; + var y = dc.translate.y; + for (final child in children) { + final xCenter = (width - child.width) ~/ 2; + dc.translate = Point(xCenter + dc.translate.x, dc.translate.y); + + child.render(dc); + + y += child.height + space; + dc.translate = Point(restoreTranslate.x, y); + } + dc.translate = restoreTranslate; + } + + @override + int get width => children.reduce((a, b) => a.width > b.width ? a : b).width; + + @override + int get height { + return childHeight + spacesSum; + } +} diff --git a/patterns/composite/products_and_boxes/diagram/render_element.dart b/patterns/composite/products_and_boxes/diagram/render_element.dart index 022bcca..1f56d27 100644 --- a/patterns/composite/products_and_boxes/diagram/render_element.dart +++ b/patterns/composite/products_and_boxes/diagram/render_element.dart @@ -3,28 +3,7 @@ import 'package:design_patterns_dart/text_canvas.dart'; abstract class RenderElement { int get width; - final int height = 0; + int get height; - String render(Canvas dc); -} - -class BorderText extends RenderElement { - @override - int get width => text.length + 2 + 2; - - @override - int get height => 3; - - final String text; - final BorderStyle borderStyle; - - BorderText(this.text, this.borderStyle); - - @override - String render(Canvas dc) { - return (dc - ..text(text, widthCenter: width, heightCenter: height) - ..border(width, height, borderStyle)) - .toString(); - } + void render(Canvas dc); } diff --git a/patterns/composite/products_and_boxes/diagram/render_position.dart b/patterns/composite/products_and_boxes/diagram/render_position.dart new file mode 100644 index 0000000..ff98627 --- /dev/null +++ b/patterns/composite/products_and_boxes/diagram/render_position.dart @@ -0,0 +1,25 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import 'render_element.dart'; + +class RenderPosition extends RenderElement { + final int x; + final int y; + final RenderElement renderElement; + + RenderPosition(this.x, this.y, this.renderElement); + + @override + void render(Canvas dc) { + final old = dc.translate; + dc.translate = Point(x, y); + renderElement.render(dc); + dc.translate = old; + } + + @override + int get width => renderElement.width; + + @override + int get height => renderElement.height; +} diff --git a/patterns/composite/products_and_boxes/diagram/render_row.dart b/patterns/composite/products_and_boxes/diagram/render_row.dart new file mode 100644 index 0000000..4a52451 --- /dev/null +++ b/patterns/composite/products_and_boxes/diagram/render_row.dart @@ -0,0 +1,32 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import 'layout_render.dart'; +import 'render_element.dart'; + +class RenderRow extends RenderLayout { + RenderRow({ + required List children, + int space = 3, + }) : super(children: children, space: space); + + @override + void render(Canvas dc) { + final restoreTranslate = dc.translate; + var x = dc.translate.x; + for (final child in children) { + child.render(dc); + x += child.width + space; + dc.translate = Point(x, restoreTranslate.y); + } + dc.translate = restoreTranslate; + } + + @override + int get width { + return childWidth + spacesSum; + } + + @override + int get height => + children.reduce((a, b) => a.width > b.height ? a : b).height; +} diff --git a/patterns/composite/products_and_boxes/diagram/render_text.dart b/patterns/composite/products_and_boxes/diagram/render_text.dart new file mode 100644 index 0000000..59788ff --- /dev/null +++ b/patterns/composite/products_and_boxes/diagram/render_text.dart @@ -0,0 +1,24 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import 'render_element.dart'; + +class RenderText extends RenderElement { + final String text; + final BorderStyle? borderStyle; + + RenderText(this.text, [this.borderStyle]); + + @override + int get width => text.length + 2 + 2; + + @override + int get height => 3; + + @override + void render(Canvas dc) { + if (borderStyle != null) { + dc.border(width, height, borderStyle!); + } + dc.text(text, widthCenter: width, heightCenter: height); + } +} diff --git a/patterns/composite/products_and_boxes/node/node.dart b/patterns/composite/products_and_boxes/node/node.dart deleted file mode 100644 index 6aa8c8d..0000000 --- a/patterns/composite/products_and_boxes/node/node.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:design_patterns_dart/text_canvas.dart'; - -import '../diagram/diagram.dart'; -import '../diagram/render_element.dart'; - -class Node { - final String text; - final List children; - final BorderStyle borderStyle; - - Node({ - required this.text, - required this.borderStyle, - this.children = const [], - }); - - Diagram toDiagram() { - // get size & position - final spaceBetweenChildren = 2; - final childDiagrams = [ - for (final child in children) child.toDiagram(), - ].toList(); - final childXPosition = [ - for (final child in childDiagrams) child.width + spaceBetweenChildren, - ]; - final childWidth = childXPosition.fold(0, (a, b) => a + b); - final thisBox = BorderText(text, borderStyle); - final thisBoxCenterX = (childWidth + thisBox.width) ~/ 2; - - final resultDiagram = Diagram(); - resultDiagram.addRenderElement(thisBoxCenterX, 0, thisBox); - - final y = thisBox.height; - var x = 0; - - for (final childDiagram in childDiagrams) { - resultDiagram.addRenderElement(x, y, childDiagram); - // resultDiagram.drawLine((childDiagram.width / 2) + x, y, x2, y2); - x += childDiagram.width; - } - - return resultDiagram; - } -} - diff --git a/patterns/composite/products_and_boxes/products/box.dart b/patterns/composite/products_and_boxes/products/box.dart index f8c481b..3eb687c 100644 --- a/patterns/composite/products_and_boxes/products/box.dart +++ b/patterns/composite/products_and_boxes/products/box.dart @@ -1,6 +1,4 @@ -import 'package:design_patterns_dart/text_canvas.dart'; - -import '../node/node.dart'; +import '../diagram/diagram.dart'; import 'product.dart'; class Box implements Product { @@ -8,34 +6,23 @@ class Box implements Product { Box({required this.children}); - factory Box.single(Product productLeaf) { - return Box(children: [productLeaf]); - } + @override + String get content => 'Box($size)'; @override - Node toNode() { - return Node( - text: 'Box($size)', - borderStyle: _borderStyleBySize, - children: [ - for (final Product childNode in children) childNode.toNode(), - ], + Diagram toDiagram() { + return Diagram.parentNode( + this, + children, ); } - BorderStyle get _borderStyleBySize { - final currSize = size; - if (currSize > 4) { - return BorderStyle.bold; - } else if (currSize >= 2) { - return BorderStyle.double; - } else if (currSize == 1) { - return BorderStyle.single; - } else { - return BorderStyle.single; // todo: - } - } - @override int get size => children.fold(0, (sum, product) => sum + product.size); + + factory Box.single(Product productLeaf) { + return Box( + children: [productLeaf], + ); + } } diff --git a/patterns/composite/products_and_boxes/products/product.dart b/patterns/composite/products_and_boxes/products/product.dart index 93bd3fe..42d0173 100644 --- a/patterns/composite/products_and_boxes/products/product.dart +++ b/patterns/composite/products_and_boxes/products/product.dart @@ -1,7 +1,9 @@ -import '../node/node.dart'; +import '../diagram/diagram.dart'; abstract class Product { - Node toNode(); + String get content; int get size; + + Diagram toDiagram(); } diff --git a/patterns/composite/products_and_boxes/products/product_leaf.dart b/patterns/composite/products_and_boxes/products/product_leaf.dart index d6df22c..b615204 100644 --- a/patterns/composite/products_and_boxes/products/product_leaf.dart +++ b/patterns/composite/products_and_boxes/products/product_leaf.dart @@ -1,21 +1,17 @@ -import 'package:design_patterns_dart/text_canvas.dart'; - -import '../node/node.dart'; +import '../diagram/diagram.dart'; import 'product.dart'; class ProductLeaf implements Product { + @override final String content; + @override + int get size => 1; + ProductLeaf(this.content); @override - Node toNode() { - return Node( - text: content, - borderStyle: BorderStyle.round, - ); + Diagram toDiagram() { + return Diagram.node(this); } - - @override - int get size => 1; } From 085bb69b2c2179059846355d747087cf4d91be91 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 25 Dec 2021 17:38:20 +0200 Subject: [PATCH 052/479] Supply all render elements with named arguments --- .../products_and_boxes/diagram/diagram.dart | 2 +- .../diagram/render_position.dart | 25 ------------------- .../diagram/render_text.dart | 2 +- 3 files changed, 2 insertions(+), 27 deletions(-) delete mode 100644 patterns/composite/products_and_boxes/diagram/render_position.dart diff --git a/patterns/composite/products_and_boxes/diagram/diagram.dart b/patterns/composite/products_and_boxes/diagram/diagram.dart index 0683167..690c278 100644 --- a/patterns/composite/products_and_boxes/diagram/diagram.dart +++ b/patterns/composite/products_and_boxes/diagram/diagram.dart @@ -56,7 +56,7 @@ extension ExtConvertProductToRenderElement on Product { RenderElement toRenderElement() { return RenderText( content, - borderStyleBySize(size), + borderStyle: borderStyleBySize(size), ); } diff --git a/patterns/composite/products_and_boxes/diagram/render_position.dart b/patterns/composite/products_and_boxes/diagram/render_position.dart deleted file mode 100644 index ff98627..0000000 --- a/patterns/composite/products_and_boxes/diagram/render_position.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:design_patterns_dart/text_canvas.dart'; - -import 'render_element.dart'; - -class RenderPosition extends RenderElement { - final int x; - final int y; - final RenderElement renderElement; - - RenderPosition(this.x, this.y, this.renderElement); - - @override - void render(Canvas dc) { - final old = dc.translate; - dc.translate = Point(x, y); - renderElement.render(dc); - dc.translate = old; - } - - @override - int get width => renderElement.width; - - @override - int get height => renderElement.height; -} diff --git a/patterns/composite/products_and_boxes/diagram/render_text.dart b/patterns/composite/products_and_boxes/diagram/render_text.dart index 59788ff..436ff6d 100644 --- a/patterns/composite/products_and_boxes/diagram/render_text.dart +++ b/patterns/composite/products_and_boxes/diagram/render_text.dart @@ -6,7 +6,7 @@ class RenderText extends RenderElement { final String text; final BorderStyle? borderStyle; - RenderText(this.text, [this.borderStyle]); + RenderText(this.text, {this.borderStyle}); @override int get width => text.length + 2 + 2; From a099a5ec9d59e0a788d9a7e8d222f3a2170cb4c9 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 25 Dec 2021 17:42:36 +0200 Subject: [PATCH 053/479] Use Color instead of String in drawing characters --- lib/text_canvas/canvas.dart | 8 +++--- lib/text_canvas/primitives.dart | 44 +++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/lib/text_canvas/canvas.dart b/lib/text_canvas/canvas.dart index 7ffd661..8405af3 100644 --- a/lib/text_canvas/canvas.dart +++ b/lib/text_canvas/canvas.dart @@ -49,7 +49,7 @@ class Canvas { } } - void char(int x, int y, String char) { + void char(int x, int y, Color char) { x += translate.x; y += translate.y; @@ -57,11 +57,11 @@ class Canvas { return; } - if (Color(char) == Color.transparent) { + if (char == Color.transparent) { return; } - _pixel[y][x] = char.codeUnits; + _pixel[y][x] = char.units; } var _currPos = Point(0, 0); @@ -159,7 +159,7 @@ class Canvas { var y = heightCenter ~/ 2; for (final c in text.split('')) { - char(x++, y, c); + char(x++, y, Color(c)); } } diff --git a/lib/text_canvas/primitives.dart b/lib/text_canvas/primitives.dart index 4a64852..5929d06 100644 --- a/lib/text_canvas/primitives.dart +++ b/lib/text_canvas/primitives.dart @@ -13,7 +13,7 @@ class Color { static final Color light = Color('░'); static final Color white = Color(' '); static final Color point = Color('■'); - static final Color transparent = Color('`'); + static final Color transparent = Color(' '); @override bool operator ==(Object other) { @@ -23,6 +23,12 @@ class Color { @override int get hashCode => ListEquality().hash(units); + @override + String toString() { + final symbol = String.fromCharCodes(units); + return '$runtimeType(symbol: "$symbol", unit: $units)'; + } + } class Point { @@ -53,20 +59,20 @@ class BorderStyle { static final BorderStyle round = BorderStyle.fromBorderText( '╭━━╮' - '` `' - '````', + '   ' + '    ', ); - final String topLeft; - final String topRight; - final String bottomRight; - final String bottomLeft; + final Color topLeft; + final Color topRight; + final Color bottomRight; + final Color bottomLeft; - final String top; - final String bottom; + final Color top; + final Color bottom; - final String left; - final String right; + final Color left; + final Color right; BorderStyle._( this.topLeft, @@ -81,14 +87,14 @@ class BorderStyle { factory BorderStyle.fromBorderText(String text) { return BorderStyle._( - text[0], - text[3], - text[11], - text[8], - text[1], - text[9], - text[4], - text[7], + Color(text[0]), + Color(text[3]), + Color(text[11]), + Color(text[8]), + Color(text[1]), + Color(text[9]), + Color(text[4]), + Color(text[7]), ); } } From dcce88cbcf7fb98d0718320d4f191cc9e419a62a Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 26 Dec 2021 01:41:57 +0200 Subject: [PATCH 054/479] Fix toDiagram() --- patterns/composite/products_and_boxes/diagram/diagram.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/composite/products_and_boxes/diagram/diagram.dart b/patterns/composite/products_and_boxes/diagram/diagram.dart index 690c278..04393ab 100644 --- a/patterns/composite/products_and_boxes/diagram/diagram.dart +++ b/patterns/composite/products_and_boxes/diagram/diagram.dart @@ -43,7 +43,7 @@ class Diagram extends RenderElement { // Children nodes RenderRow( children: [ - for (final child in children) child.toRenderElement(), + for (final child in children) child.toDiagram(), ], ), ], From 9a42d9a1c5e847d6ab5c472462ade8a55b2ff402 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 26 Dec 2021 01:43:18 +0200 Subject: [PATCH 055/479] Auto generate canvas size --- patterns/composite/products_and_boxes/diagram/diagram.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/patterns/composite/products_and_boxes/diagram/diagram.dart b/patterns/composite/products_and_boxes/diagram/diagram.dart index 04393ab..c7b50d1 100644 --- a/patterns/composite/products_and_boxes/diagram/diagram.dart +++ b/patterns/composite/products_and_boxes/diagram/diagram.dart @@ -22,7 +22,11 @@ class Diagram extends RenderElement { void render(Canvas dc) => _rootRenderElement.render(dc); String renderToText() { - final dc = Canvas(_rootRenderElement.width, 10); + final dc = Canvas( + (_rootRenderElement.width / 3).ceil(), + _rootRenderElement.height, + fillColor: Color.white, + ); render(dc); return dc.toString(); } From dd08d095b3e5128475aad73ad21d0ef27efd498b Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 26 Dec 2021 01:44:32 +0200 Subject: [PATCH 056/479] Different birders for boxes & products --- .../composite/products_and_boxes/diagram/diagram.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/patterns/composite/products_and_boxes/diagram/diagram.dart b/patterns/composite/products_and_boxes/diagram/diagram.dart index c7b50d1..fa4209a 100644 --- a/patterns/composite/products_and_boxes/diagram/diagram.dart +++ b/patterns/composite/products_and_boxes/diagram/diagram.dart @@ -1,6 +1,7 @@ import 'package:design_patterns_dart/text_canvas.dart'; import '../products/product.dart'; +import '../products/product_leaf.dart'; import 'render_text.dart'; import 'render_column.dart'; import 'render_element.dart'; @@ -60,17 +61,17 @@ extension ExtConvertProductToRenderElement on Product { RenderElement toRenderElement() { return RenderText( content, - borderStyle: borderStyleBySize(size), + borderStyle: borderStyleBySize(), ); } - static BorderStyle borderStyleBySize(int size) { + BorderStyle? borderStyleBySize() { if (size > 4) { return BorderStyle.bold; } else if (size >= 2) { return BorderStyle.double; - } else if (size == 1) { - return BorderStyle.single; + } else if (this is ProductLeaf) { + return null; } else { return BorderStyle.single; // todo: } From 896e395e9da653e05af3c68500685ae87fa6930d Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 27 Dec 2021 14:46:58 +0200 Subject: [PATCH 057/479] Add RenderPosition --- .../diagram/render_position.dart | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 patterns/composite/products_and_boxes/diagram/render_position.dart diff --git a/patterns/composite/products_and_boxes/diagram/render_position.dart b/patterns/composite/products_and_boxes/diagram/render_position.dart new file mode 100644 index 0000000..088a43f --- /dev/null +++ b/patterns/composite/products_and_boxes/diagram/render_position.dart @@ -0,0 +1,30 @@ +import 'package:design_patterns_dart/text_canvas/canvas.dart'; +import 'package:design_patterns_dart/text_canvas/primitives.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, + }); + + @override + int get height => child.height; + + @override + int get width => child.width; + + @override + void render(Canvas dc) { + final oldTranslate = dc.translate; + dc.translate = Point(x, y); + child.render(dc); + dc.translate = oldTranslate; + } +} From 147dfc9d6e8910a867d63f6393235321039f763b Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 27 Dec 2021 16:27:13 +0200 Subject: [PATCH 058/479] Use RenderPosition in Layouts --- .../diagram/layout_render.dart | 12 ++++++ .../diagram/render_column.dart | 37 ++++++++++--------- .../diagram/render_position.dart | 2 +- .../diagram/render_row.dart | 33 +++++++++-------- 4 files changed, 49 insertions(+), 35 deletions(-) diff --git a/patterns/composite/products_and_boxes/diagram/layout_render.dart b/patterns/composite/products_and_boxes/diagram/layout_render.dart index 6010983..f71eddc 100644 --- a/patterns/composite/products_and_boxes/diagram/layout_render.dart +++ b/patterns/composite/products_and_boxes/diagram/layout_render.dart @@ -1,8 +1,13 @@ import 'dart:math'; +import 'package:design_patterns_dart/text_canvas.dart'; + import 'render_element.dart'; +import 'render_position.dart'; abstract class RenderLayout extends RenderElement { + List get positions; + final List children; final int space; @@ -18,4 +23,11 @@ abstract class RenderLayout extends RenderElement { 0, (height, renderElement) => height + renderElement.height); int get spacesSum => max(0, (children.length - 1) * space); + + @override + void render(Canvas dc) { + for(final child in positions) { + child.render(dc); + } + } } diff --git a/patterns/composite/products_and_boxes/diagram/render_column.dart b/patterns/composite/products_and_boxes/diagram/render_column.dart index 1f20123..cfad2bd 100644 --- a/patterns/composite/products_and_boxes/diagram/render_column.dart +++ b/patterns/composite/products_and_boxes/diagram/render_column.dart @@ -1,7 +1,6 @@ -import 'package:design_patterns_dart/text_canvas.dart'; - import 'layout_render.dart'; import 'render_element.dart'; +import 'render_position.dart'; class RenderColumn extends RenderLayout { RenderColumn({ @@ -10,26 +9,28 @@ class RenderColumn extends RenderLayout { }) : super(children: children, space: space); @override - void render(Canvas dc) { - final restoreTranslate = dc.translate; - var y = dc.translate.y; + int get width => children.reduce((a, b) => a.width > b.width ? a : b).width; + + @override + int get height => childHeight + spacesSum; + + @override + List get positions { + final result = []; + var y = 0; + for (final child in children) { final xCenter = (width - child.width) ~/ 2; - dc.translate = Point(xCenter + dc.translate.x, dc.translate.y); - - child.render(dc); + result.add( + RenderPosition( + x: xCenter, + y: y, + child: child, + ), + ); y += child.height + space; - dc.translate = Point(restoreTranslate.x, y); } - dc.translate = restoreTranslate; - } - - @override - int get width => children.reduce((a, b) => a.width > b.width ? a : b).width; - - @override - int get height { - return childHeight + spacesSum; + return result; } } diff --git a/patterns/composite/products_and_boxes/diagram/render_position.dart b/patterns/composite/products_and_boxes/diagram/render_position.dart index 088a43f..c6d5134 100644 --- a/patterns/composite/products_and_boxes/diagram/render_position.dart +++ b/patterns/composite/products_and_boxes/diagram/render_position.dart @@ -23,7 +23,7 @@ class RenderPosition extends RenderElement { @override void render(Canvas dc) { final oldTranslate = dc.translate; - dc.translate = Point(x, y); + dc.translate = Point(oldTranslate.x + x, oldTranslate.y + y); child.render(dc); dc.translate = oldTranslate; } diff --git a/patterns/composite/products_and_boxes/diagram/render_row.dart b/patterns/composite/products_and_boxes/diagram/render_row.dart index 4a52451..2f07974 100644 --- a/patterns/composite/products_and_boxes/diagram/render_row.dart +++ b/patterns/composite/products_and_boxes/diagram/render_row.dart @@ -1,7 +1,6 @@ -import 'package:design_patterns_dart/text_canvas.dart'; - import 'layout_render.dart'; import 'render_element.dart'; +import 'render_position.dart'; class RenderRow extends RenderLayout { RenderRow({ @@ -10,23 +9,25 @@ class RenderRow extends RenderLayout { }) : super(children: children, space: space); @override - void render(Canvas dc) { - final restoreTranslate = dc.translate; - var x = dc.translate.x; + int get width => childWidth + spacesSum; + + @override + int get height => + children.reduce((a, b) => a.height > b.height ? a : b).height; + + @override + List get positions { + final result = []; + var x = 0; + final y = 0; + for (final child in children) { - child.render(dc); + result.add( + RenderPosition(x: x, y: y, child: child), + ); x += child.width + space; - dc.translate = Point(x, restoreTranslate.y); } - dc.translate = restoreTranslate; - } - @override - int get width { - return childWidth + spacesSum; + return result; } - - @override - int get height => - children.reduce((a, b) => a.width > b.height ? a : b).height; } From b15de044b619eef9894cec4e774b9bf71d7d2e6f Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 27 Dec 2021 20:00:14 +0200 Subject: [PATCH 059/479] Don't draw empty border --- lib/text_canvas/canvas.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/text_canvas/canvas.dart b/lib/text_canvas/canvas.dart index 8405af3..8ce81d8 100644 --- a/lib/text_canvas/canvas.dart +++ b/lib/text_canvas/canvas.dart @@ -126,6 +126,10 @@ class Canvas { } void border(int width, int height, BorderStyle borderStyle) { + if (borderStyle == BorderStyle.empty) { + return; + } + assert(width >= 2); assert(height >= 2); From a54740928b3a6d8ed1339b069252fad4f92dfe44 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 27 Dec 2021 20:01:15 +0200 Subject: [PATCH 060/479] Remove custom pixel color --- lib/text_canvas/canvas.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/text_canvas/canvas.dart b/lib/text_canvas/canvas.dart index 8ce81d8..399c2b7 100644 --- a/lib/text_canvas/canvas.dart +++ b/lib/text_canvas/canvas.dart @@ -30,7 +30,7 @@ class Canvas { var translate = Point(0, 0); var penColor = Color.black; - void setPixel(int x, int y, [int? colorCode]) { + void setPixel(int x, int y) { x += translate.x; y += translate.y; From 22d88e3e2d6f4cda6c0ea3ec8e6cdc815b857dd4 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 27 Dec 2021 20:02:41 +0200 Subject: [PATCH 061/479] Add border bottom connector --- lib/text_canvas/primitives.dart | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/text_canvas/primitives.dart b/lib/text_canvas/primitives.dart index 5929d06..15c4f22 100644 --- a/lib/text_canvas/primitives.dart +++ b/lib/text_canvas/primitives.dart @@ -42,19 +42,19 @@ class BorderStyle { static final BorderStyle bold = BorderStyle.fromBorderText( '▄▄▄▄' '█ █' - '▀▀▀▀', + '▀▀█▀', ); static final BorderStyle double = BorderStyle.fromBorderText( '╔══╗' '║ ║' - '╚══╝', + '╚═╦╝', ); static final BorderStyle single = BorderStyle.fromBorderText( '┌──┐' '│ │' - '└──┘', + '└─┬┘', ); static final BorderStyle round = BorderStyle.fromBorderText( @@ -74,7 +74,9 @@ class BorderStyle { final Color left; final Color right; - BorderStyle._( + final Color bottomConnect; + + const BorderStyle._( this.topLeft, this.topRight, this.bottomRight, @@ -83,18 +85,20 @@ class BorderStyle { this.bottom, this.left, this.right, + this.bottomConnect, ); factory BorderStyle.fromBorderText(String text) { return BorderStyle._( Color(text[0]), - Color(text[3]), - Color(text[11]), - Color(text[8]), - Color(text[1]), - Color(text[9]), - Color(text[4]), - Color(text[7]), + Color(text[3]), + Color(text[11]), + Color(text[8]), + Color(text[1]), + Color(text[9]), + Color(text[4]), + Color(text[7]), + Color(text[10]), ); } } From 5d1fc0d0ac8337ee021aa3c4338d46f1308fc7f9 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 27 Dec 2021 20:04:00 +0200 Subject: [PATCH 062/479] Add empty border --- lib/text_canvas/primitives.dart | 6 +++++- .../composite/products_and_boxes/diagram/diagram.dart | 4 ++-- .../products_and_boxes/diagram/render_text.dart | 9 +++++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/text_canvas/primitives.dart b/lib/text_canvas/primitives.dart index 15c4f22..2872098 100644 --- a/lib/text_canvas/primitives.dart +++ b/lib/text_canvas/primitives.dart @@ -28,7 +28,6 @@ class Color { final symbol = String.fromCharCodes(units); return '$runtimeType(symbol: "$symbol", unit: $units)'; } - } class Point { @@ -63,6 +62,11 @@ class BorderStyle { '    ', ); + static final BorderStyle empty = BorderStyle.fromBorderText( + '    ' + '   ' + '    ', + ); final Color topLeft; final Color topRight; final Color bottomRight; diff --git a/patterns/composite/products_and_boxes/diagram/diagram.dart b/patterns/composite/products_and_boxes/diagram/diagram.dart index fa4209a..e1df588 100644 --- a/patterns/composite/products_and_boxes/diagram/diagram.dart +++ b/patterns/composite/products_and_boxes/diagram/diagram.dart @@ -65,13 +65,13 @@ extension ExtConvertProductToRenderElement on Product { ); } - BorderStyle? borderStyleBySize() { + BorderStyle borderStyleBySize() { if (size > 4) { return BorderStyle.bold; } else if (size >= 2) { return BorderStyle.double; } else if (this is ProductLeaf) { - return null; + return BorderStyle.empty; } else { return BorderStyle.single; // todo: } diff --git a/patterns/composite/products_and_boxes/diagram/render_text.dart b/patterns/composite/products_and_boxes/diagram/render_text.dart index 436ff6d..3bb3abb 100644 --- a/patterns/composite/products_and_boxes/diagram/render_text.dart +++ b/patterns/composite/products_and_boxes/diagram/render_text.dart @@ -1,12 +1,13 @@ import 'package:design_patterns_dart/text_canvas.dart'; +import 'package:design_patterns_dart/text_canvas/primitives.dart'; import 'render_element.dart'; class RenderText extends RenderElement { final String text; - final BorderStyle? borderStyle; + final BorderStyle borderStyle; - RenderText(this.text, {this.borderStyle}); + RenderText(this.text, {required this.borderStyle}); @override int get width => text.length + 2 + 2; @@ -16,8 +17,8 @@ class RenderText extends RenderElement { @override void render(Canvas dc) { - if (borderStyle != null) { - dc.border(width, height, borderStyle!); + if (borderStyle != BorderStyle.empty) { + dc.border(width, height, borderStyle); } dc.text(text, widthCenter: width, heightCenter: height); } From 9c8a921108d1d65b07f22dfe4541ab5725c692e3 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 27 Dec 2021 22:00:42 +0200 Subject: [PATCH 063/479] Clear diagram duplicates --- .../products_and_boxes/diagram/diagram.dart | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/patterns/composite/products_and_boxes/diagram/diagram.dart b/patterns/composite/products_and_boxes/diagram/diagram.dart index e1df588..3b618bb 100644 --- a/patterns/composite/products_and_boxes/diagram/diagram.dart +++ b/patterns/composite/products_and_boxes/diagram/diagram.dart @@ -40,17 +40,10 @@ class Diagram extends RenderElement { factory Diagram.parentNode(Product product, List children) { return Diagram( - RenderColumn( + RenderConnectingLines( + parent: product.toRenderElement(), children: [ - // Root node - product.toRenderElement(), - - // Children nodes - RenderRow( - children: [ - for (final child in children) child.toDiagram(), - ], - ), + for (final child in children) child.toDiagram()._rootRenderElement, ], ), ); From 2972bbf87f095d1758586c027b680662056de3e3 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 14:00:57 +0200 Subject: [PATCH 064/479] Add price property --- patterns/composite/products_and_boxes/products/box.dart | 9 ++++++++- .../composite/products_and_boxes/products/product.dart | 2 ++ .../products_and_boxes/products/product_leaf.dart | 9 +++++++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/patterns/composite/products_and_boxes/products/box.dart b/patterns/composite/products_and_boxes/products/box.dart index 3eb687c..353eb93 100644 --- a/patterns/composite/products_and_boxes/products/box.dart +++ b/patterns/composite/products_and_boxes/products/box.dart @@ -7,7 +7,11 @@ class Box implements Product { Box({required this.children}); @override - String get content => 'Box($size)'; + String get content { + final places = size > 1 ? "places: $size, " : ""; + final _price = size > 1 ? "price: $price\$" : "$price\$"; + return 'Box($places$_price)'; + } @override Diagram toDiagram() { @@ -17,6 +21,9 @@ class Box implements Product { ); } + @override + int get price => children.fold(0, (sum, product) => sum + product.price); + @override int get size => children.fold(0, (sum, product) => sum + product.size); diff --git a/patterns/composite/products_and_boxes/products/product.dart b/patterns/composite/products_and_boxes/products/product.dart index 42d0173..d5970fd 100644 --- a/patterns/composite/products_and_boxes/products/product.dart +++ b/patterns/composite/products_and_boxes/products/product.dart @@ -5,5 +5,7 @@ abstract class Product { int get size; + int get price; + Diagram toDiagram(); } diff --git a/patterns/composite/products_and_boxes/products/product_leaf.dart b/patterns/composite/products_and_boxes/products/product_leaf.dart index b615204..314d555 100644 --- a/patterns/composite/products_and_boxes/products/product_leaf.dart +++ b/patterns/composite/products_and_boxes/products/product_leaf.dart @@ -3,12 +3,17 @@ import 'product.dart'; class ProductLeaf implements Product { @override - final String content; + String get content => '$name($price\$)'; + + final String name; + + @override + final int price; @override int get size => 1; - ProductLeaf(this.content); + ProductLeaf(this.name, this.price); @override Diagram toDiagram() { From 1dd2932aaa8b8404037ff1659a5c9da56fc93dea Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 14:15:21 +0200 Subject: [PATCH 065/479] Add renderElement converter --- .../convert_product_to_render_element.dart | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 patterns/composite/products_and_boxes/diagram/convert_product_to_render_element.dart diff --git a/patterns/composite/products_and_boxes/diagram/convert_product_to_render_element.dart b/patterns/composite/products_and_boxes/diagram/convert_product_to_render_element.dart new file mode 100644 index 0000000..ebaf344 --- /dev/null +++ b/patterns/composite/products_and_boxes/diagram/convert_product_to_render_element.dart @@ -0,0 +1,30 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import '../products/product.dart'; +import '../products/product_leaf.dart'; +import 'render_element.dart'; +import 'render_text.dart'; + +extension ConvertProductToDiagram on Product { + RenderElement toRenderElement() { + return RenderText( + content, + borderStyle: borderStyleBySize(), + ); + } + + BorderStyle borderStyleBySize() { + // if (size > 4) { + // return BorderStyle.bold; + // } else + // if (size >= 2) { + // return BorderStyle.bold; + // } else + + if (this is ProductLeaf) { + return BorderStyle.empty; + } else { + return BorderStyle.single; + } + } +} From 38ed989c0cd88b52ce2b7e12d90c32f69c489db3 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 14:20:30 +0200 Subject: [PATCH 066/479] Remove RenderElement inheritance from diagram --- .../products_and_boxes/diagram/diagram.dart | 55 +++++-------------- 1 file changed, 15 insertions(+), 40 deletions(-) diff --git a/patterns/composite/products_and_boxes/diagram/diagram.dart b/patterns/composite/products_and_boxes/diagram/diagram.dart index 3b618bb..9f47fdc 100644 --- a/patterns/composite/products_and_boxes/diagram/diagram.dart +++ b/patterns/composite/products_and_boxes/diagram/diagram.dart @@ -1,34 +1,28 @@ import 'package:design_patterns_dart/text_canvas.dart'; +import 'package:design_patterns_dart/text_canvas/primitives.dart'; import '../products/product.dart'; -import '../products/product_leaf.dart'; -import 'render_text.dart'; -import 'render_column.dart'; +import 'convert_product_to_render_element.dart'; +import 'render_connecting_lines.dart'; import 'render_element.dart'; -import 'render_row.dart'; -class Diagram extends RenderElement { - final RenderElement _rootRenderElement; +class Diagram { + final RenderElement rootRenderElement; - Diagram(RenderElement root) - : _rootRenderElement = (root is Diagram) ? root._rootRenderElement : root; - - @override - int get width => _rootRenderElement.width; - - @override - int get height => _rootRenderElement.height; - - @override - void render(Canvas dc) => _rootRenderElement.render(dc); + Diagram(this.rootRenderElement); String renderToText() { + const pixelWidth = 3; + final width = (rootRenderElement.width / pixelWidth).ceil(); + final height = rootRenderElement.height; final dc = Canvas( - (_rootRenderElement.width / 3).ceil(), - _rootRenderElement.height, + width, + height, + lineStretch: pixelWidth, fillColor: Color.white, ); - render(dc); + rootRenderElement.render(dc); + return dc.toString(); } @@ -43,30 +37,11 @@ class Diagram extends RenderElement { RenderConnectingLines( parent: product.toRenderElement(), children: [ - for (final child in children) child.toDiagram()._rootRenderElement, + for (final child in children) child.toDiagram().rootRenderElement, ], ), ); } } -extension ExtConvertProductToRenderElement on Product { - RenderElement toRenderElement() { - return RenderText( - content, - borderStyle: borderStyleBySize(), - ); - } - BorderStyle borderStyleBySize() { - if (size > 4) { - return BorderStyle.bold; - } else if (size >= 2) { - return BorderStyle.double; - } else if (this is ProductLeaf) { - return BorderStyle.empty; - } else { - return BorderStyle.single; // todo: - } - } -} From 7c65634e9d89ce83b70a7d269442497beebc5c08 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 14:21:33 +0200 Subject: [PATCH 067/479] Replace position to compute() in layouts --- .../composite/products_and_boxes/diagram/render_column.dart | 4 ++-- .../diagram/{layout_render.dart => render_layout.dart} | 6 ++++-- .../composite/products_and_boxes/diagram/render_row.dart | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) rename patterns/composite/products_and_boxes/diagram/{layout_render.dart => render_layout.dart} (84%) diff --git a/patterns/composite/products_and_boxes/diagram/render_column.dart b/patterns/composite/products_and_boxes/diagram/render_column.dart index cfad2bd..ec06fe9 100644 --- a/patterns/composite/products_and_boxes/diagram/render_column.dart +++ b/patterns/composite/products_and_boxes/diagram/render_column.dart @@ -1,4 +1,4 @@ -import 'layout_render.dart'; +import 'render_layout.dart'; import 'render_element.dart'; import 'render_position.dart'; @@ -15,7 +15,7 @@ class RenderColumn extends RenderLayout { int get height => childHeight + spacesSum; @override - List get positions { + List compute() { final result = []; var y = 0; diff --git a/patterns/composite/products_and_boxes/diagram/layout_render.dart b/patterns/composite/products_and_boxes/diagram/render_layout.dart similarity index 84% rename from patterns/composite/products_and_boxes/diagram/layout_render.dart rename to patterns/composite/products_and_boxes/diagram/render_layout.dart index f71eddc..3dc9d08 100644 --- a/patterns/composite/products_and_boxes/diagram/layout_render.dart +++ b/patterns/composite/products_and_boxes/diagram/render_layout.dart @@ -6,7 +6,9 @@ import 'render_element.dart'; import 'render_position.dart'; abstract class RenderLayout extends RenderElement { - List get positions; + late final List positions = compute(); + + List compute(); final List children; final int space; @@ -26,7 +28,7 @@ abstract class RenderLayout extends RenderElement { @override void render(Canvas dc) { - for(final child in positions) { + for (final child in positions) { child.render(dc); } } diff --git a/patterns/composite/products_and_boxes/diagram/render_row.dart b/patterns/composite/products_and_boxes/diagram/render_row.dart index 2f07974..c886117 100644 --- a/patterns/composite/products_and_boxes/diagram/render_row.dart +++ b/patterns/composite/products_and_boxes/diagram/render_row.dart @@ -1,4 +1,4 @@ -import 'layout_render.dart'; +import 'render_layout.dart'; import 'render_element.dart'; import 'render_position.dart'; @@ -16,7 +16,7 @@ class RenderRow extends RenderLayout { children.reduce((a, b) => a.height > b.height ? a : b).height; @override - List get positions { + List compute() { final result = []; var x = 0; final y = 0; From 239ebebf700ef7385f32a051106da0a0ee846b32 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 14:24:50 +0200 Subject: [PATCH 068/479] Put render elements to separate folder --- .../convert_product_to_render_element.dart | 5 +++-- .../products_and_boxes/diagram/diagram.dart | 5 +++-- .../composite/products_and_boxes/main.dart | 19 +++++++------------ .../render_column.dart | 2 +- .../render_element.dart | 0 .../render_layout.dart | 0 .../render_position.dart | 0 .../render_row.dart | 2 +- .../render_text.dart | 0 9 files changed, 15 insertions(+), 18 deletions(-) rename patterns/composite/products_and_boxes/{diagram => render_elements}/render_column.dart (100%) rename patterns/composite/products_and_boxes/{diagram => render_elements}/render_element.dart (100%) rename patterns/composite/products_and_boxes/{diagram => render_elements}/render_layout.dart (100%) rename patterns/composite/products_and_boxes/{diagram => render_elements}/render_position.dart (100%) rename patterns/composite/products_and_boxes/{diagram => render_elements}/render_row.dart (100%) rename patterns/composite/products_and_boxes/{diagram => render_elements}/render_text.dart (100%) diff --git a/patterns/composite/products_and_boxes/diagram/convert_product_to_render_element.dart b/patterns/composite/products_and_boxes/diagram/convert_product_to_render_element.dart index ebaf344..e91f5ce 100644 --- a/patterns/composite/products_and_boxes/diagram/convert_product_to_render_element.dart +++ b/patterns/composite/products_and_boxes/diagram/convert_product_to_render_element.dart @@ -2,8 +2,9 @@ import 'package:design_patterns_dart/text_canvas.dart'; import '../products/product.dart'; import '../products/product_leaf.dart'; -import 'render_element.dart'; -import 'render_text.dart'; +import '../render_elements/render_element.dart'; +import '../render_elements/render_text.dart'; + extension ConvertProductToDiagram on Product { RenderElement toRenderElement() { diff --git a/patterns/composite/products_and_boxes/diagram/diagram.dart b/patterns/composite/products_and_boxes/diagram/diagram.dart index 9f47fdc..2a59bcf 100644 --- a/patterns/composite/products_and_boxes/diagram/diagram.dart +++ b/patterns/composite/products_and_boxes/diagram/diagram.dart @@ -2,9 +2,10 @@ import 'package:design_patterns_dart/text_canvas.dart'; import 'package:design_patterns_dart/text_canvas/primitives.dart'; import '../products/product.dart'; +import '../render_elements/render_connecting_lines.dart'; +import '../render_elements/render_element.dart'; import 'convert_product_to_render_element.dart'; -import 'render_connecting_lines.dart'; -import 'render_element.dart'; + class Diagram { final RenderElement rootRenderElement; diff --git a/patterns/composite/products_and_boxes/main.dart b/patterns/composite/products_and_boxes/main.dart index a181cc8..42264c3 100644 --- a/patterns/composite/products_and_boxes/main.dart +++ b/patterns/composite/products_and_boxes/main.dart @@ -1,41 +1,36 @@ -import 'package:design_patterns_dart/text_canvas.dart'; - -import 'diagram/render_element.dart'; import 'products/box.dart'; import 'products/product.dart'; import 'products/product_leaf.dart'; void main() { - final dc = Canvas(6, 6); -print( - BorderText('Node', BorderStyle.single).render(dc) -); + final diagram = createParcel().toDiagram(); + print(diagram.renderToText()); } Product createParcel() { return Box( children: [ Box.single( - ProductLeaf('🔨'), + ProductLeaf('Molot', 9), ), Box( children: [ Box( children: [ Box.single( - ProductLeaf('📱'), + ProductLeaf('Phone', 450), ), Box.single( - ProductLeaf('🎧'), + ProductLeaf('Hidrophone', 30), ), ], ), Box.single( - ProductLeaf('🔌'), + ProductLeaf('Lamp', 25), ), ], ), - ProductLeaf('🧾'), + ProductLeaf('Paychak', 0), ], ); } diff --git a/patterns/composite/products_and_boxes/diagram/render_column.dart b/patterns/composite/products_and_boxes/render_elements/render_column.dart similarity index 100% rename from patterns/composite/products_and_boxes/diagram/render_column.dart rename to patterns/composite/products_and_boxes/render_elements/render_column.dart index ec06fe9..d40b85b 100644 --- a/patterns/composite/products_and_boxes/diagram/render_column.dart +++ b/patterns/composite/products_and_boxes/render_elements/render_column.dart @@ -1,5 +1,5 @@ -import 'render_layout.dart'; import 'render_element.dart'; +import 'render_layout.dart'; import 'render_position.dart'; class RenderColumn extends RenderLayout { diff --git a/patterns/composite/products_and_boxes/diagram/render_element.dart b/patterns/composite/products_and_boxes/render_elements/render_element.dart similarity index 100% rename from patterns/composite/products_and_boxes/diagram/render_element.dart rename to patterns/composite/products_and_boxes/render_elements/render_element.dart diff --git a/patterns/composite/products_and_boxes/diagram/render_layout.dart b/patterns/composite/products_and_boxes/render_elements/render_layout.dart similarity index 100% rename from patterns/composite/products_and_boxes/diagram/render_layout.dart rename to patterns/composite/products_and_boxes/render_elements/render_layout.dart diff --git a/patterns/composite/products_and_boxes/diagram/render_position.dart b/patterns/composite/products_and_boxes/render_elements/render_position.dart similarity index 100% rename from patterns/composite/products_and_boxes/diagram/render_position.dart rename to patterns/composite/products_and_boxes/render_elements/render_position.dart diff --git a/patterns/composite/products_and_boxes/diagram/render_row.dart b/patterns/composite/products_and_boxes/render_elements/render_row.dart similarity index 100% rename from patterns/composite/products_and_boxes/diagram/render_row.dart rename to patterns/composite/products_and_boxes/render_elements/render_row.dart index c886117..a363819 100644 --- a/patterns/composite/products_and_boxes/diagram/render_row.dart +++ b/patterns/composite/products_and_boxes/render_elements/render_row.dart @@ -1,5 +1,5 @@ -import 'render_layout.dart'; import 'render_element.dart'; +import 'render_layout.dart'; import 'render_position.dart'; class RenderRow extends RenderLayout { diff --git a/patterns/composite/products_and_boxes/diagram/render_text.dart b/patterns/composite/products_and_boxes/render_elements/render_text.dart similarity index 100% rename from patterns/composite/products_and_boxes/diagram/render_text.dart rename to patterns/composite/products_and_boxes/render_elements/render_text.dart From cfe48b6ebd65a8cb12b2aeb9eeb179399b3981a6 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 16:49:08 +0200 Subject: [PATCH 069/479] Add render connecting lines --- .../render_connecting_lines.dart | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 patterns/composite/products_and_boxes/render_elements/render_connecting_lines.dart 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 new file mode 100644 index 0000000..fce154f --- /dev/null +++ b/patterns/composite/products_and_boxes/render_elements/render_connecting_lines.dart @@ -0,0 +1,99 @@ +import 'package:design_patterns_dart/text_canvas.dart'; +import 'package:design_patterns_dart/text_canvas/canvas.dart'; +import 'package:design_patterns_dart/text_canvas/primitives.dart'; + +import '../render_elements/render_column.dart'; +import 'render_element.dart'; +import 'render_position.dart'; +import 'render_row.dart'; +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, + }) { + final row = RenderRow( + children: children, + ); + + _child = RenderColumn( + children: [ + parent, + row, + ], + ); + _lines = _Lines(_child.positions.first, row.positions); + } + + @override + int get height => _child.height; + + @override + int get width => _child.width; + + @override + void render(Canvas dc) { + _child.render(dc); + _lines.render(dc); + } +} + +class _Lines { + final RenderPosition parent; + final List childPos; + + _Lines(this.parent, this.childPos); + + void render(Canvas dc) { + if (parent.child is RenderText) { + final x = parent.x + parent.width ~/ 2; + var y = parent.y + parent.height - 1; + dc.char(x, y, Color('┬')); + + if (childPos.length == 1) { + dc.char(x, ++y, Color('│')); + dc.char(x, ++y, Color('┴')); + return; + } + + if (childPos.length > 2) { + dc.char(x, ++y, Color('┼')); + } + + dc.char(x, ++y, Color('┴')); + + if (childPos.length == 2) { + y++; + } + + drawLeftLine(x, y, dc); + drawRightLine(x, y, dc); + } + } + + void drawLeftLine(int parentCenterX, int y, Canvas dc) { + var x = childPos.first.x + (childPos.first.width ~/ 2); + dc.char(x, y, Color('┴')); + dc.char(x, --y, Color('┌')); + + while (x < parentCenterX - 1) { + dc.char(++x, y, Color('─')); + } + } + + void drawRightLine(int parentCenterX, int y, Canvas dc) { + final currChild = childPos.length == 2 ? childPos[1] : childPos[2]; + var x = currChild.x + (currChild.width ~/ 2); + dc.char(x, y, Color('┴')); + dc.char(x, --y, Color('┐')); + + while (x > parentCenterX + 1) { + dc.char(--x, y, Color('─')); + } + } +} From 61b7536451e6acff1f83a726eddc44f1fec81b8a Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 17:06:26 +0200 Subject: [PATCH 070/479] Fix product names --- patterns/composite/products_and_boxes/main.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/patterns/composite/products_and_boxes/main.dart b/patterns/composite/products_and_boxes/main.dart index 42264c3..37d1595 100644 --- a/patterns/composite/products_and_boxes/main.dart +++ b/patterns/composite/products_and_boxes/main.dart @@ -11,7 +11,7 @@ Product createParcel() { return Box( children: [ Box.single( - ProductLeaf('Molot', 9), + ProductLeaf('Hammer', 9), ), Box( children: [ @@ -21,16 +21,16 @@ Product createParcel() { ProductLeaf('Phone', 450), ), Box.single( - ProductLeaf('Hidrophone', 30), + ProductLeaf('Headphones', 30), ), ], ), Box.single( - ProductLeaf('Lamp', 25), + ProductLeaf('Charger', 25), ), ], ), - ProductLeaf('Paychak', 0), + ProductLeaf('Receipt', 0), ], ); } From a54ed1b857f796352e24d3283cb3fa30d6dce1f5 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 17:06:51 +0200 Subject: [PATCH 071/479] Add README to composite pattern --- .../composite/products_and_boxes/README.md | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 patterns/composite/products_and_boxes/README.md diff --git a/patterns/composite/products_and_boxes/README.md b/patterns/composite/products_and_boxes/README.md new file mode 100644 index 0000000..1df6648 --- /dev/null +++ b/patterns/composite/products_and_boxes/README.md @@ -0,0 +1,64 @@ +# Composite pattern + +![problem-en](https://user-images.githubusercontent.com/8049534/147579298-0c60c4a7-6acb-4ab3-a973-e06524c5a061.png) + +**Description:** +https://refactoring.guru/design-patterns/composite?#problem + +**Diagram:** + +![image](https://user-images.githubusercontent.com/8049534/147579175-f5ce6191-a76a-4f1f-8ac9-fae1a26f87bb.png) + +**Client code:** +```dart +main() { + Box( + children: [ + Box.single( + ProductLeaf('Hammer', 9), + ), + Box( + children: [ + Box( + children: [ + Box.single( + ProductLeaf('Phone', 450), + ), + Box.single( + ProductLeaf('Headphones', 30), + ), + ], + ), + Box.single( + ProductLeaf('Charger', 25), + ), + ], + ), + ProductLeaf('Receipt', 0), + ], + ); +} +``` + +**Output:** +``` + ┌─────────────────────────────┐ + │ Box(places: 5, price: 514$) │ + └──────────────┬──────────────┘ + ┌─────────────────────────────────────┼─────────────────────────────────────┐ + ┌─────┴───┐ ┌───────────────┴─────────────┐ ┴ + │ Box(9$) │ │ Box(places: 3, price: 505$) │ Receipt(0$) + └────┬────┘ └──────────────┬──────────────┘ + │ ┌────────┴────────────────────┐ + ┴ ┌──────────────┴──────────────┐ ┌─────┴────┐ + Hammer(9$) │ Box(places: 2, price: 480$) │ │ Box(25$) │ + └──────────────┬──────────────┘ └─────┬────┘ + ┌──────────┴────────┐ │ + ┌─────┴─────┐ ┌─────┴────┐ ┴ + │ Box(450$) │ │ Box(30$) │ Charger(25$) + └─────┬─────┘ └─────┬────┘ + │ │ + ┴ ┴ + Phone(450$) Headphones(30$) + +``` From 0e5befc16344dd073c1c2a0a190726b354f44602 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 17:06:51 +0200 Subject: [PATCH 072/479] Add README to composite pattern --- .../composite/products_and_boxes/README.md | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 patterns/composite/products_and_boxes/README.md diff --git a/patterns/composite/products_and_boxes/README.md b/patterns/composite/products_and_boxes/README.md new file mode 100644 index 0000000..a422c9e --- /dev/null +++ b/patterns/composite/products_and_boxes/README.md @@ -0,0 +1,70 @@ +# Composite pattern + +![problem-en](https://user-images.githubusercontent.com/8049534/147579298-0c60c4a7-6acb-4ab3-a973-e06524c5a061.png) + +**Problem description:** +https://refactoring.guru/design-patterns/composite?#problem + +**Folder description:** + +`/products` - represent product and box (composite pattern) +`/diagramm` - convert products to render elements +`/render_elements` - classes for visualization (real-world composite pattern) + +**Diagram:** + +![image](https://user-images.githubusercontent.com/8049534/147579175-f5ce6191-a76a-4f1f-8ac9-fae1a26f87bb.png) + +**Client code:** +```dart +main() { + Box( + children: [ + Box.single( + ProductLeaf('Hammer', 9), + ), + Box( + children: [ + Box( + children: [ + Box.single( + ProductLeaf('Phone', 450), + ), + Box.single( + ProductLeaf('Headphones', 30), + ), + ], + ), + Box.single( + ProductLeaf('Charger', 25), + ), + ], + ), + ProductLeaf('Receipt', 0), + ], + ); +} +``` + +**Output:** +``` + ┌─────────────────────────────┐ + │ Box(places: 5, price: 514$) │ + └──────────────┬──────────────┘ + ┌─────────────────────────────────────┼─────────────────────────────────────┐ + ┌─────┴───┐ ┌───────────────┴─────────────┐ ┴ + │ Box(9$) │ │ Box(places: 3, price: 505$) │ Receipt(0$) + └────┬────┘ └──────────────┬──────────────┘ + │ ┌────────┴────────────────────┐ + ┴ ┌──────────────┴──────────────┐ ┌─────┴────┐ + Hammer(9$) │ Box(places: 2, price: 480$) │ │ Box(25$) │ + └──────────────┬──────────────┘ └─────┬────┘ + ┌──────────┴────────┐ │ + ┌─────┴─────┐ ┌─────┴────┐ ┴ + │ Box(450$) │ │ Box(30$) │ Charger(25$) + └─────┬─────┘ └─────┬────┘ + │ │ + ┴ ┴ + Phone(450$) Headphones(30$) + +``` From 4d4d0f63ad62f13ff6d4abaad61af0b5352b5ef1 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 20:01:20 +0200 Subject: [PATCH 073/479] Add enter after description --- patterns/bridge/devices_remote_control/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/patterns/bridge/devices_remote_control/README.md b/patterns/bridge/devices_remote_control/README.md index 51de5a2..c0166f1 100644 --- a/patterns/bridge/devices_remote_control/README.md +++ b/patterns/bridge/devices_remote_control/README.md @@ -7,6 +7,7 @@ But removed the ability to use null for devices. Instead, the EmptyDevice class ![image](https://user-images.githubusercontent.com/8049534/145878324-3cbc52f5-51f4-4642-921d-69fbe2886f8c.png) **Description:** + https://refactoring.guru/design-patterns/bridge?#pseudocode **Output:** From 18b601d4851eac53841c5a9cbad2fd2261ac8af0 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 20:02:57 +0200 Subject: [PATCH 074/479] Remove unnecessary getter in GraphElement-s --- .../text_graphics/third_party_graphics_lib/star.dart | 6 ++---- .../text_graphics/third_party_graphics_lib/wave.dart | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/patterns/adapter/text_graphics/third_party_graphics_lib/star.dart b/patterns/adapter/text_graphics/third_party_graphics_lib/star.dart index 970d2e4..f08133f 100644 --- a/patterns/adapter/text_graphics/third_party_graphics_lib/star.dart +++ b/patterns/adapter/text_graphics/third_party_graphics_lib/star.dart @@ -19,11 +19,9 @@ class Star extends GraphElement { list.addAll([0, p3]); list.addAll([p8, p3]); list.addAll([p1, p9]); - _points = Int32List.fromList(list); + points = Int32List.fromList(list); } - late Int32List _points; - @override - Int32List get points => _points; + late Int32List points; } diff --git a/patterns/adapter/text_graphics/third_party_graphics_lib/wave.dart b/patterns/adapter/text_graphics/third_party_graphics_lib/wave.dart index 0f03434..e4f9d6d 100644 --- a/patterns/adapter/text_graphics/third_party_graphics_lib/wave.dart +++ b/patterns/adapter/text_graphics/third_party_graphics_lib/wave.dart @@ -14,11 +14,9 @@ class Wave extends GraphElement { final y = (height + cos(x / pi / waveStep) * height).toInt(); list.addAll([x, y]); } - _points = Int32List.fromList(list); + points = Int32List.fromList(list); } - late Int32List _points; - @override - Int32List get points => _points; + late Int32List points; } From 9bc400206bc1da90709f837ff73f755e5d82ea05 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 20:04:03 +0200 Subject: [PATCH 075/479] Replace Point class to standard Point from math lib --- lib/text_canvas/canvas.dart | 2 ++ lib/text_canvas/primitives.dart | 7 ------- patterns/adapter/text_graphics/engine/shape_engine.dart | 2 ++ .../render_elements/render_position.dart | 3 ++- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/text_canvas/canvas.dart b/lib/text_canvas/canvas.dart index 399c2b7..f9cc52d 100644 --- a/lib/text_canvas/canvas.dart +++ b/lib/text_canvas/canvas.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'primitives.dart'; class Canvas { diff --git a/lib/text_canvas/primitives.dart b/lib/text_canvas/primitives.dart index 2872098..4ce4c73 100644 --- a/lib/text_canvas/primitives.dart +++ b/lib/text_canvas/primitives.dart @@ -30,13 +30,6 @@ class Color { } } -class Point { - final int x; - final int y; - - Point(this.x, this.y); -} - class BorderStyle { static final BorderStyle bold = BorderStyle.fromBorderText( '▄▄▄▄' diff --git a/patterns/adapter/text_graphics/engine/shape_engine.dart b/patterns/adapter/text_graphics/engine/shape_engine.dart index a7a75c9..0b281de 100644 --- a/patterns/adapter/text_graphics/engine/shape_engine.dart +++ b/patterns/adapter/text_graphics/engine/shape_engine.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:design_patterns_dart/text_canvas.dart'; import '../shapes/shape.dart'; 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 c6d5134..28bc7ed 100644 --- a/patterns/composite/products_and_boxes/render_elements/render_position.dart +++ b/patterns/composite/products_and_boxes/render_elements/render_position.dart @@ -1,5 +1,6 @@ +import 'dart:math'; + import 'package:design_patterns_dart/text_canvas/canvas.dart'; -import 'package:design_patterns_dart/text_canvas/primitives.dart'; import 'render_element.dart'; From 20fb3b836f159f2ee5266729e5dd8a692e7f6e39 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 20:04:37 +0200 Subject: [PATCH 076/479] Rename shape_adapter to graph_element_adapter --- patterns/adapter/text_graphics/main.dart | 2 +- .../shapes/{shape_adapter.dart => graph_element_adapter.dart} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename patterns/adapter/text_graphics/shapes/{shape_adapter.dart => graph_element_adapter.dart} (100%) diff --git a/patterns/adapter/text_graphics/main.dart b/patterns/adapter/text_graphics/main.dart index 61fdd84..661fe4d 100644 --- a/patterns/adapter/text_graphics/main.dart +++ b/patterns/adapter/text_graphics/main.dart @@ -2,7 +2,7 @@ import 'package:design_patterns_dart/text_canvas.dart'; import 'engine/shape_engine.dart'; import 'shapes/shape.dart'; -import 'shapes/shape_adapter.dart'; +import 'shapes/graph_element_adapter.dart'; import 'third_party_graphics_lib/star.dart' as third_party; import 'third_party_graphics_lib/wave.dart' as third_party; diff --git a/patterns/adapter/text_graphics/shapes/shape_adapter.dart b/patterns/adapter/text_graphics/shapes/graph_element_adapter.dart similarity index 100% rename from patterns/adapter/text_graphics/shapes/shape_adapter.dart rename to patterns/adapter/text_graphics/shapes/graph_element_adapter.dart From dcfc86c3a6b6c3de96d12945f5a3ae70a1e0e4a4 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 20:08:49 +0200 Subject: [PATCH 077/479] Add diagram to square_round_conflict --- patterns/adapter/square_round_conflict/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/patterns/adapter/square_round_conflict/README.md b/patterns/adapter/square_round_conflict/README.md index 1a37840..7ab3b2e 100644 --- a/patterns/adapter/square_round_conflict/README.md +++ b/patterns/adapter/square_round_conflict/README.md @@ -1,5 +1,9 @@ # Adapter pattern +**Diagram:** + +![example](https://user-images.githubusercontent.com/8049534/147594536-66627fa1-f4eb-42ba-b648-8757f9e5bf20.png) + **Description:** https://refactoring.guru/design-patterns/adapter?#pseudocode From be20c93e7f7e51ab8111a9cf2d0c4b13d5ad357f Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 20:10:37 +0200 Subject: [PATCH 078/479] Fix dart client code in color_text_format README --- patterns/builder/color_text_format/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/patterns/builder/color_text_format/README.md b/patterns/builder/color_text_format/README.md index 28f5b8b..d4eb11f 100644 --- a/patterns/builder/color_text_format/README.md +++ b/patterns/builder/color_text_format/README.md @@ -1,10 +1,11 @@ # Builder pattern Using different text formats -![image](https://user-images.githubusercontent.com/8049534/146023073-5d7644a4-d3b9-4420-bffe-f72ac3fd83dd.png)) +![image](https://user-images.githubusercontent.com/8049534/146023073-5d7644a4-d3b9-4420-bffe-f72ac3fd83dd.png) **Client code:** ```dart +main() { final reader = ColorTextReader( text: 'I love looking at the blue sky, ' 'eating red apples, ' @@ -18,6 +19,7 @@ Using different text formats '$json,\n\n' '$console,\n\n', ); +} ``` **Output:** From 95167c9392e9e58eaff651717664d030c3e4ca9d Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 20:14:34 +0200 Subject: [PATCH 079/479] Add client code to square_round_conflict README --- .../adapter/square_round_conflict/README.md | 25 +++++++++++++++++++ .../adapter/square_round_conflict/main.dart | 4 +-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/patterns/adapter/square_round_conflict/README.md b/patterns/adapter/square_round_conflict/README.md index 7ab3b2e..37976d4 100644 --- a/patterns/adapter/square_round_conflict/README.md +++ b/patterns/adapter/square_round_conflict/README.md @@ -7,6 +7,31 @@ **Description:** https://refactoring.guru/design-patterns/adapter?#pseudocode +**Client code:** + +```dart +void main() { + final hole = RoundHole(5); + final peg = RoundPeg(5); + if (hole.fits(peg)) { + print("Round peg r5 fits round hole r5."); + } + + final smallSqPeg = SquarePeg(2); + final largeSqPeg = SquarePeg(20); + + 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."); + } +} +``` + **Output:** ``` diff --git a/patterns/adapter/square_round_conflict/main.dart b/patterns/adapter/square_round_conflict/main.dart index 3d5b7d2..483f158 100644 --- a/patterns/adapter/square_round_conflict/main.dart +++ b/patterns/adapter/square_round_conflict/main.dart @@ -6,8 +6,8 @@ void main() { // // RU: Круглое к круглому — всё работает. final hole = RoundHole(5); - final rpeg = RoundPeg(5); - if (hole.fits(rpeg)) { + final peg = RoundPeg(5); + if (hole.fits(peg)) { print("Round peg r5 fits round hole r5."); } From f14ba542c24cd5b60a1851584ac4f2dd221f4f9f Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 20:19:47 +0200 Subject: [PATCH 080/479] Bump version 0.8.5. Update README & CHANGELOG --- CHANGELOG.md | 5 ++++- README.md | 4 ++-- pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dbf391..de6a09e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,11 @@ +## 0.8.5 +- Add composite pattern "products_and_boxes" +- Created a Canvas helper tool for visual presentation of patterns + ## 0.7.8 - Update diagram for text_graphics, clock, remote_control, color_text_format - Code match with diagram in clock - ## 0.7.0 - Add bridge pattern. Clock example diff --git a/README.md b/README.md index 0d3548a..5e4b1e4 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ It contains **Dart** examples for all classic GoF design patterns. - [ ] **Structural** - [x] Adapter - [x] Bridge - - [ ] Composite + - [x] Composite - [ ] Decorator - [ ] Facade - [ ] Flyweight @@ -76,4 +76,4 @@ This work is licensed under a Creative Commons Attribution-NonCommercial-NoDeriv ## Credits -Authors: Alexander Shvets ([@neochief](https://github.com/neochief)) +Authors: Alexander Shvets ([@neochief](https://github.com/neochief)), ilopX ([@ilopX](https://github.com/ilopX)) diff --git a/pubspec.yaml b/pubspec.yaml index 9d86c12..85f5e4c 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.7.8 +version: 0.8.5 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 3019d3ed0a12c80c685694675e97d7e3d9352496 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 28 Dec 2021 23:35:02 +0200 Subject: [PATCH 081/479] Fix folder description in composite pattern README --- patterns/composite/products_and_boxes/README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/patterns/composite/products_and_boxes/README.md b/patterns/composite/products_and_boxes/README.md index a422c9e..2b6d21b 100644 --- a/patterns/composite/products_and_boxes/README.md +++ b/patterns/composite/products_and_boxes/README.md @@ -3,13 +3,14 @@ ![problem-en](https://user-images.githubusercontent.com/8049534/147579298-0c60c4a7-6acb-4ab3-a973-e06524c5a061.png) **Problem description:** + https://refactoring.guru/design-patterns/composite?#problem **Folder description:** -`/products` - represent product and box (composite pattern) -`/diagramm` - convert products to render elements -`/render_elements` - classes for visualization (real-world composite pattern) +- `/products` - represent product and box (composite pattern) +- `/diagram` - convert products to render elements +- `/render_elements` - classes for visualization (real-world composite pattern) **Diagram:** From 789b334699ff49ea88d12ee04e160d4946d6705e Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 5 Jan 2022 14:51:30 +0200 Subject: [PATCH 082/479] Remove unnecessary comments --- .../diagram/convert_product_to_render_element.dart | 7 ------- patterns/composite/products_and_boxes/diagram/diagram.dart | 3 --- 2 files changed, 10 deletions(-) diff --git a/patterns/composite/products_and_boxes/diagram/convert_product_to_render_element.dart b/patterns/composite/products_and_boxes/diagram/convert_product_to_render_element.dart index e91f5ce..a8882b8 100644 --- a/patterns/composite/products_and_boxes/diagram/convert_product_to_render_element.dart +++ b/patterns/composite/products_and_boxes/diagram/convert_product_to_render_element.dart @@ -15,13 +15,6 @@ extension ConvertProductToDiagram on Product { } BorderStyle borderStyleBySize() { - // if (size > 4) { - // return BorderStyle.bold; - // } else - // if (size >= 2) { - // return BorderStyle.bold; - // } else - if (this is ProductLeaf) { return BorderStyle.empty; } else { diff --git a/patterns/composite/products_and_boxes/diagram/diagram.dart b/patterns/composite/products_and_boxes/diagram/diagram.dart index 2a59bcf..ef6502b 100644 --- a/patterns/composite/products_and_boxes/diagram/diagram.dart +++ b/patterns/composite/products_and_boxes/diagram/diagram.dart @@ -6,7 +6,6 @@ import '../render_elements/render_connecting_lines.dart'; import '../render_elements/render_element.dart'; import 'convert_product_to_render_element.dart'; - class Diagram { final RenderElement rootRenderElement; @@ -44,5 +43,3 @@ class Diagram { ); } } - - From d11a266f533c4336063143bf9c28c3bdefd7a7c0 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 11 Jan 2022 15:46:03 +0200 Subject: [PATCH 083/479] Add pattern Decorator. From book translate. --- .../decorator/data_source_decoder/Secret.txt | 1 + .../decorator/data_source_decoder/main.dart | 25 +++++++++++++ .../src/compression_decorator.dart | 37 +++++++++++++++++++ .../data_source_decoder/src/data_source.dart | 5 +++ .../src/data_source_decorator.dart | 17 +++++++++ .../src/encryption_decorator.dart | 29 +++++++++++++++ .../src/file_data_source.dart | 26 +++++++++++++ 7 files changed, 140 insertions(+) create mode 100644 patterns/decorator/data_source_decoder/Secret.txt create mode 100644 patterns/decorator/data_source_decoder/main.dart create mode 100644 patterns/decorator/data_source_decoder/src/compression_decorator.dart create mode 100644 patterns/decorator/data_source_decoder/src/data_source.dart create mode 100644 patterns/decorator/data_source_decoder/src/data_source_decorator.dart create mode 100644 patterns/decorator/data_source_decoder/src/encryption_decorator.dart create mode 100644 patterns/decorator/data_source_decoder/src/file_data_source.dart diff --git a/patterns/decorator/data_source_decoder/Secret.txt b/patterns/decorator/data_source_decoder/Secret.txt new file mode 100644 index 0000000..b175c13 --- /dev/null +++ b/patterns/decorator/data_source_decoder/Secret.txt @@ -0,0 +1 @@ +STV0SkJCQkJCQkJCRHdPTXtGNFdEVjhOVFR6cjZRTUx7OWlVRE44T01Ob1JOVVJCQmI4aGx1VHoyRXhHczB6bFppMk1SelBoRkJCOHhaTzlOQkJCQkI+Pg== \ No newline at end of file diff --git a/patterns/decorator/data_source_decoder/main.dart b/patterns/decorator/data_source_decoder/main.dart new file mode 100644 index 0000000..24dd92e --- /dev/null +++ b/patterns/decorator/data_source_decoder/main.dart @@ -0,0 +1,25 @@ +import 'src/compression_decorator.dart'; +import 'src/data_source.dart'; +import 'src/data_source_decorator.dart'; +import 'src/encryption_decorator.dart'; +import 'src/file_data_source.dart'; + +void main() { + final records = 'Name,Salary\nJohn Smith,100000\nSteven Jobs,912000'; + + // Create encrypt file + DataSourceDecorator encoded = CompressionDecorator( + EncryptionDecorator( + FileDataSource('Secret.txt'), + ), + ); + encoded.writeData(records); + + DataSource plain = FileDataSource('Secret.txt'); + print("- Input ----------------"); + print(records); + print("\n- Encoded --------------"); + print(plain.readData()); + print("\n- Decoded --------------"); + print(encoded.readData()); +} diff --git a/patterns/decorator/data_source_decoder/src/compression_decorator.dart b/patterns/decorator/data_source_decoder/src/compression_decorator.dart new file mode 100644 index 0000000..c9a6131 --- /dev/null +++ b/patterns/decorator/data_source_decoder/src/compression_decorator.dart @@ -0,0 +1,37 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'data_source.dart'; +import 'data_source_decorator.dart'; + +class CompressionDecorator extends DataSourceDecorator { + var compressionLevel = 6; + + CompressionDecorator(DataSource source) : super(source); + + @override + void writeData(String data) { + super.writeData(_compress(data)); + } + + @override + String readData() { + return _decompress(super.readData()); + } + + String _compress(String stringData) { + final gzip = GZipCodec(level: compressionLevel); + final encodeStr = base64.encode( + gzip.encode(stringData.codeUnits), + ); + return encodeStr; + } + + String _decompress(String stringData) { + final gzip = GZipCodec(level: compressionLevel); + final decodeStr = gzip.decode( + base64.decode(stringData), + ); + return String.fromCharCodes(decodeStr); + } +} diff --git a/patterns/decorator/data_source_decoder/src/data_source.dart b/patterns/decorator/data_source_decoder/src/data_source.dart new file mode 100644 index 0000000..bdd5270 --- /dev/null +++ b/patterns/decorator/data_source_decoder/src/data_source.dart @@ -0,0 +1,5 @@ +abstract class DataSource { + void writeData(String data); + + String readData(); +} diff --git a/patterns/decorator/data_source_decoder/src/data_source_decorator.dart b/patterns/decorator/data_source_decoder/src/data_source_decorator.dart new file mode 100644 index 0000000..e057cda --- /dev/null +++ b/patterns/decorator/data_source_decoder/src/data_source_decorator.dart @@ -0,0 +1,17 @@ +import 'data_source.dart'; + +class DataSourceDecorator implements DataSource { + final DataSource _wrapper; + + DataSourceDecorator(this._wrapper); + + @override + void writeData(String data) { + _wrapper.writeData(data); + } + + @override + String readData() { + return _wrapper.readData(); + } +} diff --git a/patterns/decorator/data_source_decoder/src/encryption_decorator.dart b/patterns/decorator/data_source_decoder/src/encryption_decorator.dart new file mode 100644 index 0000000..f276324 --- /dev/null +++ b/patterns/decorator/data_source_decoder/src/encryption_decorator.dart @@ -0,0 +1,29 @@ +import 'dart:convert'; + +import 'data_source.dart'; +import 'data_source_decorator.dart'; + +class EncryptionDecorator extends DataSourceDecorator { + EncryptionDecorator(DataSource source) : super(source); + + @override + void writeData(String data) { + super.writeData(_encode(data)); + } + + @override + String readData() { + return _decode(super.readData()); + } + + String _encode(String data) { + final encrypt = data.codeUnits.map((e) => e + 1).toList(); + return base64.encode(encrypt); + } + + String _decode(String data) { + final encrypt = base64.decode(data); + final decrypt = encrypt.map((e) => e - 1).toList(); + return String.fromCharCodes(decrypt); + } +} diff --git a/patterns/decorator/data_source_decoder/src/file_data_source.dart b/patterns/decorator/data_source_decoder/src/file_data_source.dart new file mode 100644 index 0000000..90d6198 --- /dev/null +++ b/patterns/decorator/data_source_decoder/src/file_data_source.dart @@ -0,0 +1,26 @@ +import 'dart:io'; + +import 'data_source.dart'; + +class FileDataSource implements DataSource { + final String _name; + + FileDataSource(String name) : _name = thisPath(name); + + @override + void writeData(String data) { + File(_name).writeAsStringSync(data); + } + + @override + String readData() { + return File(_name).readAsStringSync(); + } +} + +thisPath(name) => + Platform.script.pathSegments + .sublist(0, Platform.script.pathSegments.length - 1) + .join(Platform.pathSeparator) + + Platform.pathSeparator + + name; From 4fe04e0fa1f0133ec2b87273e11e54dc271131b8 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 11 Jan 2022 15:55:10 +0200 Subject: [PATCH 084/479] Add README --- .../decorator/data_source_decoder/README.md | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 patterns/decorator/data_source_decoder/README.md diff --git a/patterns/decorator/data_source_decoder/README.md b/patterns/decorator/data_source_decoder/README.md new file mode 100644 index 0000000..c50ea2e --- /dev/null +++ b/patterns/decorator/data_source_decoder/README.md @@ -0,0 +1,47 @@ +# 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: +![image](https://user-images.githubusercontent.com/8049534/148954932-edc22d7b-becd-4e2f-bae8-d0d8200d8918.png) + +## Description: +https://refactoring.guru/design-patterns/decorator?#pseudocode + +## Client code: +```dart +void main() { + final records = 'Name,Salary\nJohn Smith,100000\nSteven Jobs,912000'; + + // Create encrypt file + DataSourceDecorator encoded = CompressionDecorator( + EncryptionDecorator( + FileDataSource('Secret.txt'), + ), + ); + encoded.writeData(records); + + DataSource plain = FileDataSource('Secret.txt'); + print("- Input ----------------"); + print(records); + print("\n- Encoded --------------"); + print(plain.readData()); + print("\n- Decoded --------------"); + print(encoded.readData()); +} +``` + +## Output: +``` +- Input ---------------- +Name,Salary +John Smith,100000 +Steven Jobs,912000 + +- Encoded -------------- +STV0SkJCQkJCQkJCRHdPTXtGNFdEVjhOVFR6cjZRTUx7OWlVRE44T01Ob1JOVVJCQmI4aGx1VHoyRXhHczB6bFppMk1SelBoRkJCOHhaTzlOQkJCQkI+Pg== + +- Decoded -------------- +Name,Salary +John Smith,100000 +Steven Jobs,912000 +``` From 779189be1be429a50bd29fc02ccb0e3dce23b38f Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 11 Jan 2022 15:55:28 +0200 Subject: [PATCH 085/479] Bump version 0.9.0 --- CHANGELOG.md | 2 ++ README.md | 2 +- pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de6a09e..7c5bc81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## 0.9.0 +- Add pattern Decorator. Translate from official book java example. ## 0.8.5 - Add composite pattern "products_and_boxes" - Created a Canvas helper tool for visual presentation of patterns diff --git a/README.md b/README.md index 5e4b1e4..68dc834 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ It contains **Dart** examples for all classic GoF design patterns. - [x] Adapter - [x] Bridge - [x] Composite - - [ ] Decorator + - [x] Decorator - [ ] Facade - [ ] Flyweight - [ ] Proxy diff --git a/pubspec.yaml b/pubspec.yaml index 85f5e4c..c3c4347 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.8.5 +version: 0.9.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 a167809f661c3e55191c535559512f2a27973e83 Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 12 Jan 2022 17:39:51 +0200 Subject: [PATCH 086/479] Add composite template from official book, rewritten from Java example --- .../image_editor/editor/image_editor.dart | 20 +++ patterns/composite/image_editor/main.dart | 25 ++++ .../image_editor/shapes/base_shape.dart | 52 +++++++ .../composite/image_editor/shapes/circle.dart | 26 ++++ .../image_editor/shapes/compound_shape.dart | 131 ++++++++++++++++++ .../composite/image_editor/shapes/dot.dart | 20 +++ .../image_editor/shapes/rectangle.dart | 25 ++++ .../composite/image_editor/shapes/shape.dart | 21 +++ 8 files changed, 320 insertions(+) create mode 100644 patterns/composite/image_editor/editor/image_editor.dart create mode 100644 patterns/composite/image_editor/main.dart create mode 100644 patterns/composite/image_editor/shapes/base_shape.dart create mode 100644 patterns/composite/image_editor/shapes/circle.dart create mode 100644 patterns/composite/image_editor/shapes/compound_shape.dart create mode 100644 patterns/composite/image_editor/shapes/dot.dart create mode 100644 patterns/composite/image_editor/shapes/rectangle.dart create mode 100644 patterns/composite/image_editor/shapes/shape.dart diff --git a/patterns/composite/image_editor/editor/image_editor.dart b/patterns/composite/image_editor/editor/image_editor.dart new file mode 100644 index 0000000..5c6de06 --- /dev/null +++ b/patterns/composite/image_editor/editor/image_editor.dart @@ -0,0 +1,20 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import '../shapes/compound_shape.dart'; +import '../shapes/shape.dart'; + +typedef Graphics = Canvas; + +class ImageEditor { + final _allShapes = CompoundShape(); + + void loadShapes(List shapes) { + _allShapes + ..clear() + ..addAll(shapes); + + final graphics = Graphics(_allShapes.width + 2, _allShapes.height + 2); + _allShapes.paint(graphics); + print(graphics.toString()); + } +} diff --git a/patterns/composite/image_editor/main.dart b/patterns/composite/image_editor/main.dart new file mode 100644 index 0000000..dc62a6a --- /dev/null +++ b/patterns/composite/image_editor/main.dart @@ -0,0 +1,25 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import 'editor/image_editor.dart'; +import 'shapes/circle.dart'; +import 'shapes/compound_shape.dart'; +import 'shapes/dot.dart'; +import 'shapes/rectangle.dart'; + +void main() { + final editor = ImageEditor(); + editor.loadShapes([ + Circle(1, 1, 4, Color.grey), + CompoundShape([ + Circle(12, 12, 6, Color.dark), + Dot(12 + 6, 12 + 6, Color.dark), + ])..select(), + CompoundShape([ + Rectangle(31, 31, 10, 10, Color.black), + Dot(28, 28, Color.grey), + Dot(40, 28, Color.grey), + Dot(40, 41, Color.grey), + Dot(28, 41, Color.grey), + ]), + ]); +} diff --git a/patterns/composite/image_editor/shapes/base_shape.dart b/patterns/composite/image_editor/shapes/base_shape.dart new file mode 100644 index 0000000..b6c9dea --- /dev/null +++ b/patterns/composite/image_editor/shapes/base_shape.dart @@ -0,0 +1,52 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import '../editor/image_editor.dart'; +import 'shape.dart'; + +abstract class BaseShape implements Shape { + int _x; + int _y; + final Color color; + + bool _selected = false; + + BaseShape(this._x, this._y, this.color); + + @override + int get x => _x; + + @override + int get y => _y; + + @override + void move(int x, int y) { + _x += x; + _y += y; + } + + @override + void select() => _selected = true; + + @override + void unSelect() => _selected = false; + + @override + bool get isSelected => _selected; + + void enableSelectionStyle(Graphics graphics) { + graphics.penColor = Color.white; + } + + void disableSelectionStyle(Graphics graphics) { + graphics.penColor = color; + } + + @override + void paint(Graphics graphics) { + if (isSelected) { + enableSelectionStyle(graphics); + } else { + disableSelectionStyle(graphics); + } + } +} diff --git a/patterns/composite/image_editor/shapes/circle.dart b/patterns/composite/image_editor/shapes/circle.dart new file mode 100644 index 0000000..1411511 --- /dev/null +++ b/patterns/composite/image_editor/shapes/circle.dart @@ -0,0 +1,26 @@ +import 'dart:math'; + +import 'package:design_patterns_dart/text_canvas.dart'; + +import '../editor/image_editor.dart'; +import 'base_shape.dart'; + +class Circle extends BaseShape { + final int radius; + + Circle(int x, int y, this.radius, Color color) : super(x, y, color); + + @override + int get width => radius * 2; + + @override + int get height => radius * 2; + + @override + void paint(Graphics graphics) { + super.paint(graphics); + graphics + ..translate = Point(x + radius, y + radius) + ..circle(radius); + } +} diff --git a/patterns/composite/image_editor/shapes/compound_shape.dart b/patterns/composite/image_editor/shapes/compound_shape.dart new file mode 100644 index 0000000..751cb78 --- /dev/null +++ b/patterns/composite/image_editor/shapes/compound_shape.dart @@ -0,0 +1,131 @@ +import 'dart:math'; + +import 'package:design_patterns_dart/text_canvas.dart'; +import 'package:design_patterns_dart/text_canvas/primitives.dart'; + +import '../editor/image_editor.dart'; +import 'base_shape.dart'; +import 'shape.dart'; + +class CompoundShape extends BaseShape { + final _children = []; + + CompoundShape([List children = const []]) : super(0, 0, Color.black) { + _children.addAll(children); + } + + void add(Shape component) => _children.add(component); + + void addAll(List children) => _children.addAll(children); + + void remove(Shape child) => _children.remove(child); + + void removeList(List children) { + for (final item in children) { + _children.remove(item); + } + } + + void clear() => _children.clear(); + + @override + int get x { + if (_children.isEmpty) { + return 0; + } + + var minX = _children.fold( + _children.first.x, + (x, Shape element) => element.x > x ? x : element.x, + ); + + return minX; + } + + @override + int get y { + if (_children.isEmpty) { + return 0; + } + + var minY = _children.fold( + _children.first.y, + (y, Shape element) => element.y > y ? y : element.y, + ); + + return minY; + } + + @override + int get width { + int maxWidth = 0; + final y = this.y; + for (final child in _children) { + int childWidth = child.x + child.width - y; + if (childWidth > maxWidth) { + maxWidth = childWidth; + } + } + + return maxWidth; + } + + @override + int get height { + int maxHeight = 0; + final y = this.y; + for (final child in _children) { + final childHeight = child.y + child.height - y; + if (childHeight > maxHeight) { + maxHeight = childHeight; + } + } + + return maxHeight; + } + + @override + void move(int x, int y) { + for (Shape child in _children) { + child.move(x, y); + } + } + + @override + void unSelect() { + super.unSelect(); + for (Shape child in _children) { + child.unSelect(); + } + } + + @override + void select() { + super.select(); + for (Shape child in _children) { + child.select(); + } + } + + @override + void paint(Graphics graphics) { + if (isSelected) { + const padding = 2; + graphics + ..translate = Point( + x * graphics.lineStretch - graphics.lineStretch, + y - 2, + ) + ..border( + (width + 2 + 1) * graphics.lineStretch, + height + padding * 2 + 1, + BorderStyle.single, + ); + } + + for (Shape child in _children) { + graphics.translate = Point(0, 0); + child.paint(graphics); + } + } +} diff --git a/patterns/composite/image_editor/shapes/dot.dart b/patterns/composite/image_editor/shapes/dot.dart new file mode 100644 index 0000000..d52644d --- /dev/null +++ b/patterns/composite/image_editor/shapes/dot.dart @@ -0,0 +1,20 @@ +import 'package:design_patterns_dart/text_canvas.dart'; + +import '../editor/image_editor.dart'; +import 'base_shape.dart'; + +class Dot extends BaseShape { + Dot(int x, int y, Color color) : super(x, y, color); + + @override + int get width => 1; + + @override + int get height => 1; + + @override + void paint(Graphics graphics) { + super.paint(graphics); + graphics.setPixel(x, y); + } +} diff --git a/patterns/composite/image_editor/shapes/rectangle.dart b/patterns/composite/image_editor/shapes/rectangle.dart new file mode 100644 index 0000000..a15fd4c --- /dev/null +++ b/patterns/composite/image_editor/shapes/rectangle.dart @@ -0,0 +1,25 @@ +import 'dart:math'; + +import 'package:design_patterns_dart/text_canvas.dart'; + +import '../editor/image_editor.dart'; +import 'base_shape.dart'; + +class Rectangle extends BaseShape { + @override + final int width; + + @override + final int height; + + Rectangle(int x, int y, this.width, this.height, Color color) + : super(x, y, color); + + @override + void paint(Graphics graphics) { + super.paint(graphics); + graphics + ..translate = Point(x - 1, y - 1) + ..rectangle(width - 2, height - 1); + } +} diff --git a/patterns/composite/image_editor/shapes/shape.dart b/patterns/composite/image_editor/shapes/shape.dart new file mode 100644 index 0000000..010f45c --- /dev/null +++ b/patterns/composite/image_editor/shapes/shape.dart @@ -0,0 +1,21 @@ +import '../editor/image_editor.dart'; + +abstract class Shape { + int get x; + + int get y; + + int get width; + + int get height; + + void move(int x, int y); + + void select(); + + void unSelect(); + + bool get isSelected; + + void paint(Graphics graphics); +} From e8f657d8a65b151655199d9ac0bb350f576bceff Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 12 Jan 2022 18:00:11 +0200 Subject: [PATCH 087/479] Add README --- patterns/composite/image_editor/README.md | 80 +++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 patterns/composite/image_editor/README.md diff --git a/patterns/composite/image_editor/README.md b/patterns/composite/image_editor/README.md new file mode 100644 index 0000000..de15d64 --- /dev/null +++ b/patterns/composite/image_editor/README.md @@ -0,0 +1,80 @@ +# 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: +https://refactoring.guru/design-patterns/composite?#pseudocode + +## Origin source code: +This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/master/src/refactoring_guru/composite/example) + +## Diagram: +![image](https://user-images.githubusercontent.com/8049534/149174388-25ca21b1-d762-40b5-a853-528abe18b66c.png) + +**Client code:** +```dart +void main() { + final editor = ImageEditor(); + editor.loadShapes([ + Circle(1, 1, 4, Color.grey), + CompoundShape([ + Circle(12, 12, 6, Color.dark), + Dot(12 + 6, 12 + 6, Color.dark), + ])..select(), + CompoundShape([ + Rectangle(31, 31, 10, 10, Color.black), + Dot(28, 28, Color.grey), + Dot(40, 28, Color.grey), + Dot(40, 41, Color.grey), + Dot(28, 41, Color.grey), + ]), + ]); +} +``` + +**Output:** +``` +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░▒▒▒▒▒▒░░░░░░░░░▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░▒▒▒░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░▒▒▒░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░▒▒▒▒▒▒░░░░░░░░░▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░┌───────────────────────────────────────────┐░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░░░░░░░░░░ ░░░░░░░░░░░░░░░ ░░░░░░░░░░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░ ░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░ ░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░░░░░░░░░░ ░░░░░░░░░░░░░░░ ░░░░░░░░░░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░└───────────────────────────────────────────┘░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███████████████████████████░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░░░░░░░░███░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███████████████████████████░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ + +``` From 61fdc43ca249f134cee2bdc8aeb56ff53b42c0b1 Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 12 Jan 2022 18:00:34 +0200 Subject: [PATCH 088/479] Bump version 0.10.0 --- CHANGELOG.md | 8 ++++++-- pubspec.yaml | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c5bc81..8e79896 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ +# 0.10.0 +- Add composite template from official book, rewritten from Java example + ## 0.9.0 -- Add pattern Decorator. Translate from official book java example. +- Add decorator pattern. Translate from official book java example + ## 0.8.5 - Add composite pattern "products_and_boxes" - Created a Canvas helper tool for visual presentation of patterns @@ -32,5 +36,5 @@ ## 0.0.1 -- Initial project structure. +- Initial project structure - Add README diff --git a/pubspec.yaml b/pubspec.yaml index c3c4347..e754617 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.9.0 +version: 0.10.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 3068e344827f0686a2fbe5b8951e5d304acef5ca Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 14 Jan 2022 10:32:31 +0200 Subject: [PATCH 089/479] Add "Chain of Responsibility" pattern --- .../server_middleware/main.dart | 51 +++++++++++++++++++ .../middleware/role_check_middleware.dart | 16 ++++++ .../middleware/throttling_middleware.dart | 49 ++++++++++++++++++ .../middleware/user_exists_middleware.dart | 26 ++++++++++ .../server_middleware/server/middleware.dart | 19 +++++++ .../server_middleware/server/server.dart | 34 +++++++++++++ 6 files changed, 195 insertions(+) create mode 100644 patterns/chain_of_responsibility/server_middleware/main.dart create mode 100644 patterns/chain_of_responsibility/server_middleware/middleware/role_check_middleware.dart create mode 100644 patterns/chain_of_responsibility/server_middleware/middleware/throttling_middleware.dart create mode 100644 patterns/chain_of_responsibility/server_middleware/middleware/user_exists_middleware.dart create mode 100644 patterns/chain_of_responsibility/server_middleware/server/middleware.dart create mode 100644 patterns/chain_of_responsibility/server_middleware/server/server.dart diff --git a/patterns/chain_of_responsibility/server_middleware/main.dart b/patterns/chain_of_responsibility/server_middleware/main.dart new file mode 100644 index 0000000..d369e1d --- /dev/null +++ b/patterns/chain_of_responsibility/server_middleware/main.dart @@ -0,0 +1,51 @@ +import 'middleware/role_check_middleware.dart'; +import 'middleware/throttling_middleware.dart'; +import 'middleware/user_exists_middleware.dart'; +import 'server/server.dart'; + +void main() { + final server = Server( + users: { + 'admin@example.com': 'admin_pass', + 'user@example.com': 'user_pass', + }, + // EN: All checks are linked. Client can build various chains using the + // same components. + // + // RU: Проверки связаны в одну цепь. Клиент может строить различные + // цепи, используя одни и те же компоненты. + middleware: ThrottlingMiddleware( + requestPerMinute: 4, + next: UserExistsMiddleware( + next: RoleCheckMiddleware(), + ), + ), + ); + + for (var value in [ + ['admin@example.com', 'admin_pass'], + ['user@example.com', 'user_pass'], + ['not exist user', 'pass'], + ['user@example.com', 'fail password'], + ['user@example.com', 'user_pass'], // limited request + ]) { + userLogin(server, email: value[0], password: value[1]); + } +} + +void userLogin( + Server server, { + required String email, + required String password, +}) { + print('Start middleware:'); + final success = server.logIn(email, password); + + if (success) { + print('Authorization successful'); + } else { + print('User is not authorized'); + } + + print(''); +} diff --git a/patterns/chain_of_responsibility/server_middleware/middleware/role_check_middleware.dart b/patterns/chain_of_responsibility/server_middleware/middleware/role_check_middleware.dart new file mode 100644 index 0000000..1f764b6 --- /dev/null +++ b/patterns/chain_of_responsibility/server_middleware/middleware/role_check_middleware.dart @@ -0,0 +1,16 @@ +import '../server/server.dart'; + +class RoleCheckMiddleware extends Middleware { + RoleCheckMiddleware({Middleware? next}) : super(next: next); + + @override + bool check(String email, String password) { + if (email == 'admin@example.com') { + print('RoleCheckMiddleware: role defined as "admin"'); + return true; + } + + print('RoleCheckMiddleware: role defined as "user"'); + return checkNext(email, password); + } +} diff --git a/patterns/chain_of_responsibility/server_middleware/middleware/throttling_middleware.dart b/patterns/chain_of_responsibility/server_middleware/middleware/throttling_middleware.dart new file mode 100644 index 0000000..c4cc2d9 --- /dev/null +++ b/patterns/chain_of_responsibility/server_middleware/middleware/throttling_middleware.dart @@ -0,0 +1,49 @@ +import '../server/server.dart'; + +/// EN: ConcreteHandler. Checks whether there are too many failed login requests. +/// +/// RU: Конкретный элемент цепи обрабатывает запрос по-своему. +class ThrottlingMiddleware extends Middleware { + final int requestPerMinute; + var _request = 0; + var _startTime = DateTime.now(); + + ThrottlingMiddleware({ + required this.requestPerMinute, + Middleware? next, + }) : super(next: next); + + /// EN: Please, not that checkNext() call can be inserted both in the + /// beginning of this method and in the end. + /// + /// This gives much more flexibility than a simple loop over all middleware + /// objects. For instance, an element of a chain can change the order of + /// checks by running its check after all other checks. + /// + /// RU: Обратите внимание, вызов checkNext() можно вставить как в начале + /// этого метода, так и в середине или в конце. + /// + /// Это даёт еще один уровень гибкости по сравнению с проверками в цикле. + /// Например, элемент цепи может пропустить все остальные проверки вперёд и + /// запустить свою проверку в конце. + @override + bool check(String email, String password) { + const waiting = Duration(minutes: 2); + final hasExpired = DateTime.now().isAfter(_startTime.add(waiting)); + + if (hasExpired) { + _request = 0; + _startTime = DateTime.now(); + } + + _request++; + + if (_request > requestPerMinute) { + print('ThrottlingMiddleware: Request limit exceeded! ' + 'Requests=$_request, limit=$requestPerMinute per minute'); + return false; + } + + return checkNext(email, password); + } +} diff --git a/patterns/chain_of_responsibility/server_middleware/middleware/user_exists_middleware.dart b/patterns/chain_of_responsibility/server_middleware/middleware/user_exists_middleware.dart new file mode 100644 index 0000000..3a0549c --- /dev/null +++ b/patterns/chain_of_responsibility/server_middleware/middleware/user_exists_middleware.dart @@ -0,0 +1,26 @@ +import '../server/server.dart'; + +class UserExistsMiddleware extends Middleware { + UserExistsMiddleware({Middleware? next}) : super(next: next); + + @override + bool check(String email, String password) { + if (server == null) { + return false; + } + + if (!server!.hasEmail(email)) { + print('UserExistsMiddleware: this email($email) is not registered!'); + return false; + } + + if (!server!.isValidPassword(email, password)) { + print('UserExistsMiddleware: wrong password!'); + return false; + } + + print('UserExistsMiddleware: user has been validated'); + + return checkNext(email, password); + } +} diff --git a/patterns/chain_of_responsibility/server_middleware/server/middleware.dart b/patterns/chain_of_responsibility/server_middleware/server/middleware.dart new file mode 100644 index 0000000..c9ccc0c --- /dev/null +++ b/patterns/chain_of_responsibility/server_middleware/server/middleware.dart @@ -0,0 +1,19 @@ +part of server; + +abstract class Middleware { + final Middleware? next; + + Middleware({ + this.next, + }); + + bool check(String email, String password); + + bool checkNext(String email, String password) { + return next?.check(email, password) ?? true; + } + + Server? _server; + + Server? get server => _server; +} diff --git a/patterns/chain_of_responsibility/server_middleware/server/server.dart b/patterns/chain_of_responsibility/server_middleware/server/server.dart new file mode 100644 index 0000000..ec79a6f --- /dev/null +++ b/patterns/chain_of_responsibility/server_middleware/server/server.dart @@ -0,0 +1,34 @@ +library server; + +part 'middleware.dart'; + +class Server { + final Map users; + final Middleware middleware; + + Server({ + required this.users, + required this.middleware, + }) { + _applyServerToAllMiddleware(); + } + + bool logIn(String email, String password) { + print('Sever: login User("$email", "$password")'); + return middleware.check(email, password); + } + + void _applyServerToAllMiddleware() { + Middleware? middleware = this.middleware; + while (middleware != null) { + middleware._server = this; + middleware = middleware.next; + } + } + + bool hasEmail(String email) => users.containsKey(email); + + bool isValidPassword(String email, String password) { + return users[email] == password; + } +} From 05762b460a3e441bb31abadbb8f2c08787312954 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 14 Jan 2022 11:03:34 +0200 Subject: [PATCH 090/479] Add "Chain of Responsibility" pattern --- .../server_middleware/server/server.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/patterns/chain_of_responsibility/server_middleware/server/server.dart b/patterns/chain_of_responsibility/server_middleware/server/server.dart index ec79a6f..a2a534c 100644 --- a/patterns/chain_of_responsibility/server_middleware/server/server.dart +++ b/patterns/chain_of_responsibility/server_middleware/server/server.dart @@ -3,13 +3,13 @@ library server; part 'middleware.dart'; class Server { - final Map users; + final Map _users; final Middleware middleware; Server({ - required this.users, + required Map users, required this.middleware, - }) { + }) : _users = Map.of(users) { _applyServerToAllMiddleware(); } @@ -26,9 +26,9 @@ class Server { } } - bool hasEmail(String email) => users.containsKey(email); + bool hasEmail(String email) => _users.containsKey(email); bool isValidPassword(String email, String password) { - return users[email] == password; + return _users[email] == password; } } From fd332bc556b1498d689a4e40649b607b44dd1d84 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 14 Jan 2022 11:05:33 +0200 Subject: [PATCH 091/479] Add README --- .../server_middleware/README.md | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 patterns/chain_of_responsibility/server_middleware/README.md diff --git a/patterns/chain_of_responsibility/server_middleware/README.md b/patterns/chain_of_responsibility/server_middleware/README.md new file mode 100644 index 0000000..7563881 --- /dev/null +++ b/patterns/chain_of_responsibility/server_middleware/README.md @@ -0,0 +1,77 @@ +# Chain of Responsibility pattern +Chain of Responsibility is a behavioral design pattern that lets you pass requests along a chain of handlers. + +## Server middleware example +![image](https://user-images.githubusercontent.com/8049534/149480179-ba06640c-0858-4ff9-8957-f2c4aa22ccc4.png) + +## Problem description: +https://refactoring.guru/design-patterns/chain-of-responsibility?#problem + +## 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: +![image](https://user-images.githubusercontent.com/8049534/149488654-7ff5f659-4086-4d1a-ae44-326c71fb880a.png) + +## Client code: +```dart +void main() { + final server = Server( + users: { + 'admin@example.com': 'admin_pass', + 'user@example.com': 'user_pass', + }, + // EN: All checks are linked. Client can build various chains using the + // same components. + // + // RU: Проверки связаны в одну цепь. Клиент может строить различные + // цепи, используя одни и те же компоненты. + middleware: ThrottlingMiddleware( + requestPerMinute: 4, + next: UserExistsMiddleware( + next: RoleCheckMiddleware(), + ), + ), + ); + + for (var value in [ + ['admin@example.com', 'admin_pass'], + ['user@example.com', 'user_pass'], + ['not exist user', 'pass'], + ['user@example.com', 'fail password'], + ['user@example.com', 'user_pass'], // limited request + ]) { + userLogin(server, email: value[0], password: value[1]); + } +} +``` + +**Output:** +``` +Start middleware: +Sever: login User("admin@example.com", "admin_pass") +UserExistsMiddleware: user has been validated +RoleCheckMiddleware: role defined as "admin" +Authorization successful + +Start middleware: +Sever: login User("user@example.com", "user_pass") +UserExistsMiddleware: user has been validated +RoleCheckMiddleware: role defined as "user" +Authorization successful + +Start middleware: +Sever: login User("not exist user", "pass") +UserExistsMiddleware: this email(not exist user) is not registered! +User is not authorized + +Start middleware: +Sever: login User("user@example.com", "fail password") +UserExistsMiddleware: wrong password! +User is not authorized + +Start middleware: +Sever: login User("user@example.com", "user_pass") +ThrottlingMiddleware: Request limit exceeded! Requests=5, limit=4 per minute +User is not authorized +``` From 5e6d37a2238ca6feb6b5bb12c50cda992f92a408 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 14 Jan 2022 11:07:06 +0200 Subject: [PATCH 092/479] Add README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 68dc834..948730f 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ It contains **Dart** examples for all classic GoF design patterns. - [x] Prototype - [ ] Singleton - [ ] **Behavioral** - - [ ] Chain of Responsibility + - [x] Chain of Responsibility - [ ] Command - [ ] Interpreter - [ ] Iterator From 278bd12907b47c5197075ae871f59647ccb58fb2 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 14 Jan 2022 11:07:35 +0200 Subject: [PATCH 093/479] Bump version 0.11.0 --- CHANGELOG.md | 5 ++++- pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e79896..122061c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ -# 0.10.0 +## 0.11.0 +- Add chain of responsibility template from official book, rewritten from Java example + +## 0.10.0 - Add composite template from official book, rewritten from Java example ## 0.9.0 diff --git a/pubspec.yaml b/pubspec.yaml index e754617..ea5be06 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.10.0 +version: 0.11.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 9e3bcb37ef888ddba2e9fe57bd986b02e2ac2a38 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 11:21:51 +0200 Subject: [PATCH 094/479] Add Command pattern. Editor application. --- patterns/command/application/application.dart | 82 +++++++++++++++++++ .../command/application/command_history.dart | 11 +++ patterns/command/application/editor.dart | 73 +++++++++++++++++ patterns/command/commands/command.dart | 13 +++ patterns/command/commands/copy_command.dart | 35 ++++++++ patterns/command/commands/cut_command.dart | 42 ++++++++++ .../commands/input_keyboard_command.dart | 39 +++++++++ patterns/command/commands/move_command.dart | 34 ++++++++ patterns/command/commands/past_command.dart | 47 +++++++++++ patterns/command/commands/select_command.dart | 44 ++++++++++ patterns/command/main.dart | 32 ++++++++ 11 files changed, 452 insertions(+) create mode 100644 patterns/command/application/application.dart create mode 100644 patterns/command/application/command_history.dart create mode 100644 patterns/command/application/editor.dart create mode 100644 patterns/command/commands/command.dart create mode 100644 patterns/command/commands/copy_command.dart create mode 100644 patterns/command/commands/cut_command.dart create mode 100644 patterns/command/commands/input_keyboard_command.dart create mode 100644 patterns/command/commands/move_command.dart create mode 100644 patterns/command/commands/past_command.dart create mode 100644 patterns/command/commands/select_command.dart create mode 100644 patterns/command/main.dart diff --git a/patterns/command/application/application.dart b/patterns/command/application/application.dart new file mode 100644 index 0000000..5a7579c --- /dev/null +++ b/patterns/command/application/application.dart @@ -0,0 +1,82 @@ +import '../commands/command.dart'; +import '../commands/copy_command.dart'; +import '../commands/input_keyboard_command.dart'; +import '../commands/move_command.dart'; +import '../commands/past_command.dart'; +import '../commands/select_command.dart'; +import 'command_history.dart'; +import 'editor.dart'; + +typedef ExecuteCommandListener = Function( + Command command, bool isUndo, String textBefore, String textAfter); + +class Application { + final editor = Editor(); + final history = CommandHistory(); + + String clipboard = ''; + + void keyboardInput(String text) { + executeAndPushHistoryAnd( + InputCommand(this, text), + ); + } + + void textCursorMove({required int position}) { + executeAndPushHistoryAnd( + MoveCommand(this, position), + ); + } + + void selectText({required int start, required int end}) { + executeAndPushHistoryAnd( + SelectCommand(this, start, end), + ); + } + + void ctrlC() { + executeAndPushHistoryAnd( + CopyCommand(this), + ); + } + + void ctrlX() { + executeAndPushHistoryAnd( + CutCommand(this), + ); + } + + void ctrlV() { + executeAndPushHistoryAnd( + PastCommand(this), + ); + } + + void undo() { + if (history.isNotEmpty) { + final command = history.pop(); + final textBefore = editor.text; + command.undo(); + final textAfter = editor.text; + _executeListener(command, true, textBefore, textAfter); + } + } + + void executeAndPushHistoryAnd(Command command) { + final textBefore = editor.text; + command.execute(); + final textAfter = editor.text; + _executeListener(command, false, textBefore, textAfter); + + if (command.isSaveHistory) { + history.push(command); + } + } + + // ignore: prefer_function_declarations_over_variables + ExecuteCommandListener _executeListener = (_, __, ___, ____) {}; + + void addListenerExecuteCommand(ExecuteCommandListener listener) { + _executeListener = listener; + } +} diff --git a/patterns/command/application/command_history.dart b/patterns/command/application/command_history.dart new file mode 100644 index 0000000..6541985 --- /dev/null +++ b/patterns/command/application/command_history.dart @@ -0,0 +1,11 @@ +import '../commands/command.dart'; + +class CommandHistory { + final _stack = []; + + bool get isNotEmpty => _stack.isNotEmpty; + + void push(Command command) => _stack.add(command); + + Command pop() => _stack.removeLast(); +} diff --git a/patterns/command/application/editor.dart b/patterns/command/application/editor.dart new file mode 100644 index 0000000..b47d015 --- /dev/null +++ b/patterns/command/application/editor.dart @@ -0,0 +1,73 @@ +class Editor { + var _text = ''; + + String get text { + if (selectedText.isNotEmpty) { + final selectedText = _text.substring( + _startSelection!, + _textCursorPosition, + ); + return _text.replaceRange( + _startSelection!, + _textCursorPosition, + '[$selectedText]', + ); + } + + return _text.replaceRange(_textCursorPosition, _textCursorPosition, '|'); + } + + int _textCursorPosition = 0; + + int? _startSelection; + + void inputText(String addText) { + if (_startSelection == null) { + _text = _text.replaceRange( + _textCursorPosition, + _textCursorPosition, + addText, + ); + _textCursorPosition += addText.length; + return; + } + _text = _text.replaceRange(_startSelection!, _textCursorPosition, addText); + textCursorPosition = _startSelection! + addText.length; + } + + void selectText(int start, int? end) { + end = end ?? _text.length; + assert(start < end, 'start: $start, end: $end'); + _startSelection = start; + _textCursorPosition = end; + } + + void removeSelected() { + if (_startSelection == null) { + return; + } + + _text = _text.replaceRange(_startSelection!, _textCursorPosition, ''); + textCursorPosition = _startSelection!; + } + + set textCursorPosition(int newPosition) { + _startSelection = null; + _textCursorPosition = newPosition; + } + + int get textCursorPosition => _textCursorPosition; + + int? get startSelection => _startSelection; + + String get selectedText { + if (_textCursorPosition > _text.length) { + return ''; + } + + return _text.substring( + _startSelection ?? _textCursorPosition, + _textCursorPosition, + ); + } +} diff --git a/patterns/command/commands/command.dart b/patterns/command/commands/command.dart new file mode 100644 index 0000000..8894441 --- /dev/null +++ b/patterns/command/commands/command.dart @@ -0,0 +1,13 @@ +import '../application/application.dart'; + +abstract class Command { + final Application app; + + Command(this.app); + + bool get isSaveHistory; + + void execute(); + + void undo(); +} diff --git a/patterns/command/commands/copy_command.dart b/patterns/command/commands/copy_command.dart new file mode 100644 index 0000000..ada5781 --- /dev/null +++ b/patterns/command/commands/copy_command.dart @@ -0,0 +1,35 @@ +library copy_past; + +import '../application/application.dart'; +import 'command.dart'; + +part 'cut_command.dart'; + +class CopyCommand extends Command { + String _copyText = ''; + + CopyCommand(Application app) : super(app); + + @override + bool get isSaveHistory => false; + + @override + void execute() { + _copyText = app.editor.selectedText; + + if (_copyText.isEmpty) { + return; + } + + app.clipboard = _copyText; + } + + @override + void undo() {} + + @override + String toString() { + return 'Copy( ' + 'text: "$_copyText" )'; + } +} diff --git a/patterns/command/commands/cut_command.dart b/patterns/command/commands/cut_command.dart new file mode 100644 index 0000000..5893323 --- /dev/null +++ b/patterns/command/commands/cut_command.dart @@ -0,0 +1,42 @@ +part of copy_past; + +class CutCommand extends CopyCommand { + int? _textCursorPosition; + + CutCommand(Application app) : super(app); + + @override + bool get isSaveHistory => _copyText.isNotEmpty; + + @override + void execute() { + super.execute(); + + if (_copyText.isEmpty) { + return; + } + + app.editor.removeSelected(); + _textCursorPosition = app.editor.textCursorPosition; + + } + + @override + void undo() { + if (_copyText.isEmpty) { + return; + } + + app.editor + ..textCursorPosition = _textCursorPosition! + ..inputText(_copyText); + } + + @override + String toString() { + return 'Cut( ' + 'cursorPosition: $_textCursorPosition, ' + 'cutText: "$_copyText" )'; +; + } +} diff --git a/patterns/command/commands/input_keyboard_command.dart b/patterns/command/commands/input_keyboard_command.dart new file mode 100644 index 0000000..59d52f3 --- /dev/null +++ b/patterns/command/commands/input_keyboard_command.dart @@ -0,0 +1,39 @@ +import '../application/application.dart'; +import 'command.dart'; + +class InputCommand extends Command { + final String _addText; + int? _startPosition; + String _prevSelectText = ''; + + InputCommand(Application app, this._addText) : super(app); + + @override + bool get isSaveHistory => _startPosition != null || _addText.isNotEmpty; + + @override + void execute() { + _prevSelectText = app.editor.selectedText; + _startPosition = app.editor.startSelection ?? app.editor.textCursorPosition; + app.editor.inputText(_addText); + } + + @override + void undo() { + if (_startPosition == null) { + return; + } + + app.editor + ..selectText(_startPosition!, _startPosition! + _addText.length) + ..inputText(_prevSelectText); + } + + @override + String toString() { + return 'Input( ' + 'cursorPosition: $_startPosition, ' + 'addText: "$_addText" )'; + + } +} diff --git a/patterns/command/commands/move_command.dart b/patterns/command/commands/move_command.dart new file mode 100644 index 0000000..26ddce8 --- /dev/null +++ b/patterns/command/commands/move_command.dart @@ -0,0 +1,34 @@ +import '../application/application.dart'; +import 'command.dart'; + +class MoveCommand extends Command { + final int _positionTo; + int? _positionFrom; + + MoveCommand(Application app, this._positionTo) : super(app); + + @override + bool get isSaveHistory => false; + + @override + void execute() { + _positionFrom = app.editor.textCursorPosition; + app.editor.textCursorPosition = _positionTo; + } + + @override + void undo() { + if (_positionFrom == null) { + return; + } + + app.editor.textCursorPosition = _positionFrom!; + } + + @override + String toString() { + return 'Move( ' + 'from: $_positionFrom, ' + 'to: $_positionTo )'; + } +} diff --git a/patterns/command/commands/past_command.dart b/patterns/command/commands/past_command.dart new file mode 100644 index 0000000..95f0cb1 --- /dev/null +++ b/patterns/command/commands/past_command.dart @@ -0,0 +1,47 @@ +import '../application/application.dart'; +import 'command.dart'; + +class PastCommand extends Command { + String _text = ''; + String _selectText = ''; + int? _textCursorPosition; + + PastCommand(Application app) : super(app); + + @override + bool get isSaveHistory => _text.isNotEmpty; + + @override + void execute() { + if (app.clipboard.isEmpty) { + return; + } + + _selectText = app.editor.selectedText; + _text = app.clipboard; + app.editor.inputText(_text); + _textCursorPosition = app.editor.textCursorPosition; + } + + @override + void undo() { + if(_text.isEmpty) { + return; + } + + app.editor + ..selectText( + _textCursorPosition! - _text.length, + _textCursorPosition!, + ) + ..inputText(_selectText); + } + + @override + String toString() { + return 'Past( ' + 'cursorPosition: $_textCursorPosition, ' + 'text: "$_text", ' + 'pevRestore: "$_selectText" )'; + } +} diff --git a/patterns/command/commands/select_command.dart b/patterns/command/commands/select_command.dart new file mode 100644 index 0000000..a9df420 --- /dev/null +++ b/patterns/command/commands/select_command.dart @@ -0,0 +1,44 @@ +import '../application/application.dart'; +import 'command.dart'; + +class SelectCommand extends Command { + final int _start; + final int _end; + + int? _oldStart; + int? _oldEnd; + + SelectCommand(Application app, this._start, this._end) : super(app); + + @override + bool get isSaveHistory => false; + + @override + void execute() { + _oldStart = app.editor.startSelection; + _oldEnd = app.editor.textCursorPosition; + app.editor.selectText(_start, _end); + } + + @override + void undo() { + if (_oldEnd == null) { + return; + } + + final noSelection = _oldStart == null; + + if (noSelection) { + app.editor.textCursorPosition = _oldEnd!; + } else { + app.editor.selectText(_oldStart!, _oldEnd); + } + } + + @override + String toString() { + return 'Select( ' + 'start: $_start, ' + 'end: $_end )'; + } +} diff --git a/patterns/command/main.dart b/patterns/command/main.dart new file mode 100644 index 0000000..29667e5 --- /dev/null +++ b/patterns/command/main.dart @@ -0,0 +1,32 @@ +import 'application/application.dart'; +import 'commands/command.dart'; + +void main() { + final app = Application(); + app.addListenerExecuteCommand(log); + app + ..keyboardInput('Hello world') + ..textCursorMove(position: 6) + ..keyboardInput('Refactring GuBu ') + ..selectText(start: 17, end: 19) + ..ctrlX() + ..selectText(start: 17, end: 19) + ..keyboardInput('Guru') + ..selectText(start: 4, end: 5) + ..ctrlC() + ..textCursorMove(position: 12) + ..ctrlV() + ..textCursorMove(position: 28); + + print('\nUndo steps\n'); + + while (app.history.isNotEmpty) { + app.undo(); + } +} + +void log(Command command, bool isUndo, String textBefore, String textAfter) { + final addOrUndo = isUndo ? 'Undo_' : '[➕] '; + final description = '$addOrUndo$command'; + print(description.padRight(72, '_') + '"$textAfter"'); +} From ee23e7ed97f7575cdeb16739f9b70720e29868db Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 11:26:08 +0200 Subject: [PATCH 095/479] Remove "textBefore" from command listener. --- patterns/command/application/application.dart | 13 +++++++------ patterns/command/commands/cut_command.dart | 2 -- patterns/command/main.dart | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/patterns/command/application/application.dart b/patterns/command/application/application.dart index 5a7579c..bc84936 100644 --- a/patterns/command/application/application.dart +++ b/patterns/command/application/application.dart @@ -8,7 +8,10 @@ import 'command_history.dart'; import 'editor.dart'; typedef ExecuteCommandListener = Function( - Command command, bool isUndo, String textBefore, String textAfter); + Command command, + bool isUndo, + String textAfter, +); class Application { final editor = Editor(); @@ -55,18 +58,16 @@ class Application { void undo() { if (history.isNotEmpty) { final command = history.pop(); - final textBefore = editor.text; command.undo(); final textAfter = editor.text; - _executeListener(command, true, textBefore, textAfter); + _executeListener(command, true, textAfter); } } void executeAndPushHistoryAnd(Command command) { - final textBefore = editor.text; command.execute(); final textAfter = editor.text; - _executeListener(command, false, textBefore, textAfter); + _executeListener(command, false, textAfter); if (command.isSaveHistory) { history.push(command); @@ -74,7 +75,7 @@ class Application { } // ignore: prefer_function_declarations_over_variables - ExecuteCommandListener _executeListener = (_, __, ___, ____) {}; + ExecuteCommandListener _executeListener = (_, __, ___) {}; void addListenerExecuteCommand(ExecuteCommandListener listener) { _executeListener = listener; diff --git a/patterns/command/commands/cut_command.dart b/patterns/command/commands/cut_command.dart index 5893323..7c94078 100644 --- a/patterns/command/commands/cut_command.dart +++ b/patterns/command/commands/cut_command.dart @@ -18,7 +18,6 @@ class CutCommand extends CopyCommand { app.editor.removeSelected(); _textCursorPosition = app.editor.textCursorPosition; - } @override @@ -37,6 +36,5 @@ class CutCommand extends CopyCommand { return 'Cut( ' 'cursorPosition: $_textCursorPosition, ' 'cutText: "$_copyText" )'; -; } } diff --git a/patterns/command/main.dart b/patterns/command/main.dart index 29667e5..f917aa3 100644 --- a/patterns/command/main.dart +++ b/patterns/command/main.dart @@ -25,7 +25,7 @@ void main() { } } -void log(Command command, bool isUndo, String textBefore, String textAfter) { +void log(Command command, bool isUndo, String textAfter) { final addOrUndo = isUndo ? 'Undo_' : '[➕] '; final description = '$addOrUndo$command'; print(description.padRight(72, '_') + '"$textAfter"'); From 895b370451269033dd0c6bb755baab8483655c7a Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 11:36:15 +0200 Subject: [PATCH 096/479] Remove history access. --- patterns/command/application/application.dart | 24 ++++++++++--------- patterns/command/main.dart | 2 +- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/patterns/command/application/application.dart b/patterns/command/application/application.dart index bc84936..237b13b 100644 --- a/patterns/command/application/application.dart +++ b/patterns/command/application/application.dart @@ -15,62 +15,64 @@ typedef ExecuteCommandListener = Function( class Application { final editor = Editor(); - final history = CommandHistory(); + final _history = CommandHistory(); String clipboard = ''; void keyboardInput(String text) { - executeAndPushHistoryAnd( + executeAndPushHistory( InputCommand(this, text), ); } void textCursorMove({required int position}) { - executeAndPushHistoryAnd( + executeAndPushHistory( MoveCommand(this, position), ); } void selectText({required int start, required int end}) { - executeAndPushHistoryAnd( + executeAndPushHistory( SelectCommand(this, start, end), ); } void ctrlC() { - executeAndPushHistoryAnd( + executeAndPushHistory( CopyCommand(this), ); } void ctrlX() { - executeAndPushHistoryAnd( + executeAndPushHistory( CutCommand(this), ); } void ctrlV() { - executeAndPushHistoryAnd( + executeAndPushHistory( PastCommand(this), ); } + bool get isHistoryNotEmpty => _history.isNotEmpty; + void undo() { - if (history.isNotEmpty) { - final command = history.pop(); + if (_history.isNotEmpty) { + final command = _history.pop(); command.undo(); final textAfter = editor.text; _executeListener(command, true, textAfter); } } - void executeAndPushHistoryAnd(Command command) { + void executeAndPushHistory(Command command) { command.execute(); final textAfter = editor.text; _executeListener(command, false, textAfter); if (command.isSaveHistory) { - history.push(command); + _history.push(command); } } diff --git a/patterns/command/main.dart b/patterns/command/main.dart index f917aa3..8678de6 100644 --- a/patterns/command/main.dart +++ b/patterns/command/main.dart @@ -20,7 +20,7 @@ void main() { print('\nUndo steps\n'); - while (app.history.isNotEmpty) { + while (app.isHistoryNotEmpty) { app.undo(); } } From e3d854aab26bf923eeec8923d1a0862e033ee57c Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 11:37:38 +0200 Subject: [PATCH 097/479] Rename "input_keyboard_command.dart" to "input_command.dart". --- patterns/command/application/application.dart | 2 +- .../{input_keyboard_command.dart => input_command.dart} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename patterns/command/commands/{input_keyboard_command.dart => input_command.dart} (100%) diff --git a/patterns/command/application/application.dart b/patterns/command/application/application.dart index 237b13b..6e43e0a 100644 --- a/patterns/command/application/application.dart +++ b/patterns/command/application/application.dart @@ -1,6 +1,6 @@ import '../commands/command.dart'; import '../commands/copy_command.dart'; -import '../commands/input_keyboard_command.dart'; +import '../commands/input_command.dart'; import '../commands/move_command.dart'; import '../commands/past_command.dart'; import '../commands/select_command.dart'; diff --git a/patterns/command/commands/input_keyboard_command.dart b/patterns/command/commands/input_command.dart similarity index 100% rename from patterns/command/commands/input_keyboard_command.dart rename to patterns/command/commands/input_command.dart From 512ab2b3545ac847016df2dcec432a0b667b6996 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 11:42:29 +0200 Subject: [PATCH 098/479] Fix "InputCommand". Remove startPosition checker. --- patterns/command/commands/input_command.dart | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/patterns/command/commands/input_command.dart b/patterns/command/commands/input_command.dart index 59d52f3..bb9d040 100644 --- a/patterns/command/commands/input_command.dart +++ b/patterns/command/commands/input_command.dart @@ -9,7 +9,7 @@ class InputCommand extends Command { InputCommand(Application app, this._addText) : super(app); @override - bool get isSaveHistory => _startPosition != null || _addText.isNotEmpty; + bool get isSaveHistory => _addText.isNotEmpty; @override void execute() { @@ -20,10 +20,6 @@ class InputCommand extends Command { @override void undo() { - if (_startPosition == null) { - return; - } - app.editor ..selectText(_startPosition!, _startPosition! + _addText.length) ..inputText(_prevSelectText); @@ -34,6 +30,5 @@ class InputCommand extends Command { return 'Input( ' 'cursorPosition: $_startPosition, ' 'addText: "$_addText" )'; - } } From e0fcaf0a944722a7bb65cc0f907f9e52e212666e Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 12:06:09 +0200 Subject: [PATCH 099/479] Remove textAfter variable from Application.undo(). --- patterns/command/application/application.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/patterns/command/application/application.dart b/patterns/command/application/application.dart index 6e43e0a..33a70d0 100644 --- a/patterns/command/application/application.dart +++ b/patterns/command/application/application.dart @@ -61,8 +61,7 @@ class Application { if (_history.isNotEmpty) { final command = _history.pop(); command.undo(); - final textAfter = editor.text; - _executeListener(command, true, textAfter); + _executeListener(command, true, editor.text); } } From 6fd3022d9ae60bb92ff6905bfd68434c882a3662 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 12:07:51 +0200 Subject: [PATCH 100/479] Make "executeAndPushHistory()" private. --- patterns/command/application/application.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/patterns/command/application/application.dart b/patterns/command/application/application.dart index 33a70d0..51cb239 100644 --- a/patterns/command/application/application.dart +++ b/patterns/command/application/application.dart @@ -20,37 +20,37 @@ class Application { String clipboard = ''; void keyboardInput(String text) { - executeAndPushHistory( + _executeAndPushHistory( InputCommand(this, text), ); } void textCursorMove({required int position}) { - executeAndPushHistory( + _executeAndPushHistory( MoveCommand(this, position), ); } void selectText({required int start, required int end}) { - executeAndPushHistory( + _executeAndPushHistory( SelectCommand(this, start, end), ); } void ctrlC() { - executeAndPushHistory( + _executeAndPushHistory( CopyCommand(this), ); } void ctrlX() { - executeAndPushHistory( + _executeAndPushHistory( CutCommand(this), ); } void ctrlV() { - executeAndPushHistory( + _executeAndPushHistory( PastCommand(this), ); } @@ -65,7 +65,7 @@ class Application { } } - void executeAndPushHistory(Command command) { + void _executeAndPushHistory(Command command) { command.execute(); final textAfter = editor.text; _executeListener(command, false, textAfter); From 8e9cec69324a3687ba82d734f3ea4fc5c67a597e Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 12:10:04 +0200 Subject: [PATCH 101/479] Refactor "CopyCommand". --- patterns/command/commands/copy_command.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/patterns/command/commands/copy_command.dart b/patterns/command/commands/copy_command.dart index ada5781..c530e1f 100644 --- a/patterns/command/commands/copy_command.dart +++ b/patterns/command/commands/copy_command.dart @@ -15,12 +15,11 @@ class CopyCommand extends Command { @override void execute() { - _copyText = app.editor.selectedText; - - if (_copyText.isEmpty) { + if (app.editor.selectedText.isEmpty) { return; } + _copyText = app.editor.selectedText; app.clipboard = _copyText; } From 9a9a8ab7101f75260c3dc63526ef65a21cd6d025 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 12:36:22 +0200 Subject: [PATCH 102/479] Refactor "Editor.text" getter. --- patterns/command/application/editor.dart | 84 +++++++++++++----------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/patterns/command/application/editor.dart b/patterns/command/application/editor.dart index b47d015..d84bd15 100644 --- a/patterns/command/application/editor.dart +++ b/patterns/command/application/editor.dart @@ -2,37 +2,40 @@ class Editor { var _text = ''; String get text { - if (selectedText.isNotEmpty) { - final selectedText = _text.substring( - _startSelection!, - _textCursorPosition, - ); - return _text.replaceRange( - _startSelection!, - _textCursorPosition, - '[$selectedText]', - ); - } - - return _text.replaceRange(_textCursorPosition, _textCursorPosition, '|'); + return isTextSelected + ? _text.replaceRange( + _startSelection!, + _textCursorPosition, + '[$selectedText]', + ) + : _text.replaceRange( + _textCursorPosition, + _textCursorPosition, + '|', + ); } - int _textCursorPosition = 0; - - int? _startSelection; - void inputText(String addText) { if (_startSelection == null) { - _text = _text.replaceRange( - _textCursorPosition, - _textCursorPosition, - addText, - ); - _textCursorPosition += addText.length; - return; + _insertText(addText); + } else { + _replaceSelection(addText); } - _text = _text.replaceRange(_startSelection!, _textCursorPosition, addText); - textCursorPosition = _startSelection! + addText.length; + } + + void _replaceSelection(String replaceText) { + _text = _text.replaceRange( + _startSelection!, + _textCursorPosition, + replaceText, + ); + textCursorPosition = _startSelection! + replaceText.length; + } + + void _insertText(String insertText) { + _text = _text.replaceRange( + _textCursorPosition, _textCursorPosition, insertText); + _textCursorPosition += insertText.length; } void selectText(int start, int? end) { @@ -43,22 +46,12 @@ class Editor { } void removeSelected() { - if (_startSelection == null) { - return; + if (_startSelection != null) { + _replaceSelection(''); } - - _text = _text.replaceRange(_startSelection!, _textCursorPosition, ''); - textCursorPosition = _startSelection!; } - set textCursorPosition(int newPosition) { - _startSelection = null; - _textCursorPosition = newPosition; - } - - int get textCursorPosition => _textCursorPosition; - - int? get startSelection => _startSelection; + bool get isTextSelected => _startSelection != null; String get selectedText { if (_textCursorPosition > _text.length) { @@ -70,4 +63,17 @@ class Editor { _textCursorPosition, ); } + + int? _startSelection; + + int? get startSelection => _startSelection; + + int _textCursorPosition = 0; + + set textCursorPosition(int newPosition) { + _startSelection = null; + _textCursorPosition = newPosition; + } + + int get textCursorPosition => _textCursorPosition; } From 15c09566e6dc70c82cb041f2fbfef74fd7c78775 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 12:40:47 +0200 Subject: [PATCH 103/479] Add "isTextSelected". --- patterns/command/application/editor.dart | 45 +++++++++++---------- patterns/command/commands/copy_command.dart | 2 +- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/patterns/command/application/editor.dart b/patterns/command/application/editor.dart index d84bd15..421ca6e 100644 --- a/patterns/command/application/editor.dart +++ b/patterns/command/application/editor.dart @@ -16,28 +16,13 @@ class Editor { } void inputText(String addText) { - if (_startSelection == null) { - _insertText(addText); - } else { + if (isTextSelected) { _replaceSelection(addText); + } else { + _insertText(addText); } } - void _replaceSelection(String replaceText) { - _text = _text.replaceRange( - _startSelection!, - _textCursorPosition, - replaceText, - ); - textCursorPosition = _startSelection! + replaceText.length; - } - - void _insertText(String insertText) { - _text = _text.replaceRange( - _textCursorPosition, _textCursorPosition, insertText); - _textCursorPosition += insertText.length; - } - void selectText(int start, int? end) { end = end ?? _text.length; assert(start < end, 'start: $start, end: $end'); @@ -46,13 +31,11 @@ class Editor { } void removeSelected() { - if (_startSelection != null) { + if (isTextSelected) { _replaceSelection(''); } } - bool get isTextSelected => _startSelection != null; - String get selectedText { if (_textCursorPosition > _text.length) { return ''; @@ -68,6 +51,8 @@ class Editor { int? get startSelection => _startSelection; + bool get isTextSelected => _startSelection != null; + int _textCursorPosition = 0; set textCursorPosition(int newPosition) { @@ -76,4 +61,22 @@ class Editor { } int get textCursorPosition => _textCursorPosition; + + void _replaceSelection(String replaceText) { + _text = _text.replaceRange( + _startSelection!, + _textCursorPosition, + replaceText, + ); + textCursorPosition = _startSelection! + replaceText.length; + } + + void _insertText(String insertText) { + _text = _text.replaceRange( + _textCursorPosition, + _textCursorPosition, + insertText, + ); + _textCursorPosition += insertText.length; + } } diff --git a/patterns/command/commands/copy_command.dart b/patterns/command/commands/copy_command.dart index c530e1f..7ebf281 100644 --- a/patterns/command/commands/copy_command.dart +++ b/patterns/command/commands/copy_command.dart @@ -15,7 +15,7 @@ class CopyCommand extends Command { @override void execute() { - if (app.editor.selectedText.isEmpty) { + if (!app.editor.isTextSelected) { return; } From e580cdfcb9d8439014101a432d2dc0332f546bfd Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 12:42:51 +0200 Subject: [PATCH 104/479] Fix "insertText()". Deselect after insert text. --- patterns/command/application/editor.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/command/application/editor.dart b/patterns/command/application/editor.dart index 421ca6e..3b56e30 100644 --- a/patterns/command/application/editor.dart +++ b/patterns/command/application/editor.dart @@ -77,6 +77,6 @@ class Editor { _textCursorPosition, insertText, ); - _textCursorPosition += insertText.length; + textCursorPosition += insertText.length; } } From 670683d2ddbce53018067bd7e07b57fb9d958ec0 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 12:48:09 +0200 Subject: [PATCH 105/479] Rename "Application.undo" to "Application.ctrlZ". --- patterns/command/application/application.dart | 2 +- patterns/command/main.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/patterns/command/application/application.dart b/patterns/command/application/application.dart index 51cb239..f312131 100644 --- a/patterns/command/application/application.dart +++ b/patterns/command/application/application.dart @@ -57,7 +57,7 @@ class Application { bool get isHistoryNotEmpty => _history.isNotEmpty; - void undo() { + void ctrlZ() { if (_history.isNotEmpty) { final command = _history.pop(); command.undo(); diff --git a/patterns/command/main.dart b/patterns/command/main.dart index 8678de6..fbb5fea 100644 --- a/patterns/command/main.dart +++ b/patterns/command/main.dart @@ -21,7 +21,7 @@ void main() { print('\nUndo steps\n'); while (app.isHistoryNotEmpty) { - app.undo(); + app.ctrlZ(); } } From 51104dc43901c21bcc99605f6608516c575d0492 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 12:53:35 +0200 Subject: [PATCH 106/479] Make "ExecuteCommandListener" with named arguments. --- patterns/command/application/application.dart | 24 ++++++++++++------- patterns/command/main.dart | 8 +++++-- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/patterns/command/application/application.dart b/patterns/command/application/application.dart index f312131..6c3fa22 100644 --- a/patterns/command/application/application.dart +++ b/patterns/command/application/application.dart @@ -7,11 +7,11 @@ import '../commands/select_command.dart'; import 'command_history.dart'; import 'editor.dart'; -typedef ExecuteCommandListener = Function( - Command command, - bool isUndo, - String textAfter, -); +typedef ExecuteCommandListener = Function({ + required Command command, + required bool isUndo, + required String editorText, +}); class Application { final editor = Editor(); @@ -61,14 +61,22 @@ class Application { if (_history.isNotEmpty) { final command = _history.pop(); command.undo(); - _executeListener(command, true, editor.text); + _executeListener?.call( + command: command, + isUndo: true, + editorText: editor.text, + ); } } void _executeAndPushHistory(Command command) { command.execute(); final textAfter = editor.text; - _executeListener(command, false, textAfter); + _executeListener?.call( + command: command, + isUndo: false, + editorText: textAfter, + ); if (command.isSaveHistory) { _history.push(command); @@ -76,7 +84,7 @@ class Application { } // ignore: prefer_function_declarations_over_variables - ExecuteCommandListener _executeListener = (_, __, ___) {}; + ExecuteCommandListener? _executeListener; void addListenerExecuteCommand(ExecuteCommandListener listener) { _executeListener = listener; diff --git a/patterns/command/main.dart b/patterns/command/main.dart index fbb5fea..05748d5 100644 --- a/patterns/command/main.dart +++ b/patterns/command/main.dart @@ -25,8 +25,12 @@ void main() { } } -void log(Command command, bool isUndo, String textAfter) { +void log({ + required Command command, + required bool isUndo, + required String editorText, +}) { final addOrUndo = isUndo ? 'Undo_' : '[➕] '; final description = '$addOrUndo$command'; - print(description.padRight(72, '_') + '"$textAfter"'); + print(description.padRight(72, '_') + '"$editorText"'); } From 48a9e77ef7fe830e90e72f49e22d9c8b56ac63bb Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 14:27:44 +0200 Subject: [PATCH 107/479] Add "TextCursor" class. --- patterns/command/application/editor.dart | 50 +++++++++---------- patterns/command/application/text_cursor.dart | 17 +++++++ 2 files changed, 41 insertions(+), 26 deletions(-) create mode 100644 patterns/command/application/text_cursor.dart diff --git a/patterns/command/application/editor.dart b/patterns/command/application/editor.dart index 3b56e30..d7a1556 100644 --- a/patterns/command/application/editor.dart +++ b/patterns/command/application/editor.dart @@ -1,16 +1,18 @@ +import 'text_cursor.dart'; + class Editor { var _text = ''; String get text { return isTextSelected ? _text.replaceRange( - _startSelection!, - _textCursorPosition, + cursor.startSelection, + cursor.endSelection, '[$selectedText]', ) : _text.replaceRange( - _textCursorPosition, - _textCursorPosition, + cursor.position, + cursor.position, '|', ); } @@ -23,11 +25,10 @@ class Editor { } } - void selectText(int start, int? end) { - end = end ?? _text.length; + void selectText(int start, int end) { + assert(end <= _text.length, ' end: $end, textLength: ${_text.length}'); assert(start < end, 'start: $start, end: $end'); - _startSelection = start; - _textCursorPosition = end; + _cursor = TextCursor.fromSelection(start, end); } void removeSelected() { @@ -37,44 +38,41 @@ class Editor { } String get selectedText { - if (_textCursorPosition > _text.length) { - return ''; - } - return _text.substring( - _startSelection ?? _textCursorPosition, - _textCursorPosition, + cursor.startSelection, + cursor.endSelection, ); } - int? _startSelection; + var _cursor = TextCursor.fromPosition(0); - int? get startSelection => _startSelection; + TextCursor get cursor => _cursor; - bool get isTextSelected => _startSelection != null; + int? get startSelection => cursor.startSelection; - int _textCursorPosition = 0; + bool get isTextSelected => cursor.isTextSelected; set textCursorPosition(int newPosition) { - _startSelection = null; - _textCursorPosition = newPosition; + _cursor = TextCursor.fromPosition(newPosition); } - int get textCursorPosition => _textCursorPosition; + int get textCursorPosition => _cursor.position; void _replaceSelection(String replaceText) { _text = _text.replaceRange( - _startSelection!, - _textCursorPosition, + cursor.startSelection, + cursor.endSelection, replaceText, ); - textCursorPosition = _startSelection! + replaceText.length; + _cursor = TextCursor.fromPosition( + cursor.startSelection + replaceText.length, + ); } void _insertText(String insertText) { _text = _text.replaceRange( - _textCursorPosition, - _textCursorPosition, + cursor.position, + cursor.position, insertText, ); textCursorPosition += insertText.length; diff --git a/patterns/command/application/text_cursor.dart b/patterns/command/application/text_cursor.dart new file mode 100644 index 0000000..b6c8389 --- /dev/null +++ b/patterns/command/application/text_cursor.dart @@ -0,0 +1,17 @@ +class TextCursor { + int get position => _position; + + int get startSelection => _startSelection ?? _position; + + int get endSelection => position; + + bool get isTextSelected => _startSelection != null; + + final int _position; + + int? _startSelection; + + TextCursor.fromPosition(this._position); + + TextCursor.fromSelection(this._startSelection, this._position); +} From a15eb8b1ac633a506c228763cca1fe76e473753d Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 14:31:33 +0200 Subject: [PATCH 108/479] Move "Editor.isTextSelected" to "TextCursor.isTextSelected" --- patterns/command/application/editor.dart | 8 +++----- patterns/command/commands/copy_command.dart | 2 +- patterns/command/commands/select_command.dart | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/patterns/command/application/editor.dart b/patterns/command/application/editor.dart index d7a1556..c08f418 100644 --- a/patterns/command/application/editor.dart +++ b/patterns/command/application/editor.dart @@ -4,7 +4,7 @@ class Editor { var _text = ''; String get text { - return isTextSelected + return cursor.isTextSelected ? _text.replaceRange( cursor.startSelection, cursor.endSelection, @@ -18,7 +18,7 @@ class Editor { } void inputText(String addText) { - if (isTextSelected) { + if (cursor.isTextSelected) { _replaceSelection(addText); } else { _insertText(addText); @@ -32,7 +32,7 @@ class Editor { } void removeSelected() { - if (isTextSelected) { + if (cursor.isTextSelected) { _replaceSelection(''); } } @@ -50,8 +50,6 @@ class Editor { int? get startSelection => cursor.startSelection; - bool get isTextSelected => cursor.isTextSelected; - set textCursorPosition(int newPosition) { _cursor = TextCursor.fromPosition(newPosition); } diff --git a/patterns/command/commands/copy_command.dart b/patterns/command/commands/copy_command.dart index 7ebf281..b381254 100644 --- a/patterns/command/commands/copy_command.dart +++ b/patterns/command/commands/copy_command.dart @@ -15,7 +15,7 @@ class CopyCommand extends Command { @override void execute() { - if (!app.editor.isTextSelected) { + if (!app.editor.cursor.isTextSelected) { return; } diff --git a/patterns/command/commands/select_command.dart b/patterns/command/commands/select_command.dart index a9df420..4f9ef33 100644 --- a/patterns/command/commands/select_command.dart +++ b/patterns/command/commands/select_command.dart @@ -31,7 +31,7 @@ class SelectCommand extends Command { if (noSelection) { app.editor.textCursorPosition = _oldEnd!; } else { - app.editor.selectText(_oldStart!, _oldEnd); + app.editor.selectText(_oldStart!, _oldEnd!); } } From f1fc23325c5aabb4d3e4e30b285e53dde67ddadd Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 14:34:20 +0200 Subject: [PATCH 109/479] Delete getter "startSelection" from "Editor". --- patterns/command/application/editor.dart | 2 -- patterns/command/commands/input_command.dart | 2 +- patterns/command/commands/select_command.dart | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/patterns/command/application/editor.dart b/patterns/command/application/editor.dart index c08f418..bd78b8b 100644 --- a/patterns/command/application/editor.dart +++ b/patterns/command/application/editor.dart @@ -48,8 +48,6 @@ class Editor { TextCursor get cursor => _cursor; - int? get startSelection => cursor.startSelection; - set textCursorPosition(int newPosition) { _cursor = TextCursor.fromPosition(newPosition); } diff --git a/patterns/command/commands/input_command.dart b/patterns/command/commands/input_command.dart index bb9d040..e246a60 100644 --- a/patterns/command/commands/input_command.dart +++ b/patterns/command/commands/input_command.dart @@ -14,7 +14,7 @@ class InputCommand extends Command { @override void execute() { _prevSelectText = app.editor.selectedText; - _startPosition = app.editor.startSelection ?? app.editor.textCursorPosition; + _startPosition = app.editor.cursor.startSelection; app.editor.inputText(_addText); } diff --git a/patterns/command/commands/select_command.dart b/patterns/command/commands/select_command.dart index 4f9ef33..b0cd717 100644 --- a/patterns/command/commands/select_command.dart +++ b/patterns/command/commands/select_command.dart @@ -15,7 +15,7 @@ class SelectCommand extends Command { @override void execute() { - _oldStart = app.editor.startSelection; + _oldStart = app.editor.cursor.startSelection; _oldEnd = app.editor.textCursorPosition; app.editor.selectText(_start, _end); } From a4cbf96f90ef43fe1504b1c15f459483c88ea84d Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 14:38:42 +0200 Subject: [PATCH 110/479] Delete getter "textCursorPosition" from "Editor". --- patterns/command/application/editor.dart | 4 +--- patterns/command/commands/cut_command.dart | 2 +- patterns/command/commands/move_command.dart | 2 +- patterns/command/commands/past_command.dart | 4 ++-- patterns/command/commands/select_command.dart | 2 +- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/patterns/command/application/editor.dart b/patterns/command/application/editor.dart index bd78b8b..f63600d 100644 --- a/patterns/command/application/editor.dart +++ b/patterns/command/application/editor.dart @@ -52,8 +52,6 @@ class Editor { _cursor = TextCursor.fromPosition(newPosition); } - int get textCursorPosition => _cursor.position; - void _replaceSelection(String replaceText) { _text = _text.replaceRange( cursor.startSelection, @@ -71,6 +69,6 @@ class Editor { cursor.position, insertText, ); - textCursorPosition += insertText.length; + textCursorPosition = cursor.position + insertText.length; } } diff --git a/patterns/command/commands/cut_command.dart b/patterns/command/commands/cut_command.dart index 7c94078..5da55fb 100644 --- a/patterns/command/commands/cut_command.dart +++ b/patterns/command/commands/cut_command.dart @@ -17,7 +17,7 @@ class CutCommand extends CopyCommand { } app.editor.removeSelected(); - _textCursorPosition = app.editor.textCursorPosition; + _textCursorPosition = app.editor.cursor.position; } @override diff --git a/patterns/command/commands/move_command.dart b/patterns/command/commands/move_command.dart index 26ddce8..9fc6f62 100644 --- a/patterns/command/commands/move_command.dart +++ b/patterns/command/commands/move_command.dart @@ -12,7 +12,7 @@ class MoveCommand extends Command { @override void execute() { - _positionFrom = app.editor.textCursorPosition; + _positionFrom = app.editor.cursor.position; app.editor.textCursorPosition = _positionTo; } diff --git a/patterns/command/commands/past_command.dart b/patterns/command/commands/past_command.dart index 95f0cb1..eef292f 100644 --- a/patterns/command/commands/past_command.dart +++ b/patterns/command/commands/past_command.dart @@ -20,12 +20,12 @@ class PastCommand extends Command { _selectText = app.editor.selectedText; _text = app.clipboard; app.editor.inputText(_text); - _textCursorPosition = app.editor.textCursorPosition; + _textCursorPosition = app.editor.cursor.position; } @override void undo() { - if(_text.isEmpty) { + if (_text.isEmpty) { return; } diff --git a/patterns/command/commands/select_command.dart b/patterns/command/commands/select_command.dart index b0cd717..33a956e 100644 --- a/patterns/command/commands/select_command.dart +++ b/patterns/command/commands/select_command.dart @@ -16,7 +16,7 @@ class SelectCommand extends Command { @override void execute() { _oldStart = app.editor.cursor.startSelection; - _oldEnd = app.editor.textCursorPosition; + _oldEnd = app.editor.cursor.endSelection; app.editor.selectText(_start, _end); } From 3de5de5f69e846a81bbe9f5cfdb945037f3f7edb Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 14:43:51 +0200 Subject: [PATCH 111/479] Rename setter "Editor.textCursorPosition" to "Editor.cursorPosition". --- patterns/command/application/editor.dart | 4 ++-- patterns/command/commands/cut_command.dart | 2 +- patterns/command/commands/move_command.dart | 4 ++-- patterns/command/commands/select_command.dart | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/patterns/command/application/editor.dart b/patterns/command/application/editor.dart index f63600d..749ad1e 100644 --- a/patterns/command/application/editor.dart +++ b/patterns/command/application/editor.dart @@ -48,7 +48,7 @@ class Editor { TextCursor get cursor => _cursor; - set textCursorPosition(int newPosition) { + set cursorPosition(int newPosition) { _cursor = TextCursor.fromPosition(newPosition); } @@ -69,6 +69,6 @@ class Editor { cursor.position, insertText, ); - textCursorPosition = cursor.position + insertText.length; + cursorPosition = cursor.position + insertText.length; } } diff --git a/patterns/command/commands/cut_command.dart b/patterns/command/commands/cut_command.dart index 5da55fb..86e09e0 100644 --- a/patterns/command/commands/cut_command.dart +++ b/patterns/command/commands/cut_command.dart @@ -27,7 +27,7 @@ class CutCommand extends CopyCommand { } app.editor - ..textCursorPosition = _textCursorPosition! + ..cursorPosition = _textCursorPosition! ..inputText(_copyText); } diff --git a/patterns/command/commands/move_command.dart b/patterns/command/commands/move_command.dart index 9fc6f62..e543fd2 100644 --- a/patterns/command/commands/move_command.dart +++ b/patterns/command/commands/move_command.dart @@ -13,7 +13,7 @@ class MoveCommand extends Command { @override void execute() { _positionFrom = app.editor.cursor.position; - app.editor.textCursorPosition = _positionTo; + app.editor.cursorPosition = _positionTo; } @override @@ -22,7 +22,7 @@ class MoveCommand extends Command { return; } - app.editor.textCursorPosition = _positionFrom!; + app.editor.cursorPosition = _positionFrom!; } @override diff --git a/patterns/command/commands/select_command.dart b/patterns/command/commands/select_command.dart index 33a956e..56bc1a2 100644 --- a/patterns/command/commands/select_command.dart +++ b/patterns/command/commands/select_command.dart @@ -29,7 +29,7 @@ class SelectCommand extends Command { final noSelection = _oldStart == null; if (noSelection) { - app.editor.textCursorPosition = _oldEnd!; + app.editor.cursorPosition = _oldEnd!; } else { app.editor.selectText(_oldStart!, _oldEnd!); } From 89a7573a6a39c36668d8551ceed9a69aec429025 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 14:49:32 +0200 Subject: [PATCH 112/479] Refactor setter "CutCommand". --- patterns/command/commands/cut_command.dart | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/patterns/command/commands/cut_command.dart b/patterns/command/commands/cut_command.dart index 86e09e0..d940a4c 100644 --- a/patterns/command/commands/cut_command.dart +++ b/patterns/command/commands/cut_command.dart @@ -1,7 +1,7 @@ part of copy_past; class CutCommand extends CopyCommand { - int? _textCursorPosition; + int? _cursorPosition; CutCommand(Application app) : super(app); @@ -10,14 +10,13 @@ class CutCommand extends CopyCommand { @override void execute() { - super.execute(); - - if (_copyText.isEmpty) { + if (!app.editor.cursor.isTextSelected) { return; } + super.execute(); app.editor.removeSelected(); - _textCursorPosition = app.editor.cursor.position; + _cursorPosition = app.editor.cursor.position; } @override @@ -27,14 +26,14 @@ class CutCommand extends CopyCommand { } app.editor - ..cursorPosition = _textCursorPosition! + ..cursorPosition = _cursorPosition! ..inputText(_copyText); } @override String toString() { return 'Cut( ' - 'cursorPosition: $_textCursorPosition, ' + 'cursorPosition: $_cursorPosition, ' 'cutText: "$_copyText" )'; } } From 1f1a38ee3c6041cb2653d27a97360c268915bb1e Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 14:52:41 +0200 Subject: [PATCH 113/479] Refactor "PastCommand". Rename "_textCursorPosition" to "_cursorPosition" --- patterns/command/commands/past_command.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/patterns/command/commands/past_command.dart b/patterns/command/commands/past_command.dart index eef292f..58aba6f 100644 --- a/patterns/command/commands/past_command.dart +++ b/patterns/command/commands/past_command.dart @@ -4,7 +4,7 @@ import 'command.dart'; class PastCommand extends Command { String _text = ''; String _selectText = ''; - int? _textCursorPosition; + int? _cursorPosition; PastCommand(Application app) : super(app); @@ -20,7 +20,7 @@ class PastCommand extends Command { _selectText = app.editor.selectedText; _text = app.clipboard; app.editor.inputText(_text); - _textCursorPosition = app.editor.cursor.position; + _cursorPosition = app.editor.cursor.position; } @override @@ -31,8 +31,8 @@ class PastCommand extends Command { app.editor ..selectText( - _textCursorPosition! - _text.length, - _textCursorPosition!, + _cursorPosition! - _text.length, + _cursorPosition!, ) ..inputText(_selectText); } @@ -40,7 +40,7 @@ class PastCommand extends Command { @override String toString() { return 'Past( ' - 'cursorPosition: $_textCursorPosition, ' + 'cursorPosition: $_cursorPosition, ' 'text: "$_text", ' 'pevRestore: "$_selectText" )'; } From 4a720a0634bc802b9dbf0b1f90ce8b59c0850562 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 15:03:22 +0200 Subject: [PATCH 114/479] Refactoring "SelectCommand". Replace "oldStart", "oldEnd" with "TextCursor" object. --- patterns/command/commands/select_command.dart | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/patterns/command/commands/select_command.dart b/patterns/command/commands/select_command.dart index 56bc1a2..81bfac2 100644 --- a/patterns/command/commands/select_command.dart +++ b/patterns/command/commands/select_command.dart @@ -1,12 +1,12 @@ import '../application/application.dart'; +import '../application/text_cursor.dart'; import 'command.dart'; class SelectCommand extends Command { final int _start; final int _end; - int? _oldStart; - int? _oldEnd; + TextCursor? _previousCursor; SelectCommand(Application app, this._start, this._end) : super(app); @@ -15,24 +15,20 @@ class SelectCommand extends Command { @override void execute() { - _oldStart = app.editor.cursor.startSelection; - _oldEnd = app.editor.cursor.endSelection; + _previousCursor = app.editor.cursor; app.editor.selectText(_start, _end); } @override void undo() { - if (_oldEnd == null) { + if (_previousCursor == null) { return; } - final noSelection = _oldStart == null; - - if (noSelection) { - app.editor.cursorPosition = _oldEnd!; - } else { - app.editor.selectText(_oldStart!, _oldEnd!); - } + app.editor.selectText( + _previousCursor!.startSelection, + _previousCursor!.endSelection, + ); } @override From 54c5bddbcd3f44caa4777c53ae08c5ac0b86f535 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 15:52:23 +0200 Subject: [PATCH 115/479] Refactor. Rename "Application.textCursorMove" to "Application.moveCursor" --- patterns/command/application/application.dart | 6 +++--- patterns/command/main.dart | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/patterns/command/application/application.dart b/patterns/command/application/application.dart index 6c3fa22..c893029 100644 --- a/patterns/command/application/application.dart +++ b/patterns/command/application/application.dart @@ -25,7 +25,7 @@ class Application { ); } - void textCursorMove({required int position}) { + void moveCursor({required int position}) { _executeAndPushHistory( MoveCommand(this, position), ); @@ -55,8 +55,6 @@ class Application { ); } - bool get isHistoryNotEmpty => _history.isNotEmpty; - void ctrlZ() { if (_history.isNotEmpty) { final command = _history.pop(); @@ -69,6 +67,8 @@ class Application { } } + bool get isHistoryNotEmpty => _history.isNotEmpty; + void _executeAndPushHistory(Command command) { command.execute(); final textAfter = editor.text; diff --git a/patterns/command/main.dart b/patterns/command/main.dart index 05748d5..5862594 100644 --- a/patterns/command/main.dart +++ b/patterns/command/main.dart @@ -6,7 +6,7 @@ void main() { app.addListenerExecuteCommand(log); app ..keyboardInput('Hello world') - ..textCursorMove(position: 6) + ..moveCursor(position: 6) ..keyboardInput('Refactring GuBu ') ..selectText(start: 17, end: 19) ..ctrlX() @@ -14,9 +14,9 @@ void main() { ..keyboardInput('Guru') ..selectText(start: 4, end: 5) ..ctrlC() - ..textCursorMove(position: 12) + ..moveCursor(position: 12) ..ctrlV() - ..textCursorMove(position: 28); + ..moveCursor(position: 28); print('\nUndo steps\n'); From 2e3c571d33bda0cba2f7d2d1a4a8f309a0dd3bde Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 15:54:58 +0200 Subject: [PATCH 116/479] Make "TextCursor._startSelection" is final. --- patterns/command/application/text_cursor.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patterns/command/application/text_cursor.dart b/patterns/command/application/text_cursor.dart index b6c8389..3a756b4 100644 --- a/patterns/command/application/text_cursor.dart +++ b/patterns/command/application/text_cursor.dart @@ -9,9 +9,9 @@ class TextCursor { final int _position; - int? _startSelection; + final int? _startSelection; - TextCursor.fromPosition(this._position); + TextCursor.fromPosition(this._position) : _startSelection = null; TextCursor.fromSelection(this._startSelection, this._position); } From 1b9925a1d6d1a3780504f3678b99420d364fd5f7 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 20:06:57 +0200 Subject: [PATCH 117/479] Add "Editor.toString()" to render the text cursor state. --- patterns/command/application/application.dart | 4 +-- patterns/command/application/editor.dart | 29 ++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/patterns/command/application/application.dart b/patterns/command/application/application.dart index c893029..91df163 100644 --- a/patterns/command/application/application.dart +++ b/patterns/command/application/application.dart @@ -62,7 +62,7 @@ class Application { _executeListener?.call( command: command, isUndo: true, - editorText: editor.text, + editorText: editor.toString(), ); } } @@ -71,7 +71,7 @@ class Application { void _executeAndPushHistory(Command command) { command.execute(); - final textAfter = editor.text; + final textAfter = editor.toString(); _executeListener?.call( command: command, isUndo: false, diff --git a/patterns/command/application/editor.dart b/patterns/command/application/editor.dart index 749ad1e..ac167a5 100644 --- a/patterns/command/application/editor.dart +++ b/patterns/command/application/editor.dart @@ -3,19 +3,7 @@ import 'text_cursor.dart'; class Editor { var _text = ''; - String get text { - return cursor.isTextSelected - ? _text.replaceRange( - cursor.startSelection, - cursor.endSelection, - '[$selectedText]', - ) - : _text.replaceRange( - cursor.position, - cursor.position, - '|', - ); - } + String get text => _text; void inputText(String addText) { if (cursor.isTextSelected) { @@ -71,4 +59,19 @@ class Editor { ); cursorPosition = cursor.position + insertText.length; } + + @override + String toString() { + return cursor.isTextSelected + ? _text.replaceRange( + cursor.startSelection, + cursor.endSelection, + '[$selectedText]', + ) + : _text.replaceRange( + cursor.position, + cursor.position, + '|', + ); + } } From e2df5a70a178272ddc60953222a642fc6d0d5cdb Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 17 Jan 2022 22:02:46 +0200 Subject: [PATCH 118/479] Move command pattern to separate folder. --- patterns/command/{ => text_editor}/application/application.dart | 0 .../command/{ => text_editor}/application/command_history.dart | 0 patterns/command/{ => text_editor}/application/editor.dart | 0 patterns/command/{ => text_editor}/application/text_cursor.dart | 0 patterns/command/{ => text_editor}/commands/command.dart | 0 patterns/command/{ => text_editor}/commands/copy_command.dart | 0 patterns/command/{ => text_editor}/commands/cut_command.dart | 0 patterns/command/{ => text_editor}/commands/input_command.dart | 0 patterns/command/{ => text_editor}/commands/move_command.dart | 0 patterns/command/{ => text_editor}/commands/past_command.dart | 0 patterns/command/{ => text_editor}/commands/select_command.dart | 0 patterns/command/{ => text_editor}/main.dart | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename patterns/command/{ => text_editor}/application/application.dart (100%) rename patterns/command/{ => text_editor}/application/command_history.dart (100%) rename patterns/command/{ => text_editor}/application/editor.dart (100%) rename patterns/command/{ => text_editor}/application/text_cursor.dart (100%) rename patterns/command/{ => text_editor}/commands/command.dart (100%) rename patterns/command/{ => text_editor}/commands/copy_command.dart (100%) rename patterns/command/{ => text_editor}/commands/cut_command.dart (100%) rename patterns/command/{ => text_editor}/commands/input_command.dart (100%) rename patterns/command/{ => text_editor}/commands/move_command.dart (100%) rename patterns/command/{ => text_editor}/commands/past_command.dart (100%) rename patterns/command/{ => text_editor}/commands/select_command.dart (100%) rename patterns/command/{ => text_editor}/main.dart (100%) diff --git a/patterns/command/application/application.dart b/patterns/command/text_editor/application/application.dart similarity index 100% rename from patterns/command/application/application.dart rename to patterns/command/text_editor/application/application.dart diff --git a/patterns/command/application/command_history.dart b/patterns/command/text_editor/application/command_history.dart similarity index 100% rename from patterns/command/application/command_history.dart rename to patterns/command/text_editor/application/command_history.dart diff --git a/patterns/command/application/editor.dart b/patterns/command/text_editor/application/editor.dart similarity index 100% rename from patterns/command/application/editor.dart rename to patterns/command/text_editor/application/editor.dart diff --git a/patterns/command/application/text_cursor.dart b/patterns/command/text_editor/application/text_cursor.dart similarity index 100% rename from patterns/command/application/text_cursor.dart rename to patterns/command/text_editor/application/text_cursor.dart diff --git a/patterns/command/commands/command.dart b/patterns/command/text_editor/commands/command.dart similarity index 100% rename from patterns/command/commands/command.dart rename to patterns/command/text_editor/commands/command.dart diff --git a/patterns/command/commands/copy_command.dart b/patterns/command/text_editor/commands/copy_command.dart similarity index 100% rename from patterns/command/commands/copy_command.dart rename to patterns/command/text_editor/commands/copy_command.dart diff --git a/patterns/command/commands/cut_command.dart b/patterns/command/text_editor/commands/cut_command.dart similarity index 100% rename from patterns/command/commands/cut_command.dart rename to patterns/command/text_editor/commands/cut_command.dart diff --git a/patterns/command/commands/input_command.dart b/patterns/command/text_editor/commands/input_command.dart similarity index 100% rename from patterns/command/commands/input_command.dart rename to patterns/command/text_editor/commands/input_command.dart diff --git a/patterns/command/commands/move_command.dart b/patterns/command/text_editor/commands/move_command.dart similarity index 100% rename from patterns/command/commands/move_command.dart rename to patterns/command/text_editor/commands/move_command.dart diff --git a/patterns/command/commands/past_command.dart b/patterns/command/text_editor/commands/past_command.dart similarity index 100% rename from patterns/command/commands/past_command.dart rename to patterns/command/text_editor/commands/past_command.dart diff --git a/patterns/command/commands/select_command.dart b/patterns/command/text_editor/commands/select_command.dart similarity index 100% rename from patterns/command/commands/select_command.dart rename to patterns/command/text_editor/commands/select_command.dart diff --git a/patterns/command/main.dart b/patterns/command/text_editor/main.dart similarity index 100% rename from patterns/command/main.dart rename to patterns/command/text_editor/main.dart From e6922cbce3837956b3df25751387a160fa290266 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 18 Jan 2022 12:30:45 +0200 Subject: [PATCH 119/479] Add README & bump version: 0.12.0 --- CHANGELOG.md | 5 +- README.md | 2 +- patterns/command/text_editor/README.md | 69 +++++++++++++++++++ .../text_editor/application/application.dart | 4 +- pubspec.yaml | 2 +- 5 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 patterns/command/text_editor/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 122061c..dbfddf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ +## 0.12.0 +- Add "Command" pattern from official book, rewritten from Java example. + ## 0.11.0 -- Add chain of responsibility template from official book, rewritten from Java example +- Add "Chain of Responsibility" pattern from official book, rewritten from Java example ## 0.10.0 - Add composite template from official book, rewritten from Java example diff --git a/README.md b/README.md index 948730f..a38ee09 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ It contains **Dart** examples for all classic GoF design patterns. - [ ] Singleton - [ ] **Behavioral** - [x] Chain of Responsibility - - [ ] Command + - [x] Command - [ ] Interpreter - [ ] Iterator - [ ] Mediator diff --git a/patterns/command/text_editor/README.md b/patterns/command/text_editor/README.md new file mode 100644 index 0000000..a8248e8 --- /dev/null +++ b/patterns/command/text_editor/README.md @@ -0,0 +1,69 @@ +# Command pattern +Command is a behavioral design pattern that turns a request into a stand-alone object that contains +all information about the request. + +## Text Editor example +![image](https://user-images.githubusercontent.com/8049534/149916565-626f74bb-f922-4b10-acaa-87666cdbacb5.png) + +In this example, the Command pattern helps to track the history of executed operations and makes it +possible to revert an operation if needed. + +More detailed explanation on [RefactoringGuru](https://refactoring.guru/design-patterns/command?#pseudocode). + +## Origin source code: +This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/master/src/refactoring_guru/command/example). +But slightly changed, see the class diagram. + +## Diagram: +![image](https://user-images.githubusercontent.com/8049534/149918792-fccae912-2e67-4068-88d1-5cf824f0df2c.png) + +## Client code: +```dart +void main() { + final app = Application(); + app.addListenerExecuteCommand(log); + app + ..keyboardInput('Hello world') + ..moveCursor(position: 6) + ..keyboardInput('Refactring GuBu ') + ..selectText(start: 17, end: 19) + ..ctrlX() + ..selectText(start: 17, end: 19) + ..keyboardInput('Guru') + ..selectText(start: 4, end: 5) + ..ctrlC() + ..moveCursor(position: 12) + ..ctrlV() + ..moveCursor(position: 28); + + print('\nUndo steps\n'); + + while (app.isHistoryNotEmpty) { + app.ctrlZ(); + } +} +``` + +**Output:** +``` +[➕] Input( cursorPosition: 0, addText: "Hello world" )__________________"Hello world|" +[➕] Move( from: 11, to: 6 )_____________________________________________"Hello |world" +[➕] Input( cursorPosition: 6, addText: "Refactring GuBu " )_____________"Hello Refactring GuBu |world" +[➕] Select( start: 17, end: 19 )________________________________________"Hello Refactring [Gu]Bu world" +[➕] Cut( cursorPosition: 17, cutText: "Gu" )____________________________"Hello Refactring |Bu world" +[➕] Select( start: 17, end: 19 )________________________________________"Hello Refactring [Bu] world" +[➕] Input( cursorPosition: 17, addText: "Guru" )________________________"Hello Refactring Guru| world" +[➕] Select( start: 4, end: 5 )__________________________________________"Hell[o] Refactring Guru world" +[➕] Copy( text: "o" )___________________________________________________"Hell[o] Refactring Guru world" +[➕] Move( from: 5, to: 12 )_____________________________________________"Hello Refact|ring Guru world" +[➕] Past( cursorPosition: 13, text: "o", pevRestore: "" )_______________"Hello Refacto|ring Guru world" +[➕] Move( from: 13, to: 28 )____________________________________________"Hello Refactoring Guru world|" + +Undo steps + +Undo_Past( cursorPosition: 13, text: "o", pevRestore: "" )______________"Hello Refact|ring Guru world" +Undo_Input( cursorPosition: 17, addText: "Guru" )_______________________"Hello Refactring Bu| world" +Undo_Cut( cursorPosition: 17, cutText: "Gu" )___________________________"Hello Refactring Gu|Bu world" +Undo_Input( cursorPosition: 6, addText: "Refactring GuBu " )____________"Hello |world" +Undo_Input( cursorPosition: 0, addText: "Hello world" )_________________"|" +``` diff --git a/patterns/command/text_editor/application/application.dart b/patterns/command/text_editor/application/application.dart index 91df163..b4b35cb 100644 --- a/patterns/command/text_editor/application/application.dart +++ b/patterns/command/text_editor/application/application.dart @@ -71,11 +71,11 @@ class Application { void _executeAndPushHistory(Command command) { command.execute(); - final textAfter = editor.toString(); + final textAfterExecute = editor.toString(); _executeListener?.call( command: command, isUndo: false, - editorText: textAfter, + editorText: textAfterExecute, ); if (command.isSaveHistory) { diff --git a/pubspec.yaml b/pubspec.yaml index ea5be06..c640416 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.11.0 +version: 0.12.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 e628ffef20180bb1f382fb4b2b5dd91540ff3c0e Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 18 Jan 2022 15:14:49 +0200 Subject: [PATCH 120/479] Put "Shapes" prototype pattern to "Shapes" folder. --- patterns/prototype/{example => shapes}/README.md | 0 patterns/prototype/{example => shapes}/main.dart | 0 patterns/prototype/{example => shapes}/shape/circle.dart | 0 patterns/prototype/{example => shapes}/shape/rectangle.dart | 0 patterns/prototype/{example => shapes}/shape/shape.dart | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename patterns/prototype/{example => shapes}/README.md (100%) rename patterns/prototype/{example => shapes}/main.dart (100%) rename patterns/prototype/{example => shapes}/shape/circle.dart (100%) rename patterns/prototype/{example => shapes}/shape/rectangle.dart (100%) rename patterns/prototype/{example => shapes}/shape/shape.dart (100%) diff --git a/patterns/prototype/example/README.md b/patterns/prototype/shapes/README.md similarity index 100% rename from patterns/prototype/example/README.md rename to patterns/prototype/shapes/README.md diff --git a/patterns/prototype/example/main.dart b/patterns/prototype/shapes/main.dart similarity index 100% rename from patterns/prototype/example/main.dart rename to patterns/prototype/shapes/main.dart diff --git a/patterns/prototype/example/shape/circle.dart b/patterns/prototype/shapes/shape/circle.dart similarity index 100% rename from patterns/prototype/example/shape/circle.dart rename to patterns/prototype/shapes/shape/circle.dart diff --git a/patterns/prototype/example/shape/rectangle.dart b/patterns/prototype/shapes/shape/rectangle.dart similarity index 100% rename from patterns/prototype/example/shape/rectangle.dart rename to patterns/prototype/shapes/shape/rectangle.dart diff --git a/patterns/prototype/example/shape/shape.dart b/patterns/prototype/shapes/shape/shape.dart similarity index 100% rename from patterns/prototype/example/shape/shape.dart rename to patterns/prototype/shapes/shape/shape.dart From c9b240fc41d2208e3650ee7b5c707bb5e0bcd8b8 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 18 Jan 2022 15:16:35 +0200 Subject: [PATCH 121/479] Add projects link to main README. --- README.md | 50 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index a38ee09..a67d2b4 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,34 @@ # 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. +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 - - [x] Builder - - [ ] Factory Method - - [x] Prototype - - [ ] Singleton + - [ ] [**Abstract Factory**](https://refactoring.guru/design-patterns/abstract-factory) + - [x] [**Builder**](https://refactoring.guru/design-patterns/builder) - [[Color Text Formats](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/builder/color_text_formats)] + - [ ] ![image](https://user-images.githubusercontent.com/8049534/149951271-27c95d06-99bd-420c-9d34-3ae184c51fb0.png) [**Factory Method**](https://refactoring.guru/design-patterns/factory-method) + - [x] [**Prototype**](https://refactoring.guru/design-patterns/prototype) - [[Shapes](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/prototype/shapes)] + - [ ] [**Singleton**](https://refactoring.guru/design-patterns/singleton) - [ ] **Behavioral** - - [x] Chain of Responsibility - - [x] Command + - [x] [**Chain of Responsibility**](https://refactoring.guru/design-patterns/chain-of-esponsibility) - [[Server Middleware](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/chain_of_esponsibility/server_middleware)] + - [x] [**Command**](https://refactoring.guru/design-patterns/command) - [[Text Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/command/text_editor)] - [ ] Interpreter - - [ ] Iterator - - [ ] Mediator - - [ ] Memento - - [ ] Observer - - [ ] State - - [ ] Template Method - - [ ] Visitor - - [ ] Strategy + - [ ] [**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) + - [ ] [**State**](https://refactoring.guru/design-patterns/state) + - [ ] [**Template Method**](https://refactoring.guru/design-patterns/template-method) + - [ ] [**Visitor**](https://refactoring.guru/design-patterns/visitor) + - [ ] [**Strategy**](https://refactoring.guru/design-patterns/strategy) - [ ] **Structural** - - [x] Adapter - - [x] Bridge - - [x] Composite - - [x] Decorator - - [ ] Facade - - [ ] Flyweight - - [ ] Proxy + - [x] [**Adapter**](https://refactoring.guru/design-patterns/adapter) - [[Text Graphics](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/text_graphics)] [[Square Round conflict](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/square_round_conflict)] + - [x] [**Bridge**](https://refactoring.guru/design-patterns/bridge) - [[Remote Device Control](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/bridge/devices_remote_control)] [[Clock](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/bridge/clock)] + - [x] [**Composite**](https://refactoring.guru/design-patterns/composite) - [[Image Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/composite/image_editor)] [[Products and Boxes](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/composite/products_and_boxes)] + - [x] [**Decorator**](https://refactoring.guru/design-patterns/decorator) - [[Data Source Decoder](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/decorator/data_source_decoder)] + - [ ] [**Facade**](https://refactoring.guru/design-patterns/facade) + - [ ] [**Flyweight**](https://refactoring.guru/design-patterns/flyweight) + - [ ] [**Proxy**](https://refactoring.guru/design-patterns/proxy) ## Requirements From edc723898440077e178c1f37c6b16236dc5d16c8 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 18 Jan 2022 16:02:04 +0200 Subject: [PATCH 122/479] Remove icon in main README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a67d2b4..7ca4be9 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ It contains **Dart** examples for all classic **GoF** design patterns. - [ ] **Creation** - [ ] [**Abstract Factory**](https://refactoring.guru/design-patterns/abstract-factory) - [x] [**Builder**](https://refactoring.guru/design-patterns/builder) - [[Color Text Formats](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/builder/color_text_formats)] - - [ ] ![image](https://user-images.githubusercontent.com/8049534/149951271-27c95d06-99bd-420c-9d34-3ae184c51fb0.png) [**Factory Method**](https://refactoring.guru/design-patterns/factory-method) + - [ ] [**Factory Method**](https://refactoring.guru/design-patterns/factory-method) - [x] [**Prototype**](https://refactoring.guru/design-patterns/prototype) - [[Shapes](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/prototype/shapes)] - [ ] [**Singleton**](https://refactoring.guru/design-patterns/singleton) - [ ] **Behavioral** From c766757d3f9d962060864959b411e6595bc1dcf9 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 18 Jan 2022 16:09:41 +0200 Subject: [PATCH 123/479] Bump version: 0.12.5 --- CHANGELOG.md | 4 ++++ pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbfddf8..1eb5f13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.12.5 +- 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. diff --git a/pubspec.yaml b/pubspec.yaml index c640416..5c0ea4c 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.0 +version: 0.12.5 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 9cfa36467d935fbea93cba4acaa3401ba419f15c Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 24 Jan 2022 11:57:07 +0200 Subject: [PATCH 124/479] Fix README project urls. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7ca4be9..0e906c4 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,12 @@ It contains **Dart** examples for all classic **GoF** design patterns. # Implementation checklist: - [ ] **Creation** - [ ] [**Abstract Factory**](https://refactoring.guru/design-patterns/abstract-factory) - - [x] [**Builder**](https://refactoring.guru/design-patterns/builder) - [[Color Text Formats](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/builder/color_text_formats)] + - [x] [**Builder**](https://refactoring.guru/design-patterns/builder) - [[Color Text Format](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/builder/color_text_format)] - [ ] [**Factory Method**](https://refactoring.guru/design-patterns/factory-method) - [x] [**Prototype**](https://refactoring.guru/design-patterns/prototype) - [[Shapes](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/prototype/shapes)] - [ ] [**Singleton**](https://refactoring.guru/design-patterns/singleton) - [ ] **Behavioral** - - [x] [**Chain of Responsibility**](https://refactoring.guru/design-patterns/chain-of-esponsibility) - [[Server Middleware](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/chain_of_esponsibility/server_middleware)] + - [x] [**Chain of Responsibility**](https://refactoring.guru/design-patterns/chain-of-esponsibility) - [[Server Middleware](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/chain_of_responsibility/server_middleware)] - [x] [**Command**](https://refactoring.guru/design-patterns/command) - [[Text Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/command/text_editor)] - [ ] Interpreter - [ ] [**Iterator**](https://refactoring.guru/design-patterns/iterator) From 1cf0c2e4e94cd538fe6f26b47d5aca4414ba2638 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 24 Jan 2022 12:15:20 +0200 Subject: [PATCH 125/479] Fix README builder pattern. --- patterns/builder/color_text_format/README.md | 28 +++++++++++--------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/patterns/builder/color_text_format/README.md b/patterns/builder/color_text_format/README.md index d4eb11f..ee5765e 100644 --- a/patterns/builder/color_text_format/README.md +++ b/patterns/builder/color_text_format/README.md @@ -1,9 +1,16 @@ # Builder pattern -Using different text formats +Builder is a creational design pattern that lets you construct complex objects step by step. +**General description of the pattern:** +https://refactoring.guru/design-patterns/builder + +## Example +Using different text formats. + +### Class diagram ![image](https://user-images.githubusercontent.com/8049534/146023073-5d7644a4-d3b9-4420-bffe-f72ac3fd83dd.png) -**Client code:** +### Client code: ```dart main() { final reader = ColorTextReader( @@ -11,9 +18,11 @@ 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' @@ -23,8 +32,7 @@ main() { ``` **Output:** -``` -HtmlFormat( +```html @@ -43,9 +51,8 @@ HtmlFormat( green grass.

-), - -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. -), ``` +![image](https://user-images.githubusercontent.com/8049534/150763802-9d21f8b7-011d-4ff5-bb53-5d8a3d6d1d4c.png) + 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: ![image](https://user-images.githubusercontent.com/8049534/146028651-1262b66d-b0ac-4fc9-9487-76a2d588f97e.png) -**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: ![example](https://user-images.githubusercontent.com/8049534/147594536-66627fa1-f4eb-42ba-b648-8757f9e5bf20.png) -**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: ![image](https://user-images.githubusercontent.com/8049534/145851578-f6e95355-e2b3-4f94-bda2-c2d1d0de8935.png) -**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 ![image](https://user-images.githubusercontent.com/8049534/149480179-ba06640c-0858-4ff9-8957-f2c4aa22ccc4.png) -## 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: ![image](https://user-images.githubusercontent.com/8049534/149488654-7ff5f659-4086-4d1a-ae44-326c71fb880a.png) -## 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-en](https://user-images.githubusercontent.com/8049534/147579298-0c60c4a7-6acb-4ab3-a973-e06524c5a061.png) - -**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: ![image](https://user-images.githubusercontent.com/8049534/147579175-f5ce6191-a76a-4f1f-8ac9-fae1a26f87bb.png) -**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: ![image](https://user-images.githubusercontent.com/8049534/148954932-edc22d7b-becd-4e2f-bae8-d0d8200d8918.png) -## 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: +![image](https://user-images.githubusercontent.com/8049534/150781031-52c8c32a-e94b-4f1b-86b9-eb0012427682.png) + + +### 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: +![image](https://user-images.githubusercontent.com/8049534/150955865-7fbb29f3-ed48-4317-a356-cbb9cc79ed11.png) + +### 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: +![image](https://user-images.githubusercontent.com/8049534/151352367-c97db094-fc87-4eb2-9210-581914c57ced.png) + +### 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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 literal 0 HcmV?d00001 diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..0e76fb2 --- /dev/null +++ b/web/index.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + flutter_launcher + + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..7bab0e5 --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "flutter_launcher", + "short_name": "flutter_launcher", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} From 9f970a2c545e3584b33cddef39ffb4d660c867fa Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 1 Feb 2022 22:30:56 +0200 Subject: [PATCH 145/479] Add "Observer" pattern. --- patterns/observer/app_observer/main.dart | 36 +++++++++++++++ .../app_observer/observer/app_observer.dart | 46 +++++++++++++++++++ .../observer/app_observer/observer/event.dart | 3 ++ 3 files changed, 85 insertions(+) create mode 100644 patterns/observer/app_observer/main.dart create mode 100644 patterns/observer/app_observer/observer/app_observer.dart create mode 100644 patterns/observer/app_observer/observer/event.dart diff --git a/patterns/observer/app_observer/main.dart b/patterns/observer/app_observer/main.dart new file mode 100644 index 0000000..620fb21 --- /dev/null +++ b/patterns/observer/app_observer/main.dart @@ -0,0 +1,36 @@ +import 'observer/app_observer.dart'; +import 'observer/event.dart'; + +class FirstEvent extends Event {} + +class SecondEvent extends Event {} + +class ThirdEvent extends Event {} + +void main() { + final observer = AppObserver(); + + observer.subscribe((e) { + print('First'); + }); + + + observer.subscribe((SecondEvent e) { + print('Second'); + }); + + final saveThirdEvent = observer.subscribe((ThirdEvent e) { + print('Third'); + }); + + observer.notify(FirstEvent()); + observer.notify(SecondEvent()); + observer.notify(ThirdEvent()); + + print('---unsubscribe "ThirdEvent"---'); + observer.unsubscribe(saveThirdEvent); + + observer.notify(FirstEvent()); + observer.notify(SecondEvent()); + observer.notify(ThirdEvent()); +} diff --git a/patterns/observer/app_observer/observer/app_observer.dart b/patterns/observer/app_observer/observer/app_observer.dart new file mode 100644 index 0000000..a6fa01a --- /dev/null +++ b/patterns/observer/app_observer/observer/app_observer.dart @@ -0,0 +1,46 @@ +import 'event.dart'; + +typedef EventFunction = void Function(T e); + +class AppObserver { + final _subscribers = >{}; + + EventFunction subscribe(EventFunction func) { + assert( + Class() is Class, + '\n\nThe callback argument must implement the "Event" class.\n' + 'Correct use: \n' + '\tobserver.subscribe((MyEvent e) {}); \n' + 'Mistaken usage: \n' + '\tobserver.subscribe((String e) {});\n' + '\tobserver.subscribe((e) {});\n', + ); + + _subscribers.putIfAbsent(T.hashCode, () => []).add(func); + return func; + } + + void unsubscribe(EventFunction func) { + final isDelete = _subscribers[T.hashCode]?.remove(func) ?? false; + + if (isDelete) { + return; + } + + throw Exception('Subscriber not found.'); + } + + void notify(T event) { + final subscribers = _subscribers[T.hashCode]; + + if (subscribers == null) { + return; + } + + for (var sub in subscribers) { + sub.call(event); + } + } +} + +class Class {} diff --git a/patterns/observer/app_observer/observer/event.dart b/patterns/observer/app_observer/observer/event.dart new file mode 100644 index 0000000..dbd3bf8 --- /dev/null +++ b/patterns/observer/app_observer/observer/event.dart @@ -0,0 +1,3 @@ +abstract class Event { + +} From 0b80057d0f14ccd64b89f7d4fc83a9a4a62f4422 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 1 Feb 2022 22:58:19 +0200 Subject: [PATCH 146/479] Add README. --- patterns/observer/app_observer/README.md | 56 ++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 patterns/observer/app_observer/README.md diff --git a/patterns/observer/app_observer/README.md b/patterns/observer/app_observer/README.md new file mode 100644 index 0000000..4e509eb --- /dev/null +++ b/patterns/observer/app_observer/README.md @@ -0,0 +1,56 @@ +# 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. + +Tutorial: [here](https://refactoring.guru/design-patterns/observer). + +## AppObserver example +This example was created to be used in a more complex example. +A complex example will be implemented later. + +### Diagram: +![image](https://user-images.githubusercontent.com/8049534/152049751-b111e02a-1d33-4796-810c-b7ed069cecdc.png) + +### Sequence +![image](https://user-images.githubusercontent.com/8049534/152049996-72131655-402d-4b92-b5d0-10e3f2dd0e79.png) + +### Client code: +```dart +void main() { + final observer = AppObserver(); + + observer.subscribe((e) { + print('First'); + }); + + + observer.subscribe((SecondEvent e) { + print('Second'); + }); + + final saveThirdEvent = observer.subscribe((ThirdEvent e) { + print('Third'); + }); + + observer.notify(FirstEvent()); + observer.notify(SecondEvent()); + observer.notify(ThirdEvent()); + + print('---unsubscribe "ThirdEvent"---'); + observer.unsubscribe(saveThirdEvent); + + observer.notify(FirstEvent()); + observer.notify(SecondEvent()); + observer.notify(ThirdEvent()); +} +``` + +**Output:** +``` +First +Second +Third +---unsubscribe "ThirdEvent"--- +First +Second +``` From eeb4798bcece93d8c0e8c02eabfc058f5d277256 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 1 Feb 2022 23:01:08 +0200 Subject: [PATCH 147/479] Bump version 0.15.0. --- CHANGELOG.md | 3 +++ README.md | 4 ++-- pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 203262a..52b67b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.15.0 +- Add second "Observer" example. This example was created to be used in a more complex example. + ## 0.14.0 - Add "Memento" conceptual pattern diff --git a/README.md b/README.md index 8f0052c..c7cbbda 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ 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) - - [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)] + - [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)] [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer)] - [ ] [**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 6c85b87..c516e54 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.14.0 +version: 0.15.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 c6b8122a99baa927a053cbd1d68e28ed4d142b1f Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 2 Feb 2022 14:19:44 +0200 Subject: [PATCH 148/479] Add flutter adapter. --- bin/main.dart | 11 +- .../flutter_adapter/adapter/classic_app.dart | 69 +++++++++++ .../adapter/classic_app_render_object.dart | 83 +++++++++++++ .../adapter/classic_app_widget_adapter.dart | 24 ++++ .../flutter_adapter/client_app/app.dart | 65 ++++++++++ .../flutter_text_property_widget.dart | 112 ++++++++++++++++++ patterns/adapter/flutter_adapter/main.dart | 30 +++++ 7 files changed, 388 insertions(+), 6 deletions(-) create mode 100644 patterns/adapter/flutter_adapter/adapter/classic_app.dart create mode 100644 patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart create mode 100644 patterns/adapter/flutter_adapter/adapter/classic_app_widget_adapter.dart create mode 100644 patterns/adapter/flutter_adapter/client_app/app.dart create mode 100644 patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart create mode 100644 patterns/adapter/flutter_adapter/main.dart diff --git a/bin/main.dart b/bin/main.dart index 3865bb7..43d49a6 100644 --- a/bin/main.dart +++ b/bin/main.dart @@ -1,20 +1,19 @@ import 'package:flutter/material.dart'; +import '../patterns/adapter/flutter_adapter/main.dart'; void main() { - runApp(const MyApp()); + runApp(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 + theme: ThemeData(primarySwatch: Colors.pink), + home: Scaffold( + body: FlutterAdapterApp(), ), - home: Container(), ); } } diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app.dart b/patterns/adapter/flutter_adapter/adapter/classic_app.dart new file mode 100644 index 0000000..70813f7 --- /dev/null +++ b/patterns/adapter/flutter_adapter/adapter/classic_app.dart @@ -0,0 +1,69 @@ +import 'dart:ui'; + +import '../../../observer/app_observer/observer/app_observer.dart'; +import '../../../observer/app_observer/observer/event.dart'; + +abstract class ClassicApp { + AppObserver get events; + + void onMouseDown(); + + void onMouseUp(); + + void onMouseMove(); + + void onMoseEnter(); + + void onMouseLeave(); + + void onKeyDown(); + + void onKeyUp(); + + void onWheel(double deltaX, double deltaY); + + void onPaint(Canvas canvas, Size size); + + void repaint(); +} + +class ClassicAppRepaint extends Event {} + +abstract class ClassicAppBase implements ClassicApp { + final _appEvents = AppObserver(); + + @override + AppObserver get events => _appEvents; + + @override + void onKeyDown() {} + + @override + void onKeyUp() {} + + @override + void onMoseEnter() {} + + @override + void onMouseDown() {} + + @override + void onMouseLeave() {} + + @override + void onMouseMove() {} + + @override + void onMouseUp() {} + + @override + void onWheel(double deltaX, double deltaY) {} + + @override + void onPaint(Canvas canvas, Size size); + + @override + void repaint() { + _appEvents.notify(ClassicAppRepaint()); + } +} diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart b/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart new file mode 100644 index 0000000..d3241b3 --- /dev/null +++ b/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart @@ -0,0 +1,83 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/rendering.dart'; + +import 'classic_app.dart'; + +class ClassicAppRenderObject extends RenderBox { + ClassicAppRenderObject(ClassicApp classicApp) { + _classicApp = classicApp; + _classicApp.events.subscribe(_clientAppRepaint); + _isSubscribe = true; + } + + @override + bool get isRepaintBoundary => true; + + @override + void performLayout() { + size = Size( + constraints.maxWidth == double.infinity ? 0 : constraints.maxWidth, + constraints.maxHeight == double.infinity ? 0 : constraints.maxHeight, + ); + } + + @override + void paint(PaintingContext context, Offset offset) { + context.canvas.translate(offset.dx, offset.dy); + _classicApp.onPaint(context.canvas, size); + } + + @override + void dispose() { + if (_isSubscribe) { + _classicApp.events.unsubscribe(_clientAppRepaint); + _isSubscribe = false; + } + super.dispose(); + } + + late ClassicApp _classicApp; + var _isSubscribe = false; + + ClassicApp get classicApp => _classicApp; + + set classicApp(ClassicApp newClassicApp) { + if (newClassicApp == _classicApp) { + return; + } + + if (_isSubscribe) { + _classicApp.events.unsubscribe(_clientAppRepaint); + _isSubscribe = false; + } + + _classicApp = newClassicApp; + _classicApp.events.subscribe(_clientAppRepaint); + _isSubscribe = true; + } + + void _clientAppRepaint(ClassicAppRepaint e) => markNeedsPaint(); + + @override + void handleEvent(PointerEvent event, covariant BoxHitTestEntry entry) { + if (event is PointerHoverEvent || event is PointerMoveEvent) { + } else if (event is PointerDownEvent) { + if (event.buttons == kPrimaryMouseButton) { + _classicApp.onMouseDown(); + } else if (event.buttons == kSecondaryMouseButton) { + } else if (event.buttons == kMiddleMouseButton) {} + } else if (event is PointerScrollEvent) { + _classicApp.onWheel(event.scrollDelta.dx, event.scrollDelta.dy); + + } + } + + @override + bool hitTest(BoxHitTestResult result, {required Offset position}) { + if (size.contains(position)) { + result.add(BoxHitTestEntry(this, position)); + return true; + } + return false; + } +} diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app_widget_adapter.dart b/patterns/adapter/flutter_adapter/adapter/classic_app_widget_adapter.dart new file mode 100644 index 0000000..50f8154 --- /dev/null +++ b/patterns/adapter/flutter_adapter/adapter/classic_app_widget_adapter.dart @@ -0,0 +1,24 @@ +import 'package:flutter/widgets.dart'; + + +import 'classic_app.dart'; +import 'classic_app_render_object.dart'; + +class ClassicAppWidgetAdapter extends LeafRenderObjectWidget { + final ClassicApp classicApp; + + ClassicAppWidgetAdapter({required this.classicApp}); + + @override + RenderObject createRenderObject(BuildContext context) { + return ClassicAppRenderObject(classicApp); + } + + @override + void updateRenderObject( + BuildContext context, + covariant ClassicAppRenderObject renderObject, + ) { + renderObject.classicApp = classicApp; + } +} diff --git a/patterns/adapter/flutter_adapter/client_app/app.dart b/patterns/adapter/flutter_adapter/client_app/app.dart new file mode 100644 index 0000000..0c1c5a7 --- /dev/null +++ b/patterns/adapter/flutter_adapter/client_app/app.dart @@ -0,0 +1,65 @@ +import 'dart:ui'; + +import '../adapter/classic_app.dart'; + +class App extends ClassicAppBase { + final maxTextSize = 150; + + var _textSize = 50; + + int get textSize => _textSize; + + set textSize(int newVal) { + if (newVal == _textSize || newVal > maxTextSize || newVal < 1) { + return; + } + + _textSize = newVal; + repaint(); + } + + var _textColor = Color(0xffd81b60); + + Color get textColor => _textColor; + + set textColor(Color newColor) { + if (_textColor == newColor) { + return; + } + print(newColor); + _textColor = newColor; + repaint(); + } + + @override + void onWheel(double deltaX, double deltaY) { + textSize += deltaY ~/ 10; + } + + @override + void onPaint(Canvas canvas, Size size) { + _paintTextCenter('Flutter Adapter', canvas, size); + } + + void _paintTextCenter(String text, Canvas canvas, Size size) { + final builder = ParagraphBuilder( + ParagraphStyle( + textAlign: TextAlign.center, + fontSize: _textSize.toDouble(), + ), + ) + ..pushStyle( + TextStyle( + fontFamily: 'Arial', + color: _textColor, + ), + ) + ..addText(text); + + final paragraph = builder.build() + ..layout(ParagraphConstraints(width: size.width)); + + final centerPos = Offset(0, (size.height - paragraph.height) / 2); + canvas.drawParagraph(paragraph, centerPos); + } +} diff --git a/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart b/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart new file mode 100644 index 0000000..913441b --- /dev/null +++ b/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart @@ -0,0 +1,112 @@ +import 'package:flutter/material.dart'; + +import '../../../observer/app_observer/observer/app_observer.dart'; +import '../adapter/classic_app.dart'; +import 'app.dart'; + +class FlutterTextPropertyWidget extends StatefulWidget { + final App classicApp; + + const FlutterTextPropertyWidget({ + Key? key, + required this.classicApp, + }) : super(key: key); + + @override + State createState() => + _FlutterTextPropertyWidgetState(); +} + +class _FlutterTextPropertyWidgetState extends State { + @override + Widget build(BuildContext context) { + final app = widget.classicApp; + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildTextSizeSlider(app), + _buildTextColorButtons(app), + ], + ); + } + + late EventFunction _classicAppRepaintEvent; + + @override + void initState() { + _classicAppRepaintEvent = widget.classicApp.events.subscribe( + (ClassicAppRepaint e) => setState(() {}), + ); + super.initState(); + } + + @override + void dispose() { + widget.classicApp.events.unsubscribe(_classicAppRepaintEvent); + super.dispose(); + } + + Widget _buildTextSizeSlider(App app) { + return Row( + children: [ + SizedBox( + width: 31, + child: Text(app.textSize.toString().padLeft(3, ' ')), + ), + SizedBox( + width: 200, + child: Slider( + value: app.textSize.toDouble(), + max: app.maxTextSize.toDouble(), + min: 1, + onChanged: (newVal) { + app.textSize = newVal.toInt(); + }, + ), + ), + ], + ); + } + + Widget _buildTextColorButtons(App app) { + final colors = [ + Colors.black, + Colors.pink.shade600, + Colors.deepPurple.shade600, + ]; + return Row( + children: [ + ...colors.map( + (Color color) { + return _buildColorButton(color, app); + }, + ).toList(), + ], + ); + } + + Widget _buildColorButton(Color color, App app) { + final isColorSelect = (color == app.textColor); + return GestureDetector( + onTap: () { + app.textColor = color; + }, + child: Container( + width: 20, + height: 20, + color: color, + child: isColorSelect ? _buildSelectColorIcon() : null, + ), + ); + } + + Widget _buildSelectColorIcon() { + return Center( + child: Container( + width: 4, + height: 4, + color: Colors.white.withOpacity(0.8), + ), + ); + } +} diff --git a/patterns/adapter/flutter_adapter/main.dart b/patterns/adapter/flutter_adapter/main.dart new file mode 100644 index 0000000..736d280 --- /dev/null +++ b/patterns/adapter/flutter_adapter/main.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +import 'adapter/classic_app_widget_adapter.dart'; +import 'client_app/app.dart'; +import 'client_app/flutter_text_property_widget.dart'; + +class FlutterAdapterApp extends StatefulWidget { + @override + State createState() => _FlutterAdapterAppState(); +} + +class _FlutterAdapterAppState extends State { + final app = App(); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + FlutterTextPropertyWidget( + classicApp: app, + ), + Expanded( + child: ClassicAppWidgetAdapter( + classicApp: app, + ), + ), + ], + ); + } +} From a5878ce1783176be2678c37feb08ee9f90ba0f2a Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 2 Feb 2022 14:58:43 +0200 Subject: [PATCH 149/479] Add "NextColorEvent". --- .../flutter_adapter/client_app/app.dart | 8 +++++ .../flutter_text_property_widget.dart | 35 +++++++++++++++---- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/patterns/adapter/flutter_adapter/client_app/app.dart b/patterns/adapter/flutter_adapter/client_app/app.dart index 0c1c5a7..1f2983b 100644 --- a/patterns/adapter/flutter_adapter/client_app/app.dart +++ b/patterns/adapter/flutter_adapter/client_app/app.dart @@ -1,7 +1,10 @@ import 'dart:ui'; +import '../../../observer/app_observer/observer/event.dart'; import '../adapter/classic_app.dart'; +class NextTextColorEvent extends Event {} + class App extends ClassicAppBase { final maxTextSize = 150; @@ -36,6 +39,11 @@ class App extends ClassicAppBase { textSize += deltaY ~/ 10; } + @override + void onMouseDown() { + events.notify(NextTextColorEvent()); + } + @override void onPaint(Canvas canvas, Size size) { _paintTextCenter('Flutter Adapter', canvas, size); diff --git a/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart b/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart index 913441b..de571a5 100644 --- a/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart +++ b/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart @@ -29,20 +29,43 @@ class _FlutterTextPropertyWidgetState extends State { ], ); } + final colors = [ + Colors.black, + Colors.pink.shade600, + Colors.deepPurple.shade600, + Colors.blue.shade600, + Colors.green.shade600, + ]; late EventFunction _classicAppRepaintEvent; + late EventFunction _nextColorEvent; @override void initState() { _classicAppRepaintEvent = widget.classicApp.events.subscribe( (ClassicAppRepaint e) => setState(() {}), ); + + _nextColorEvent = widget.classicApp.events.subscribe( + (NextTextColorEvent e) => setState(() { + final currColor = widget.classicApp.textColor; + var nextIndex = colors.indexOf(currColor)+1; + + if (nextIndex >= colors.length) { + nextIndex = 0; + } + + widget.classicApp.textColor = colors[nextIndex]; + }), + ); super.initState(); } @override void dispose() { - widget.classicApp.events.unsubscribe(_classicAppRepaintEvent); + widget.classicApp.events + ..unsubscribe(_classicAppRepaintEvent) + ..unsubscribe(_nextColorEvent); super.dispose(); } @@ -51,7 +74,10 @@ class _FlutterTextPropertyWidgetState extends State { children: [ SizedBox( width: 31, - child: Text(app.textSize.toString().padLeft(3, ' ')), + child: Text( + app.textSize.toString(), + textAlign: TextAlign.right, + ), ), SizedBox( width: 200, @@ -69,11 +95,6 @@ class _FlutterTextPropertyWidgetState extends State { } Widget _buildTextColorButtons(App app) { - final colors = [ - Colors.black, - Colors.pink.shade600, - Colors.deepPurple.shade600, - ]; return Row( children: [ ...colors.map( From 4710eff807f4437629642cc53cfe3aacabdae3f6 Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 2 Feb 2022 15:12:51 +0200 Subject: [PATCH 150/479] Move the text functionality to a separate Text Coloring class. --- .../flutter_adapter/client_app/app.dart | 55 ++--------------- .../flutter_text_property_widget.dart | 16 ++--- .../client_app/text_coloring.dart | 59 +++++++++++++++++++ 3 files changed, 73 insertions(+), 57 deletions(-) create mode 100644 patterns/adapter/flutter_adapter/client_app/text_coloring.dart diff --git a/patterns/adapter/flutter_adapter/client_app/app.dart b/patterns/adapter/flutter_adapter/client_app/app.dart index 1f2983b..0167a3e 100644 --- a/patterns/adapter/flutter_adapter/client_app/app.dart +++ b/patterns/adapter/flutter_adapter/client_app/app.dart @@ -2,41 +2,20 @@ import 'dart:ui'; import '../../../observer/app_observer/observer/event.dart'; import '../adapter/classic_app.dart'; +import 'text_coloring.dart'; class NextTextColorEvent extends Event {} class App extends ClassicAppBase { - final maxTextSize = 150; - - var _textSize = 50; - - int get textSize => _textSize; - - set textSize(int newVal) { - if (newVal == _textSize || newVal > maxTextSize || newVal < 1) { - return; - } - - _textSize = newVal; - repaint(); + App() { + textColoring = TextColoring(this); } - var _textColor = Color(0xffd81b60); - - Color get textColor => _textColor; - - set textColor(Color newColor) { - if (_textColor == newColor) { - return; - } - print(newColor); - _textColor = newColor; - repaint(); - } + late final TextColoring textColoring; @override void onWheel(double deltaX, double deltaY) { - textSize += deltaY ~/ 10; + textColoring.textSize += deltaY ~/ 10; } @override @@ -46,28 +25,6 @@ class App extends ClassicAppBase { @override void onPaint(Canvas canvas, Size size) { - _paintTextCenter('Flutter Adapter', canvas, size); - } - - void _paintTextCenter(String text, Canvas canvas, Size size) { - final builder = ParagraphBuilder( - ParagraphStyle( - textAlign: TextAlign.center, - fontSize: _textSize.toDouble(), - ), - ) - ..pushStyle( - TextStyle( - fontFamily: 'Arial', - color: _textColor, - ), - ) - ..addText(text); - - final paragraph = builder.build() - ..layout(ParagraphConstraints(width: size.width)); - - final centerPos = Offset(0, (size.height - paragraph.height) / 2); - canvas.drawParagraph(paragraph, centerPos); + textColoring.paint('Flutter Adapter', canvas, size); } } diff --git a/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart b/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart index de571a5..a3b1d51 100644 --- a/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart +++ b/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart @@ -48,14 +48,14 @@ class _FlutterTextPropertyWidgetState extends State { _nextColorEvent = widget.classicApp.events.subscribe( (NextTextColorEvent e) => setState(() { - final currColor = widget.classicApp.textColor; + final currColor = widget.classicApp.textColoring.textColor; var nextIndex = colors.indexOf(currColor)+1; if (nextIndex >= colors.length) { nextIndex = 0; } - widget.classicApp.textColor = colors[nextIndex]; + widget.classicApp.textColoring.textColor = colors[nextIndex]; }), ); super.initState(); @@ -75,18 +75,18 @@ class _FlutterTextPropertyWidgetState extends State { SizedBox( width: 31, child: Text( - app.textSize.toString(), + app.textColoring.textSize.toString(), textAlign: TextAlign.right, ), ), SizedBox( width: 200, child: Slider( - value: app.textSize.toDouble(), - max: app.maxTextSize.toDouble(), + value: app.textColoring.textSize.toDouble(), + max: app.textColoring.maxTextSize.toDouble(), min: 1, onChanged: (newVal) { - app.textSize = newVal.toInt(); + app.textColoring.textSize = newVal.toInt(); }, ), ), @@ -107,10 +107,10 @@ class _FlutterTextPropertyWidgetState extends State { } Widget _buildColorButton(Color color, App app) { - final isColorSelect = (color == app.textColor); + final isColorSelect = (color == app.textColoring.textColor); return GestureDetector( onTap: () { - app.textColor = color; + app.textColoring.textColor = color; }, child: Container( width: 20, diff --git a/patterns/adapter/flutter_adapter/client_app/text_coloring.dart b/patterns/adapter/flutter_adapter/client_app/text_coloring.dart new file mode 100644 index 0000000..c4f772e --- /dev/null +++ b/patterns/adapter/flutter_adapter/client_app/text_coloring.dart @@ -0,0 +1,59 @@ +import 'dart:ui'; + +import 'app.dart'; + +class TextColoring { + final App app; + + TextColoring(this.app); + + final maxTextSize = 150; + + var _textSize = 50; + + int get textSize => _textSize; + + set textSize(int newVal) { + if (newVal == _textSize || newVal > maxTextSize || newVal < 1) { + return; + } + + _textSize = newVal; + app.repaint(); + } + + var _textColor = Color(0xffd81b60); + + Color get textColor => _textColor; + + set textColor(Color newColor) { + if (_textColor == newColor) { + return; + } + print(newColor); + _textColor = newColor; + app.repaint(); + } + + void paint(String text, Canvas canvas, Size size) { + final builder = ParagraphBuilder( + ParagraphStyle( + textAlign: TextAlign.center, + fontSize: _textSize.toDouble(), + ), + ) + ..pushStyle( + TextStyle( + fontFamily: 'Arial', + color: _textColor, + ), + ) + ..addText(text); + + final paragraph = builder.build() + ..layout(ParagraphConstraints(width: size.width)); + + final centerPos = Offset(0, (size.height - paragraph.height) / 2); + canvas.drawParagraph(paragraph, centerPos); + } +} From a4a09b9ddf4259fede145ed3b74384d6b83448f7 Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 2 Feb 2022 19:50:08 +0200 Subject: [PATCH 151/479] Remove unnecessary "setState". --- .../client_app/flutter_text_property_widget.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart b/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart index a3b1d51..c8b6230 100644 --- a/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart +++ b/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart @@ -29,6 +29,7 @@ class _FlutterTextPropertyWidgetState extends State { ], ); } + final colors = [ Colors.black, Colors.pink.shade600, @@ -47,16 +48,16 @@ class _FlutterTextPropertyWidgetState extends State { ); _nextColorEvent = widget.classicApp.events.subscribe( - (NextTextColorEvent e) => setState(() { - final currColor = widget.classicApp.textColoring.textColor; - var nextIndex = colors.indexOf(currColor)+1; + (NextTextColorEvent e) { + final currColor = widget.classicApp.textColoring.textColor; + var nextIndex = colors.indexOf(currColor) + 1; if (nextIndex >= colors.length) { nextIndex = 0; } widget.classicApp.textColoring.textColor = colors[nextIndex]; - }), + }, ); super.initState(); } From a91460040376983ed0c16bd0969fd3bfe3d60df0 Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 2 Feb 2022 19:50:39 +0200 Subject: [PATCH 152/479] Remove unnecessary "print". --- patterns/adapter/flutter_adapter/client_app/text_coloring.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/adapter/flutter_adapter/client_app/text_coloring.dart b/patterns/adapter/flutter_adapter/client_app/text_coloring.dart index c4f772e..0e24d28 100644 --- a/patterns/adapter/flutter_adapter/client_app/text_coloring.dart +++ b/patterns/adapter/flutter_adapter/client_app/text_coloring.dart @@ -30,7 +30,7 @@ class TextColoring { if (_textColor == newColor) { return; } - print(newColor); + _textColor = newColor; app.repaint(); } From 23245208a04ba0f9d09cc6e7e013881d45bd1710 Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 2 Feb 2022 19:59:16 +0200 Subject: [PATCH 153/479] Make TextColoring properties to more short. --- .../flutter_adapter/client_app/app.dart | 2 +- .../client_app/text_coloring.dart | 32 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/patterns/adapter/flutter_adapter/client_app/app.dart b/patterns/adapter/flutter_adapter/client_app/app.dart index 0167a3e..72534df 100644 --- a/patterns/adapter/flutter_adapter/client_app/app.dart +++ b/patterns/adapter/flutter_adapter/client_app/app.dart @@ -15,7 +15,7 @@ class App extends ClassicAppBase { @override void onWheel(double deltaX, double deltaY) { - textColoring.textSize += deltaY ~/ 10; + textColoring.size += deltaY ~/ 10; } @override diff --git a/patterns/adapter/flutter_adapter/client_app/text_coloring.dart b/patterns/adapter/flutter_adapter/client_app/text_coloring.dart index 0e24d28..d1a2e8c 100644 --- a/patterns/adapter/flutter_adapter/client_app/text_coloring.dart +++ b/patterns/adapter/flutter_adapter/client_app/text_coloring.dart @@ -7,53 +7,53 @@ class TextColoring { TextColoring(this.app); - final maxTextSize = 150; + final maxTextSize = 200; - var _textSize = 50; + var _size = 50; - int get textSize => _textSize; + int get size => _size; - set textSize(int newVal) { - if (newVal == _textSize || newVal > maxTextSize || newVal < 1) { + set size(int newVal) { + if (newVal == _size || newVal > maxTextSize || newVal < 1) { return; } - _textSize = newVal; + _size = newVal; app.repaint(); } - var _textColor = Color(0xffd81b60); + var _color = Color(0xffd81b60); - Color get textColor => _textColor; + Color get color => _color; - set textColor(Color newColor) { - if (_textColor == newColor) { + set color(Color newColor) { + if (_color == newColor) { return; } - _textColor = newColor; + _color = newColor; app.repaint(); } - void paint(String text, Canvas canvas, Size size) { + void paint(String text, Canvas canvas, Size canvasSize) { final builder = ParagraphBuilder( ParagraphStyle( textAlign: TextAlign.center, - fontSize: _textSize.toDouble(), + fontSize: _size.toDouble(), ), ) ..pushStyle( TextStyle( fontFamily: 'Arial', - color: _textColor, + color: _color, ), ) ..addText(text); final paragraph = builder.build() - ..layout(ParagraphConstraints(width: size.width)); + ..layout(ParagraphConstraints(width: canvasSize.width)); - final centerPos = Offset(0, (size.height - paragraph.height) / 2); + final centerPos = Offset(0, (canvasSize.height - paragraph.height) / 2); canvas.drawParagraph(paragraph, centerPos); } } From bc024746fbba8ab4e397b32baffbe0afd2bd07be Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 2 Feb 2022 19:59:54 +0200 Subject: [PATCH 154/479] Add clip content. --- .../flutter_adapter/adapter/classic_app_render_object.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart b/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart index d3241b3..aa7fe8f 100644 --- a/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart +++ b/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart @@ -24,6 +24,7 @@ class ClassicAppRenderObject extends RenderBox { @override void paint(PaintingContext context, Offset offset) { context.canvas.translate(offset.dx, offset.dy); + context.canvas.clipRect(offset & size); _classicApp.onPaint(context.canvas, size); } From 0ec80da460c9ac675dad66b489e238a21297c83c Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 2 Feb 2022 20:01:42 +0200 Subject: [PATCH 155/479] Add material namespace & make names shorten. --- .../flutter_text_property_widget.dart | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart b/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart index c8b6230..f96dc20 100644 --- a/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart +++ b/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart @@ -1,4 +1,5 @@ -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' as material; +import 'package:flutter/widgets.dart'; import '../../../observer/app_observer/observer/app_observer.dart'; import '../adapter/classic_app.dart'; @@ -31,11 +32,11 @@ class _FlutterTextPropertyWidgetState extends State { } final colors = [ - Colors.black, - Colors.pink.shade600, - Colors.deepPurple.shade600, - Colors.blue.shade600, - Colors.green.shade600, + material.Colors.black, + material.Colors.pink.shade600, + material.Colors.deepPurple.shade600, + material.Colors.blue.shade600, + material.Colors.green.shade600, ]; late EventFunction _classicAppRepaintEvent; @@ -48,16 +49,16 @@ class _FlutterTextPropertyWidgetState extends State { ); _nextColorEvent = widget.classicApp.events.subscribe( - (NextTextColorEvent e) { - final currColor = widget.classicApp.textColoring.textColor; + (NextTextColorEvent e) => setState(() { + final currColor = widget.classicApp.textColoring.color; var nextIndex = colors.indexOf(currColor) + 1; if (nextIndex >= colors.length) { nextIndex = 0; } - widget.classicApp.textColoring.textColor = colors[nextIndex]; - }, + widget.classicApp.textColoring.color = colors[nextIndex]; + }), ); super.initState(); } @@ -76,18 +77,18 @@ class _FlutterTextPropertyWidgetState extends State { SizedBox( width: 31, child: Text( - app.textColoring.textSize.toString(), + app.textColoring.size.toString(), textAlign: TextAlign.right, ), ), SizedBox( width: 200, - child: Slider( - value: app.textColoring.textSize.toDouble(), + child: material.Slider( + value: app.textColoring.size.toDouble(), max: app.textColoring.maxTextSize.toDouble(), min: 1, onChanged: (newVal) { - app.textColoring.textSize = newVal.toInt(); + app.textColoring.size = newVal.toInt(); }, ), ), @@ -108,10 +109,10 @@ class _FlutterTextPropertyWidgetState extends State { } Widget _buildColorButton(Color color, App app) { - final isColorSelect = (color == app.textColoring.textColor); + final isColorSelect = (color == app.textColoring.color); return GestureDetector( onTap: () { - app.textColoring.textColor = color; + app.textColoring.color = color; }, child: Container( width: 20, @@ -127,7 +128,7 @@ class _FlutterTextPropertyWidgetState extends State { child: Container( width: 4, height: 4, - color: Colors.white.withOpacity(0.8), + color: material.Colors.white.withOpacity(0.8), ), ); } From 20d0b76695951de739251b17d70d01311b314b64 Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 2 Feb 2022 20:04:23 +0200 Subject: [PATCH 156/479] Rename "FlutterTextPropertyWidget" to "TextPropertyWidget" --- ..._property_widget.dart => text_property_widget.dart} | 10 +++++----- patterns/adapter/flutter_adapter/main.dart | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) rename patterns/adapter/flutter_adapter/client_app/{flutter_text_property_widget.dart => text_property_widget.dart} (91%) diff --git a/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart b/patterns/adapter/flutter_adapter/client_app/text_property_widget.dart similarity index 91% rename from patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart rename to patterns/adapter/flutter_adapter/client_app/text_property_widget.dart index f96dc20..bd37867 100644 --- a/patterns/adapter/flutter_adapter/client_app/flutter_text_property_widget.dart +++ b/patterns/adapter/flutter_adapter/client_app/text_property_widget.dart @@ -5,20 +5,20 @@ import '../../../observer/app_observer/observer/app_observer.dart'; import '../adapter/classic_app.dart'; import 'app.dart'; -class FlutterTextPropertyWidget extends StatefulWidget { +class TextPropertyWidget extends StatefulWidget { final App classicApp; - const FlutterTextPropertyWidget({ + const TextPropertyWidget({ Key? key, required this.classicApp, }) : super(key: key); @override - State createState() => - _FlutterTextPropertyWidgetState(); + State createState() => + _TextPropertyWidgetState(); } -class _FlutterTextPropertyWidgetState extends State { +class _TextPropertyWidgetState extends State { @override Widget build(BuildContext context) { final app = widget.classicApp; diff --git a/patterns/adapter/flutter_adapter/main.dart b/patterns/adapter/flutter_adapter/main.dart index 736d280..06e043f 100644 --- a/patterns/adapter/flutter_adapter/main.dart +++ b/patterns/adapter/flutter_adapter/main.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'adapter/classic_app_widget_adapter.dart'; import 'client_app/app.dart'; -import 'client_app/flutter_text_property_widget.dart'; +import 'client_app/text_property_widget.dart'; class FlutterAdapterApp extends StatefulWidget { @override @@ -16,7 +16,7 @@ class _FlutterAdapterAppState extends State { Widget build(BuildContext context) { return Column( children: [ - FlutterTextPropertyWidget( + TextPropertyWidget( classicApp: app, ), Expanded( From 9850266cf7ce900b8737264f0c06da666ab165ff Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 3 Feb 2022 10:50:08 +0200 Subject: [PATCH 157/479] Add "SubscriberWidget" to "TextPropertyWidget". --- .../client_app/text_property_widget.dart | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/patterns/adapter/flutter_adapter/client_app/text_property_widget.dart b/patterns/adapter/flutter_adapter/client_app/text_property_widget.dart index bd37867..18bc774 100644 --- a/patterns/adapter/flutter_adapter/client_app/text_property_widget.dart +++ b/patterns/adapter/flutter_adapter/client_app/text_property_widget.dart @@ -3,6 +3,7 @@ import 'package:flutter/widgets.dart'; import '../../../observer/app_observer/observer/app_observer.dart'; import '../adapter/classic_app.dart'; +import '../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; import 'app.dart'; class TextPropertyWidget extends StatefulWidget { @@ -14,20 +15,24 @@ class TextPropertyWidget extends StatefulWidget { }) : super(key: key); @override - State createState() => - _TextPropertyWidgetState(); + State createState() => _TextPropertyWidgetState(); } class _TextPropertyWidgetState extends State { @override Widget build(BuildContext context) { final app = widget.classicApp; - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _buildTextSizeSlider(app), - _buildTextColorButtons(app), - ], + return SubscriberWidget( + observer: app.events, + builder: (context, event) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildTextSizeSlider(app), + _buildTextColorButtons(app), + ], + ); + }, ); } @@ -44,9 +49,9 @@ class _TextPropertyWidgetState extends State { @override void initState() { - _classicAppRepaintEvent = widget.classicApp.events.subscribe( - (ClassicAppRepaint e) => setState(() {}), - ); + // _classicAppRepaintEvent = widget.classicApp.events.subscribe( + // (ClassicAppRepaint e) => setState(() {}), + // ); _nextColorEvent = widget.classicApp.events.subscribe( (NextTextColorEvent e) => setState(() { @@ -60,6 +65,7 @@ class _TextPropertyWidgetState extends State { widget.classicApp.textColoring.color = colors[nextIndex]; }), ); + super.initState(); } From c9f124a8481ef3cdc29258c2010670fc42a4388c Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 3 Feb 2022 13:02:09 +0200 Subject: [PATCH 158/479] Add "Observer" example. Subscriber Flutter Widget. --- bin/main.dart | 12 ++--- .../events/new_hash_event.dart | 7 +++ .../subscriber_flutter_widget/main.dart | 28 ++++++++++++ .../subscriber/subscriber_widget.dart | 45 +++++++++++++++++++ .../widgets/hash_generator_widget.dart | 26 +++++++++++ .../widgets/hash_user_widget.dart | 23 ++++++++++ 6 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 patterns/observer/subscriber_flutter_widget/events/new_hash_event.dart create mode 100644 patterns/observer/subscriber_flutter_widget/main.dart create mode 100644 patterns/observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart create mode 100644 patterns/observer/subscriber_flutter_widget/widgets/hash_generator_widget.dart create mode 100644 patterns/observer/subscriber_flutter_widget/widgets/hash_user_widget.dart diff --git a/bin/main.dart b/bin/main.dart index 3865bb7..cff5656 100644 --- a/bin/main.dart +++ b/bin/main.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; +import '../patterns/observer/subscriber_flutter_widget/main.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { @@ -11,10 +12,11 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( title: 'Refactoring Guru: Flutter launcher', - theme: ThemeData( - primarySwatch: Colors.pink - ), - home: Container(), + theme: ThemeData(primarySwatch: Colors.pink), + initialRoute: '/observer/subscriber_flutter_widget', + routes: { + '/observer/subscriber_flutter_widget': (_) => SubscriberFlutterApp(), + }, ); } } diff --git a/patterns/observer/subscriber_flutter_widget/events/new_hash_event.dart b/patterns/observer/subscriber_flutter_widget/events/new_hash_event.dart new file mode 100644 index 0000000..0cbf2f5 --- /dev/null +++ b/patterns/observer/subscriber_flutter_widget/events/new_hash_event.dart @@ -0,0 +1,7 @@ +import '../../app_observer/observer/event.dart'; + +class NewHashEvent extends Event { + final String newHash; + + NewHashEvent(this.newHash); +} diff --git a/patterns/observer/subscriber_flutter_widget/main.dart b/patterns/observer/subscriber_flutter_widget/main.dart new file mode 100644 index 0000000..87f7e35 --- /dev/null +++ b/patterns/observer/subscriber_flutter_widget/main.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import '../app_observer/observer/app_observer.dart'; +import 'widgets/hash_generator_widget.dart'; +import 'widgets/hash_user_widget.dart'; + +class SubscriberFlutterApp extends StatefulWidget { + @override + State createState() => _SubscriberFlutterAppState(); +} + +class _SubscriberFlutterAppState extends State { + final observer = AppObserver(); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + HashUserWidget(observer: observer), + HashGeneratorWidget(observer: observer), + ], + ), + ), + ); + } +} diff --git a/patterns/observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart b/patterns/observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart new file mode 100644 index 0000000..f108cf1 --- /dev/null +++ b/patterns/observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart @@ -0,0 +1,45 @@ +import 'package:flutter/widgets.dart'; + +import '../../app_observer/observer/app_observer.dart'; +import '../../app_observer/observer/event.dart'; + +class SubscriberWidget extends StatefulWidget { + final AppObserver observer; + final Widget Function(BuildContext buildContext, T? event) builder; + + const SubscriberWidget({ + Key? key, + required this.builder, + required this.observer, + }) : super(key: key); + + @override + State> createState() { + return _SubscriberWidgetState(); + } +} + +class _SubscriberWidgetState + extends State> { + T? _event; + late EventFunction _saveSubscriber; + + @override + void initState() { + _saveSubscriber = widget.observer.subscribe( + (event) => setState(() => _event = event), + ); + super.initState(); + } + + @override + void dispose() { + widget.observer.unsubscribe(_saveSubscriber); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return widget.builder(context, _event); + } +} diff --git a/patterns/observer/subscriber_flutter_widget/widgets/hash_generator_widget.dart b/patterns/observer/subscriber_flutter_widget/widgets/hash_generator_widget.dart new file mode 100644 index 0000000..cfdd2e7 --- /dev/null +++ b/patterns/observer/subscriber_flutter_widget/widgets/hash_generator_widget.dart @@ -0,0 +1,26 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +import '../../app_observer/observer/app_observer.dart'; +import '../events/new_hash_event.dart'; + +class HashGeneratorWidget extends StatelessWidget { + final AppObserver observer; + + const HashGeneratorWidget({ + Key? key, + required this.observer, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ElevatedButton( + child: Text('Generate new hash'), + onPressed: () { + final hash = Random().nextDouble().hashCode.toString(); + observer.notify(NewHashEvent(hash)); + }, + ); + } +} diff --git a/patterns/observer/subscriber_flutter_widget/widgets/hash_user_widget.dart b/patterns/observer/subscriber_flutter_widget/widgets/hash_user_widget.dart new file mode 100644 index 0000000..04191bc --- /dev/null +++ b/patterns/observer/subscriber_flutter_widget/widgets/hash_user_widget.dart @@ -0,0 +1,23 @@ +import 'package:flutter/widgets.dart'; + +import '../../app_observer/observer/app_observer.dart'; +import '../events/new_hash_event.dart'; +import '../subscriber/subscriber_widget.dart'; + +class HashUserWidget extends StatelessWidget { + final AppObserver observer; + + const HashUserWidget({Key? key, required this.observer}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SubscriberWidget( + observer: observer, + builder: (context, event) { + return Text( + (event == null) ? 'Hash no generated' : event.newHash, + ); + }, + ); + } +} From 3a04bd7833d1b9ce714439386cc0d51e281a2404 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 3 Feb 2022 16:43:51 +0200 Subject: [PATCH 159/479] Add README. --- .../subscriber_flutter_widget/README.md | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 patterns/observer/subscriber_flutter_widget/README.md diff --git a/patterns/observer/subscriber_flutter_widget/README.md b/patterns/observer/subscriber_flutter_widget/README.md new file mode 100644 index 0000000..4b5cb25 --- /dev/null +++ b/patterns/observer/subscriber_flutter_widget/README.md @@ -0,0 +1,46 @@ +# 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. + +Tutorial: [here](https://refactoring.guru/design-patterns/observer). + +## Subscriber Flutter Widget example +This is a complex example of an Observer pattern, connected to a Flutter application. The example includes, +the previously implemented AppObserver template, which you can see +[here](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer) + +[Online demo](https://ilopx.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget) + +### Diagram: +??? + +### Sequence +??? + +### Client code: +```dart +void main() { + // widget that generates a new event + ElevatedButton( + child: Text('Generate new hash'), + onPressed: () { + final hash = Random().nextDouble().hashCode.toString(); + observer.notify(NewHashEvent(hash)); + }, + ); + + // widget event consumer NewHashEvent + SubscriberWidget( + observer: observer, + builder: (context, event) { + return Text( + (event == null) ? 'Hash no generated' : event.newHash, + ); + }, + ); +} + +``` + +**Output:** +[![image](https://user-images.githubusercontent.com/8049534/152333741-e289b96d-9d86-4f19-8fd5-8d14e374f523.png)](https://ilopx.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget) From a5826ac693c604dfe0941310f7abe8e069d24284 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 3 Feb 2022 16:48:15 +0200 Subject: [PATCH 160/479] Add base path to index.html. --- web/index.html | 208 +++++++++++++++++++++++++++++-------------------- 1 file changed, 122 insertions(+), 86 deletions(-) diff --git a/web/index.html b/web/index.html index 0e76fb2..d2d6493 100644 --- a/web/index.html +++ b/web/index.html @@ -1,104 +1,140 @@ - + + + + - This is a placeholder for base href that will be replaced by the value of - the `--base-href` argument provided to `flutter build`. - --> - + + - - - + flutter_launcher + + - - + }); + + // If service worker doesn't succeed in a reasonable amount of time, + // fallback to plaint + From 3fe43ea455939cd6503918b5650d3769043c15c8 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 3 Feb 2022 16:48:39 +0200 Subject: [PATCH 161/479] Add logo to web page. --- web/logo-screen.png | Bin 0 -> 64655 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 web/logo-screen.png diff --git a/web/logo-screen.png b/web/logo-screen.png new file mode 100644 index 0000000000000000000000000000000000000000..d5c9c2f475efa16e0f0008be9dc0be7951042477 GIT binary patch literal 64655 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 literal 0 HcmV?d00001 From 7a85e62dbb40ccde0a453e050aa5ad74d64e5856 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 3 Feb 2022 17:07:43 +0200 Subject: [PATCH 162/479] Add "TODO" markers to README. --- patterns/observer/subscriber_flutter_widget/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patterns/observer/subscriber_flutter_widget/README.md b/patterns/observer/subscriber_flutter_widget/README.md index 4b5cb25..159c577 100644 --- a/patterns/observer/subscriber_flutter_widget/README.md +++ b/patterns/observer/subscriber_flutter_widget/README.md @@ -12,10 +12,10 @@ the previously implemented AppObserver template, which you can see [Online demo](https://ilopx.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget) ### Diagram: -??? +TODO: ??? ### Sequence -??? +TODO: ??? ### Client code: ```dart From a56561dd51dbf381767241ba375a989633c716b5 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 3 Feb 2022 22:06:58 +0200 Subject: [PATCH 163/479] Add diagram to README. --- .../subscriber_flutter_widget/README.md | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/patterns/observer/subscriber_flutter_widget/README.md b/patterns/observer/subscriber_flutter_widget/README.md index 159c577..71ca3b9 100644 --- a/patterns/observer/subscriber_flutter_widget/README.md +++ b/patterns/observer/subscriber_flutter_widget/README.md @@ -1,21 +1,18 @@ # Observer pattern -Observer is a behavioral design pattern that lets you define a subscription mechanism to notify +**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. Tutorial: [here](https://refactoring.guru/design-patterns/observer). ## Subscriber Flutter Widget example This is a complex example of an Observer pattern, connected to a Flutter application. The example includes, -the previously implemented AppObserver template, which you can see -[here](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer) - -[Online demo](https://ilopx.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget) +the previously implemented **AppObserver** example pattern, which you can see [here](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer). ### Diagram: -TODO: ??? +![image](https://user-images.githubusercontent.com/8049534/152418114-f040bcb4-3bf5-4581-8b9b-a264e103ff76.png) ### Sequence -TODO: ??? +![image](https://user-images.githubusercontent.com/8049534/152418299-3f6f3e5d-7132-42bc-9b27-11ed53ca6434.png) ### Client code: ```dart @@ -24,7 +21,7 @@ void main() { ElevatedButton( child: Text('Generate new hash'), onPressed: () { - final hash = Random().nextDouble().hashCode.toString(); + final hash = 'some hash'; observer.notify(NewHashEvent(hash)); }, ); @@ -34,13 +31,14 @@ void main() { observer: observer, builder: (context, event) { return Text( - (event == null) ? 'Hash no generated' : event.newHash, + event?.newHash ?? 'Hash no generated' , ); }, ); } - ``` -**Output:** -[![image](https://user-images.githubusercontent.com/8049534/152333741-e289b96d-9d86-4f19-8fd5-8d14e374f523.png)](https://ilopx.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget) +### Online demo: +Click on the picture to see a [demo](https://ilopx.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget). + +[![image](https://user-images.githubusercontent.com/8049534/152419178-f40a07fd-728d-4f99-befa-0935bbdd7b71.png)](https://ilopx.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget) From 47bd63da526e9339f2528d2f2e6901d7b8009613 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 3 Feb 2022 22:08:04 +0200 Subject: [PATCH 164/479] Move "onHashGenerate" to separate method. --- .../widgets/hash_generator_widget.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/patterns/observer/subscriber_flutter_widget/widgets/hash_generator_widget.dart b/patterns/observer/subscriber_flutter_widget/widgets/hash_generator_widget.dart index cfdd2e7..e19caa2 100644 --- a/patterns/observer/subscriber_flutter_widget/widgets/hash_generator_widget.dart +++ b/patterns/observer/subscriber_flutter_widget/widgets/hash_generator_widget.dart @@ -17,10 +17,12 @@ class HashGeneratorWidget extends StatelessWidget { Widget build(BuildContext context) { return ElevatedButton( child: Text('Generate new hash'), - onPressed: () { - final hash = Random().nextDouble().hashCode.toString(); - observer.notify(NewHashEvent(hash)); - }, + onPressed: onHashGenerate, ); } + + void onHashGenerate() { + final hash = Random().nextDouble().hashCode.toString(); + observer.notify(NewHashEvent(hash)); + } } From 87090ba24d783150bd07555e9892b8c5a8a51dae Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 3 Feb 2022 22:08:41 +0200 Subject: [PATCH 165/479] Replace ternary operator. --- .../subscriber_flutter_widget/widgets/hash_user_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/observer/subscriber_flutter_widget/widgets/hash_user_widget.dart b/patterns/observer/subscriber_flutter_widget/widgets/hash_user_widget.dart index 04191bc..0b6458c 100644 --- a/patterns/observer/subscriber_flutter_widget/widgets/hash_user_widget.dart +++ b/patterns/observer/subscriber_flutter_widget/widgets/hash_user_widget.dart @@ -15,7 +15,7 @@ class HashUserWidget extends StatelessWidget { observer: observer, builder: (context, event) { return Text( - (event == null) ? 'Hash no generated' : event.newHash, + event?.newHash ?? 'Hash no generated', ); }, ); From 5eddb272e76b7ba846fcb26737f063c8e66fd092 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 3 Feb 2022 22:10:15 +0200 Subject: [PATCH 166/479] Create "SubscribeWidget.update" & move subscriber to separate method. --- .../subscriber/subscriber_widget.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/patterns/observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart b/patterns/observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart index f108cf1..daef892 100644 --- a/patterns/observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart +++ b/patterns/observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart @@ -22,22 +22,23 @@ class SubscriberWidget extends StatefulWidget { class _SubscriberWidgetState extends State> { T? _event; - late EventFunction _saveSubscriber; @override void initState() { - _saveSubscriber = widget.observer.subscribe( - (event) => setState(() => _event = event), - ); + widget.observer.subscribe(update); super.initState(); } @override void dispose() { - widget.observer.unsubscribe(_saveSubscriber); + widget.observer.unsubscribe(update); super.dispose(); } + void update(T event) { + setState(() => _event = event); + } + @override Widget build(BuildContext context) { return widget.builder(context, _event); From 6b05f198e7f1580bc6b0272faf6b68ca00f5acd1 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 3 Feb 2022 22:21:51 +0200 Subject: [PATCH 167/479] Bump version 0.16.0 & update main README. --- CHANGELOG.md | 4 ++++ README.md | 9 ++++++++- pubspec.yaml | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52b67b9..038fa50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.16.0 +- Add complex example of an Observer pattern, connected to a Flutter application. +- Add new branch "web-demos" for online examples. + ## 0.15.0 - Add second "Observer" example. This example was created to be used in a more complex example. diff --git a/README.md b/README.md index c7cbbda..3e1ca32 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) - [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)] [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_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)] [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer)] [[Subscriber Flutter Widget](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] - [ ] [**State**](https://refactoring.guru/design-patterns/state) - [ ] [**Template Method**](https://refactoring.guru/design-patterns/template-method) - [ ] [**Visitor**](https://refactoring.guru/design-patterns/visitor) @@ -33,6 +33,7 @@ It contains **Dart** examples for all classic **GoF** design patterns. ## Requirements The examples were written in **Dart 2.15**. +Some complex examples require **Flutter 2.15.0**. ## Contributor's Guide @@ -65,6 +66,12 @@ Here's a style guide which might help you to keep your changes consistent with o 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. +### Build Flutter examples +```batch +cd root directory +flutter build web -t bin\main.dart --web-renderer html +``` + ## License This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. diff --git a/pubspec.yaml b/pubspec.yaml index c516e54..fcabe61 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.15.0 +version: 0.16.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 1da56af3041e8f1ce052f9c758ce9729313586df Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 4 Feb 2022 14:05:25 +0200 Subject: [PATCH 168/479] Add web-demos deploy-script. --- bin/depoly_flutter_demos.dart | 87 +++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 bin/depoly_flutter_demos.dart diff --git a/bin/depoly_flutter_demos.dart b/bin/depoly_flutter_demos.dart new file mode 100644 index 0000000..0409174 --- /dev/null +++ b/bin/depoly_flutter_demos.dart @@ -0,0 +1,87 @@ +import 'dart:io'; + +void main() async { + buildWebProject(); + cloneRemoteRepository(); + updateFiles(); + pushToOrigin(); + clear(); +} + +void pushToOrigin() { + cmd('git add .', tmpDir); + cmd('git commit -m \'auto_commit\'', tmpDir, showOut: true); + cmd('git push origin web-demos', tmpDir, showOut: true); +} + +void updateFiles() { + final webDir = Directory(thisPath(r'..\build\web\')); + copyDirectory(webDir, tmpDir); +} + +void cloneRemoteRepository() { + final repository = 'https://github.com/ilopX/design-patterns-dart.git'; + cmd( + 'git clone -b web-demos --single-branch $repository .', + tmpDir, + showOut: true, + ); +} + +late final tmpDir = Directory.systemTemp.createTempSync(); + +void clear() { + tmpDir.deleteSync(recursive: true); +} + +void buildWebProject() { + print('Build web app ...'); + final projectPath = thisPath(r'..\'); + cmd( + r'flutter build web -t bin\main.dart --web-renderer html', + Directory(projectPath), + ); +} + +void cmd(String command, Directory workingDirectory, {bool showOut = false}) { + var process = Process.runSync( + command, + [], + workingDirectory: workingDirectory.path, + runInShell: true, + ); + + if (showOut) { + print(process.stdout); + } + + if (process.exitCode != 0) { + print(process.stderr); + exit(process.exitCode); + } +} + +String thisPath(String name) => + Platform.script.pathSegments + .sublist(0, Platform.script.pathSegments.length - 1) + .join(Platform.pathSeparator) + + Platform.pathSeparator + + name; + +void copyDirectory(Directory source, Directory target) { + for (final entry in source.listSync()) { + final newFilePath = entry.path.replaceFirst( + source.path, + target.path + Platform.pathSeparator, + ); + + if (entry is File) { + entry.copySync(newFilePath); + } else if (entry is Directory) { + final target = Directory(newFilePath)..createSync(recursive: true); + copyDirectory(entry, target); + } else { + throw Error(); + } + } +} From b8266478eb3d27c4864a6d95073f0e6eeda6f294 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 4 Feb 2022 14:07:43 +0200 Subject: [PATCH 169/479] Update main README. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 3e1ca32..c254256 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,11 @@ cd root directory flutter build web -t bin\main.dart --web-renderer html ``` +### Deploy Flutter examples +```batch +dart bin\depoly_flutter_demos.dart +``` + ## License This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. From 88334c2251c8c28d80e8963ce779deae9d5fae0b Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 4 Feb 2022 14:26:22 +0200 Subject: [PATCH 170/479] Add adapt origin repository. --- bin/depoly_flutter_demos.dart | 58 +++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/bin/depoly_flutter_demos.dart b/bin/depoly_flutter_demos.dart index 0409174..69265c6 100644 --- a/bin/depoly_flutter_demos.dart +++ b/bin/depoly_flutter_demos.dart @@ -8,19 +8,17 @@ void main() async { clear(); } -void pushToOrigin() { - cmd('git add .', tmpDir); - cmd('git commit -m \'auto_commit\'', tmpDir, showOut: true); - cmd('git push origin web-demos', tmpDir, showOut: true); -} - -void updateFiles() { - final webDir = Directory(thisPath(r'..\build\web\')); - copyDirectory(webDir, tmpDir); +void buildWebProject() { + print('Build web app ...'); + final projectPath = thisPath(r'..\'); + cmd( + r'flutter build web -t bin\main.dart --web-renderer html', + Directory(projectPath), + ); } void cloneRemoteRepository() { - final repository = 'https://github.com/ilopX/design-patterns-dart.git'; + final repository = originRemoteUrl(); cmd( 'git clone -b web-demos --single-branch $repository .', tmpDir, @@ -28,22 +26,24 @@ void cloneRemoteRepository() { ); } +void updateFiles() { + final webDir = Directory(thisPath(r'..\build\web\')); + copyDirectory(webDir, tmpDir); +} + +void pushToOrigin() { + cmd('git add .', tmpDir); + cmd('git commit -m \'auto_commit\'', tmpDir, showOut: true); + cmd('git push origin web-demos', tmpDir, showOut: true); +} + late final tmpDir = Directory.systemTemp.createTempSync(); void clear() { tmpDir.deleteSync(recursive: true); } -void buildWebProject() { - print('Build web app ...'); - final projectPath = thisPath(r'..\'); - cmd( - r'flutter build web -t bin\main.dart --web-renderer html', - Directory(projectPath), - ); -} - -void cmd(String command, Directory workingDirectory, {bool showOut = false}) { +String cmd(String command, Directory workingDirectory, {bool showOut = false}) { var process = Process.runSync( command, [], @@ -57,8 +57,26 @@ void cmd(String command, Directory workingDirectory, {bool showOut = false}) { if (process.exitCode != 0) { print(process.stderr); + clear(); exit(process.exitCode); } + + return process.stdout; +} + +String originRemoteUrl() { + final raw = cmd( + 'git remote show origin', + Directory(thisPath(r'..\')), + showOut: true, + ); + final url = RegExp('Push URL: (.+)\n').firstMatch(raw)?.group(1); + + if (url == null) { + throw Exception('Empty Remote repository'); + } + + return url; } String thisPath(String name) => From 70405f5c41c3355fee35d7158466116bbd70aa5a Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 4 Feb 2022 14:31:11 +0200 Subject: [PATCH 171/479] Add background to "FlutterAdapterApp". --- patterns/adapter/flutter_adapter/main.dart | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/patterns/adapter/flutter_adapter/main.dart b/patterns/adapter/flutter_adapter/main.dart index 06e043f..2eaa997 100644 --- a/patterns/adapter/flutter_adapter/main.dart +++ b/patterns/adapter/flutter_adapter/main.dart @@ -14,17 +14,19 @@ class _FlutterAdapterAppState extends State { @override Widget build(BuildContext context) { - return Column( - children: [ - TextPropertyWidget( - classicApp: app, - ), - Expanded( - child: ClassicAppWidgetAdapter( + return Scaffold( + body: Column( + children: [ + TextPropertyWidget( classicApp: app, ), - ), - ], + Expanded( + child: ClassicAppWidgetAdapter( + classicApp: app, + ), + ), + ], + ), ); } } From 475a21fa3365c341b83a79fd5019bacd4a54dd25 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 4 Feb 2022 20:47:31 +0200 Subject: [PATCH 172/479] Fetch to upstream & refactoring. --- bin/depoly_flutter_demos.dart | 101 +++++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 27 deletions(-) diff --git a/bin/depoly_flutter_demos.dart b/bin/depoly_flutter_demos.dart index 69265c6..5035a0b 100644 --- a/bin/depoly_flutter_demos.dart +++ b/bin/depoly_flutter_demos.dart @@ -1,45 +1,62 @@ import 'dart:io'; void main() async { + init(); buildWebProject(); - cloneRemoteRepository(); - updateFiles(); + cloneOriginRepository(); + fetchUpstream(); + copyFiles(); pushToOrigin(); clear(); } +void init() { + print('Use temp $tmpDir'); +} + void buildWebProject() { - print('Build web app ...'); - final projectPath = thisPath(r'..\'); + print('Build web app.'); + final projectPath = projectDir; cmd( r'flutter build web -t bin\main.dart --web-renderer html', - Directory(projectPath), + projectPath, ); } -void cloneRemoteRepository() { +void cloneOriginRepository() { final repository = originRemoteUrl(); + print('Cloning origin: $repository'); cmd( 'git clone -b web-demos --single-branch $repository .', tmpDir, - showOut: true, ); } -void updateFiles() { - final webDir = Directory(thisPath(r'..\build\web\')); - copyDirectory(webDir, tmpDir); +void fetchUpstream() { + final shvets = r'https://github.com/RefactoringGuru/design-patterns-dart.git'; + print('Fetch upstream: $shvets'); + cmd('git remote add upstream $shvets', tmpDir); + cmd('git fetch upstream', tmpDir); +} + +void copyFiles() { + print('Copy files:'); + copyDirectory(webBuildDir, tmpDir); } void pushToOrigin() { cmd('git add .', tmpDir); - cmd('git commit -m \'auto_commit\'', tmpDir, showOut: true); + cmd('git commit -m ${lastProjectCommit()}', tmpDir, showOut: true); + print('Push to origin:'); cmd('git push origin web-demos', tmpDir, showOut: true); } late final tmpDir = Directory.systemTemp.createTempSync(); +late final projectDir = thisPath(r'..\'); +late final webBuildDir = Directory(projectDir.uri.toFilePath() + r'build\web'); void clear() { + print('Clear $tmpDir'); tmpDir.deleteSync(recursive: true); } @@ -56,6 +73,7 @@ String cmd(String command, Directory workingDirectory, {bool showOut = false}) { } if (process.exitCode != 0) { + print(command); print(process.stderr); clear(); exit(process.exitCode); @@ -67,8 +85,7 @@ String cmd(String command, Directory workingDirectory, {bool showOut = false}) { String originRemoteUrl() { final raw = cmd( 'git remote show origin', - Directory(thisPath(r'..\')), - showOut: true, + projectDir, ); final url = RegExp('Push URL: (.+)\n').firstMatch(raw)?.group(1); @@ -79,27 +96,57 @@ String originRemoteUrl() { return url; } -String thisPath(String name) => - Platform.script.pathSegments - .sublist(0, Platform.script.pathSegments.length - 1) - .join(Platform.pathSeparator) + - Platform.pathSeparator + - name; +String lastProjectCommit() { + final rawCommit = cmd('git log -1 --pretty=%B', projectDir); + final formatCommit = rawCommit.replaceAll(' ', '_'); + return 'auto_commit:_$formatCommit'; +} + +Directory thisPath(String name) { + final dir = Platform.script.pathSegments + .sublist(0, Platform.script.pathSegments.length - 1) + .join(Platform.pathSeparator) + + Platform.pathSeparator + + name; + + return Directory(Uri.directory(dir).normalizePath().toFilePath()); +} void copyDirectory(Directory source, Directory target) { + if (!target.existsSync()) { + target.createSync(); + } + for (final entry in source.listSync()) { - final newFilePath = entry.path.replaceFirst( - source.path, - target.path + Platform.pathSeparator, - ); + final newPath = updatePath(entry, source, target); if (entry is File) { - entry.copySync(newFilePath); + copyFile(entry, newPath); } else if (entry is Directory) { - final target = Directory(newFilePath)..createSync(recursive: true); - copyDirectory(entry, target); + copyDirectory(entry, Directory(newPath)); } else { - throw Error(); + throw Exception( + 'FileSystemEntity is not recognized. It must be a file or a directory', + ); } } } + +void copyFile(File entry, String newFileName) { + print('\t${removerBuildPath(entry)}'); + entry.copySync(newFileName); +} + +String updatePath( + FileSystemEntity entry, + Directory source, + Directory target, +) { + return entry.path + .replaceFirst(source.path, target.path + Platform.pathSeparator) + .replaceAll(r'\\', r'\'); +} + +String removerBuildPath(FileSystemEntity target) { + return target.path.replaceFirst(webBuildDir.path, '').substring(1); +} From 8e36aa31d5929474bc9392a18881614919b102b9 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 5 Feb 2022 11:01:39 +0200 Subject: [PATCH 173/479] Add deploy flutter demos script. --- bin/depoly_flutter_demos.dart | 169 ++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 bin/depoly_flutter_demos.dart diff --git a/bin/depoly_flutter_demos.dart b/bin/depoly_flutter_demos.dart new file mode 100644 index 0000000..3e91dd4 --- /dev/null +++ b/bin/depoly_flutter_demos.dart @@ -0,0 +1,169 @@ +import 'dart:io'; + +void main() async { + await init(); + + final prepareRepository = Future.microtask(() async { + await cloneOriginRepository(); + await fetchUpstream(); + }); + + final buildProject = Future.microtask(() async { + await buildWebProject(); + }); + + await Future.wait([prepareRepository, buildProject]); + + copyFiles(); + await pushToOrigin(); + clear(); +} + +Future init() async { + print('Use temp: $tmpDir'); + originUrl = await repositoryOriginUrl(projectDir); +} + +Future buildWebProject() async { + final flutterTargetFile = '${projectDir.path}bin\\main.dart'; + print('Build web app: $flutterTargetFile'); + await cmd('flutter build web -t $flutterTargetFile --web-renderer html'); +} + +Future cloneOriginRepository() async { + print('Cloning origin: [web-demos] $originUrl'); + await cmd( + 'git clone -b web-demos --single-branch $originUrl .', + workingDirectory: tmpDir, + ); +} + +Future fetchUpstream() async { + final shvets = r'https://github.com/RefactoringGuru/design-patterns-dart.git'; + print('Fetch upstream: [web-demos] $shvets'); + await cmd('git remote add upstream $shvets', workingDirectory: tmpDir); + await cmd('git fetch upstream', workingDirectory: tmpDir); +} + +void copyFiles() { + print('Copy files:'); + copyDirectory(webBuildDir, tmpDir); +} + +Future pushToOrigin() async { + await cmd('git add .', workingDirectory: tmpDir); + await cmd( + 'git commit -m ${await lastProjectCommit()}', + workingDirectory: tmpDir, + showOut: true, + ); + + print('Push to origin: [web-demos] $originUrl'); + await cmd('git push origin web-demos', workingDirectory: tmpDir, showOut: true); +} + +late final tmpDir = Directory.systemTemp.createTempSync(); +late final projectDir = thisPath(r'..\'); +late final webBuildDir = Directory(projectDir.uri.toFilePath() + r'build\web'); +late final String originUrl; + +void clear() { + print('Clear: $tmpDir'); + tmpDir.deleteSync(recursive: true); +} + +Future cmd( + String command, { + Directory? workingDirectory, + bool showOut = false, +}) async { + var process = await Process.run( + command, + [], + workingDirectory: workingDirectory?.path, + runInShell: true, + ); + + if (showOut) { + print(process.stdout); + } + + if (process.exitCode != 0) { + print(command); + print(process.stderr); + clear(); + exit(process.exitCode); + } + + return process.stdout; +} + +Future repositoryOriginUrl(Directory workingDir) async { + final raw = await cmd( + 'git remote show origin', + workingDirectory: workingDir, + ); + final url = RegExp('Push URL: (.+)\n').firstMatch(raw)?.group(1); + + if (url == null) { + throw Exception('Empty Remote repository'); + } + + return url; +} + +Future lastProjectCommit() async { + final rawCommit = + await cmd('git log -1 --pretty=%B', workingDirectory: projectDir); + final formatCommit = rawCommit.replaceAll(' ', '_'); + return 'auto_commit:_$formatCommit'; +} + +Directory thisPath(String name) { + final dir = Platform.script.pathSegments + .sublist(0, Platform.script.pathSegments.length - 1) + .join(Platform.pathSeparator) + + Platform.pathSeparator + + name; + + return Directory(Uri.directory(dir).normalizePath().toFilePath()); +} + +void copyDirectory(Directory source, Directory target) { + if (!target.existsSync()) { + target.createSync(); + } + + for (final entry in source.listSync()) { + final newPath = updatePath(entry, source, target); + + if (entry is File) { + copyFile(entry, newPath); + } else if (entry is Directory) { + copyDirectory(entry, Directory(newPath)); + } else { + throw Exception( + 'FileSystemEntity is not recognized. It must be a file or a directory', + ); + } + } +} + +void copyFile(File entry, String newFileName) { + print('\t${removerBuildPath(entry)}'); + entry.copySync(newFileName); +} + +String updatePath( + FileSystemEntity entry, + Directory source, + Directory target, +) { + return entry.path + .replaceFirst(source.path, target.path + Platform.pathSeparator) + .replaceAll(r'\\', r'\'); +} + +String removerBuildPath(FileSystemEntity target) { + return target.path.replaceFirst(webBuildDir.path, '').substring(1); +} From 832b6669fba81053f51c84aaae0d6c1acb4fbe07 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 5 Feb 2022 12:14:45 +0200 Subject: [PATCH 174/479] Add deploy-script description. --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3e1ca32..f4216c6 100644 --- a/README.md +++ b/README.md @@ -72,13 +72,19 @@ cd root directory flutter build web -t bin\main.dart --web-renderer html ``` -## License +### Deploy flutter demos +1. Fork this repo: https://github.com/RefactoringGuru/design-patterns-dart +2. Apply your changes. +3. Run the script `dart bin\deploy_flutter_demos.dart`. +This script will build a web platform flutter app and push the changes to your **web-demos** branch on github. +4. You can now make a pull request on the **web-demos** branch. +5. Once approved for the merge, the web app will be available at https://refactoringguru.github.io/design-patterns-dart . +## License This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. Creative Commons License ## Credits - Authors: Alexander Shvets ([@neochief](https://github.com/neochief)), ilopX ([@ilopX](https://github.com/ilopX)) From df97736638b3697add220967de4334a395061564 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 5 Feb 2022 12:20:19 +0200 Subject: [PATCH 175/479] Fix deploy-script name. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f4216c6..4f078e8 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ flutter build web -t bin\main.dart --web-renderer html ### Deploy flutter demos 1. Fork this repo: https://github.com/RefactoringGuru/design-patterns-dart 2. Apply your changes. -3. Run the script `dart bin\deploy_flutter_demos.dart`. +3. Run the script `dart bin\depoly_flutter_demos.dart`. This script will build a web platform flutter app and push the changes to your **web-demos** branch on github. 4. You can now make a pull request on the **web-demos** branch. 5. Once approved for the merge, the web app will be available at https://refactoringguru.github.io/design-patterns-dart . From 2fbd9875173e647d2fd6f6ceea340cbf6f8fae6f Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 5 Feb 2022 12:23:15 +0200 Subject: [PATCH 176/479] Format deploy_flutter_demos.dart. --- bin/depoly_flutter_demos.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bin/depoly_flutter_demos.dart b/bin/depoly_flutter_demos.dart index 3e91dd4..b6e0509 100644 --- a/bin/depoly_flutter_demos.dart +++ b/bin/depoly_flutter_demos.dart @@ -59,7 +59,11 @@ Future pushToOrigin() async { ); print('Push to origin: [web-demos] $originUrl'); - await cmd('git push origin web-demos', workingDirectory: tmpDir, showOut: true); + await cmd( + 'git push origin web-demos', + workingDirectory: tmpDir, + showOut: true, + ); } late final tmpDir = Directory.systemTemp.createTempSync(); From 6542ba46fbbaf556ce3b4ccd0e963b29d17708bf Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 5 Feb 2022 12:28:46 +0200 Subject: [PATCH 177/479] Fix deploy file name. --- README.md | 2 +- bin/{depoly_flutter_demos.dart => deploy_flutter_demos.dart} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename bin/{depoly_flutter_demos.dart => deploy_flutter_demos.dart} (100%) diff --git a/README.md b/README.md index 4f078e8..f4216c6 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ flutter build web -t bin\main.dart --web-renderer html ### Deploy flutter demos 1. Fork this repo: https://github.com/RefactoringGuru/design-patterns-dart 2. Apply your changes. -3. Run the script `dart bin\depoly_flutter_demos.dart`. +3. Run the script `dart bin\deploy_flutter_demos.dart`. This script will build a web platform flutter app and push the changes to your **web-demos** branch on github. 4. You can now make a pull request on the **web-demos** branch. 5. Once approved for the merge, the web app will be available at https://refactoringguru.github.io/design-patterns-dart . diff --git a/bin/depoly_flutter_demos.dart b/bin/deploy_flutter_demos.dart similarity index 100% rename from bin/depoly_flutter_demos.dart rename to bin/deploy_flutter_demos.dart From 8313cb015ae7c219c5de24b3ea247f9443de4039 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 10:36:43 +0200 Subject: [PATCH 178/479] Add color rules. --- .../adapter/flutter_adapter/client_app/app.dart | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/patterns/adapter/flutter_adapter/client_app/app.dart b/patterns/adapter/flutter_adapter/client_app/app.dart index 72534df..53e48c0 100644 --- a/patterns/adapter/flutter_adapter/client_app/app.dart +++ b/patterns/adapter/flutter_adapter/client_app/app.dart @@ -1,17 +1,16 @@ import 'dart:ui'; -import '../../../observer/app_observer/observer/event.dart'; -import '../adapter/classic_app.dart'; -import 'text_coloring.dart'; - -class NextTextColorEvent extends Event {} +import '../adapter/classic_app_base.dart'; +import 'business_logic/color_rules.dart'; +import 'business_logic/text_coloring.dart'; class App extends ClassicAppBase { App() { - textColoring = TextColoring(this); + textColoring = TextColoring('Flutter Adapter', this); } late final TextColoring textColoring; + late final ColorRules colorRules; @override void onWheel(double deltaX, double deltaY) { @@ -20,11 +19,13 @@ class App extends ClassicAppBase { @override void onMouseDown() { - events.notify(NextTextColorEvent()); + textColoring.color = colorRules.nextColor(textColoring.color); } @override void onPaint(Canvas canvas, Size size) { - textColoring.paint('Flutter Adapter', canvas, size); + textColoring.paint(canvas, size); } } + + From 3e057d493b9c9b9afd55b77cd0ac6239578efb78 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 10:38:32 +0200 Subject: [PATCH 179/479] Add classic app events. --- .../business_logic/color_rules.dart | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 patterns/adapter/flutter_adapter/client_app/business_logic/color_rules.dart diff --git a/patterns/adapter/flutter_adapter/client_app/business_logic/color_rules.dart b/patterns/adapter/flutter_adapter/client_app/business_logic/color_rules.dart new file mode 100644 index 0000000..4613d76 --- /dev/null +++ b/patterns/adapter/flutter_adapter/client_app/business_logic/color_rules.dart @@ -0,0 +1,21 @@ +import 'dart:ui'; + +class ColorRules { + final colors = [ + Color(0xFF000000), + Color(0xFFD81B60), + Color(0xFF5E35B1), + Color(0xFF1E88E5), + Color(0xFF43A047), + ]; + + Color nextColor(Color currentColor) { + var nextIndex = colors.indexOf(currentColor) + 1; + + if (nextIndex >= colors.length || nextIndex < 0) { + nextIndex = 0; + } + + return colors[nextIndex]; + } +} From 88b074e74fe54457d9a365a2039918b9766bfb0e Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 10:38:53 +0200 Subject: [PATCH 180/479] Reformat & add classic app events. --- .../flutter_adapter/adapter/classic_app_render_object.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart b/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart index aa7fe8f..511b3f5 100644 --- a/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart +++ b/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart @@ -2,6 +2,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import 'classic_app.dart'; +import 'classic_app_events.dart'; class ClassicAppRenderObject extends RenderBox { ClassicAppRenderObject(ClassicApp classicApp) { @@ -65,11 +66,10 @@ class ClassicAppRenderObject extends RenderBox { } else if (event is PointerDownEvent) { if (event.buttons == kPrimaryMouseButton) { _classicApp.onMouseDown(); - } else if (event.buttons == kSecondaryMouseButton) { - } else if (event.buttons == kMiddleMouseButton) {} + } else if (event.buttons == kSecondaryMouseButton) {} + else if (event.buttons == kMiddleMouseButton) {} } else if (event is PointerScrollEvent) { _classicApp.onWheel(event.scrollDelta.dx, event.scrollDelta.dy); - } } From 424e8debcb00491d465392ded4991de46e6c12f2 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 10:39:33 +0200 Subject: [PATCH 181/479] Add slider widget. --- .../client_app/widgets/slider_widget.dart | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 patterns/adapter/flutter_adapter/client_app/widgets/slider_widget.dart diff --git a/patterns/adapter/flutter_adapter/client_app/widgets/slider_widget.dart b/patterns/adapter/flutter_adapter/client_app/widgets/slider_widget.dart new file mode 100644 index 0000000..34514fb --- /dev/null +++ b/patterns/adapter/flutter_adapter/client_app/widgets/slider_widget.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +class SliderWidget extends StatelessWidget { + final int current; + final int max; + final void Function(int newSize) onChange; + + const SliderWidget({ + Key? key, + required this.current, + required this.max, + required this.onChange, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + SizedBox( + width: 31, + child: Text('$current', textAlign: TextAlign.right), + ), + SizedBox( + width: 200, + child: Slider( + value: current.toDouble(), + max: max.toDouble(), + min: 1, + onChanged: (newVal) { + onChange(newVal.toInt()); + }, + ), + ), + ], + ); + } +} From cb086cf8e4fd2593d1e84dad077a4f94da87bb62 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 10:39:48 +0200 Subject: [PATCH 182/479] Add color buttons widget. --- .../widgets/color_buttons_widget.dart | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 patterns/adapter/flutter_adapter/client_app/widgets/color_buttons_widget.dart diff --git a/patterns/adapter/flutter_adapter/client_app/widgets/color_buttons_widget.dart b/patterns/adapter/flutter_adapter/client_app/widgets/color_buttons_widget.dart new file mode 100644 index 0000000..d3b05a0 --- /dev/null +++ b/patterns/adapter/flutter_adapter/client_app/widgets/color_buttons_widget.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; + +class ColorButtonsWidget extends StatelessWidget { + final Color currentColor; + final List colors; + final void Function(Color color) onColorSelect; + + const ColorButtonsWidget({ + Key? key, + required this.currentColor, + required this.colors, + required this.onColorSelect, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + ...colors.map( + (Color color) { + return _buildColorButton(color); + }, + ).toList(), + ], + ); + } + + Widget _buildColorButton(Color color) { + final isColorSelect = (color == currentColor); + return GestureDetector( + onTap: () { + onColorSelect(color); + }, + child: Container( + width: 20, + height: 20, + color: color, + child: isColorSelect ? _buildSelectColorIcon() : null, + ), + ); + } + + Widget _buildSelectColorIcon() { + return Center( + child: Container( + width: 4, + height: 4, + color: Colors.white.withOpacity(0.8), + ), + ); + } +} From 88379522770b48ef05b82e13c3dacd5a91ad267e Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 10:40:58 +0200 Subject: [PATCH 183/479] Divide ClassicApp into interface and implementation --- .../flutter_adapter/adapter/classic_app.dart | 42 ------------------ .../adapter/classic_app_base.dart | 44 +++++++++++++++++++ .../adapter/classic_app_events.dart | 3 ++ 3 files changed, 47 insertions(+), 42 deletions(-) create mode 100644 patterns/adapter/flutter_adapter/adapter/classic_app_base.dart create mode 100644 patterns/adapter/flutter_adapter/adapter/classic_app_events.dart diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app.dart b/patterns/adapter/flutter_adapter/adapter/classic_app.dart index 70813f7..4124262 100644 --- a/patterns/adapter/flutter_adapter/adapter/classic_app.dart +++ b/patterns/adapter/flutter_adapter/adapter/classic_app.dart @@ -1,7 +1,6 @@ import 'dart:ui'; import '../../../observer/app_observer/observer/app_observer.dart'; -import '../../../observer/app_observer/observer/event.dart'; abstract class ClassicApp { AppObserver get events; @@ -26,44 +25,3 @@ abstract class ClassicApp { void repaint(); } - -class ClassicAppRepaint extends Event {} - -abstract class ClassicAppBase implements ClassicApp { - final _appEvents = AppObserver(); - - @override - AppObserver get events => _appEvents; - - @override - void onKeyDown() {} - - @override - void onKeyUp() {} - - @override - void onMoseEnter() {} - - @override - void onMouseDown() {} - - @override - void onMouseLeave() {} - - @override - void onMouseMove() {} - - @override - void onMouseUp() {} - - @override - void onWheel(double deltaX, double deltaY) {} - - @override - void onPaint(Canvas canvas, Size size); - - @override - void repaint() { - _appEvents.notify(ClassicAppRepaint()); - } -} diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app_base.dart b/patterns/adapter/flutter_adapter/adapter/classic_app_base.dart new file mode 100644 index 0000000..185db73 --- /dev/null +++ b/patterns/adapter/flutter_adapter/adapter/classic_app_base.dart @@ -0,0 +1,44 @@ +import 'dart:ui'; + +import '../../../observer/app_observer/observer/app_observer.dart'; +import 'classic_app.dart'; +import 'classic_app_events.dart'; + +abstract class ClassicAppBase implements ClassicApp { + final _appEvents = AppObserver(); + + @override + AppObserver get events => _appEvents; + + @override + void onKeyDown() {} + + @override + void onKeyUp() {} + + @override + void onMoseEnter() {} + + @override + void onMouseDown() {} + + @override + void onMouseLeave() {} + + @override + void onMouseMove() {} + + @override + void onMouseUp() {} + + @override + void onWheel(double deltaX, double deltaY) {} + + @override + void onPaint(Canvas canvas, Size size); + + @override + void repaint() { + _appEvents.notify(ClassicAppRepaint()); + } +} diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app_events.dart b/patterns/adapter/flutter_adapter/adapter/classic_app_events.dart new file mode 100644 index 0000000..655ff78 --- /dev/null +++ b/patterns/adapter/flutter_adapter/adapter/classic_app_events.dart @@ -0,0 +1,3 @@ +import '../../../observer/app_observer/observer/event.dart'; + +class ClassicAppRepaint extends Event {} From bb9d285c50a270539c8bcd7a4904e0b046e7dcdd Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 10:41:47 +0200 Subject: [PATCH 184/479] Add text property to "TextColoring". --- .../client_app/{ => business_logic}/text_coloring.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) rename patterns/adapter/flutter_adapter/client_app/{ => business_logic}/text_coloring.dart (88%) diff --git a/patterns/adapter/flutter_adapter/client_app/text_coloring.dart b/patterns/adapter/flutter_adapter/client_app/business_logic/text_coloring.dart similarity index 88% rename from patterns/adapter/flutter_adapter/client_app/text_coloring.dart rename to patterns/adapter/flutter_adapter/client_app/business_logic/text_coloring.dart index d1a2e8c..9ff487d 100644 --- a/patterns/adapter/flutter_adapter/client_app/text_coloring.dart +++ b/patterns/adapter/flutter_adapter/client_app/business_logic/text_coloring.dart @@ -1,11 +1,13 @@ import 'dart:ui'; -import 'app.dart'; +import '../app.dart'; class TextColoring { final App app; - TextColoring(this.app); + final String text; + + TextColoring(this.text, this.app); final maxTextSize = 200; @@ -35,7 +37,7 @@ class TextColoring { app.repaint(); } - void paint(String text, Canvas canvas, Size canvasSize) { + void paint(Canvas canvas, Size canvasSize) { final builder = ParagraphBuilder( ParagraphStyle( textAlign: TextAlign.center, From 14ca017440b6d53d95004bc3b5623622f8837711 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 10:42:29 +0200 Subject: [PATCH 185/479] Move TextPropertyWidget to separate directory. --- .../client_app/text_property_widget.dart | 141 ------------------ .../widgets/text_property_widget.dart | 51 +++++++ patterns/adapter/flutter_adapter/main.dart | 2 +- 3 files changed, 52 insertions(+), 142 deletions(-) delete mode 100644 patterns/adapter/flutter_adapter/client_app/text_property_widget.dart create mode 100644 patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart diff --git a/patterns/adapter/flutter_adapter/client_app/text_property_widget.dart b/patterns/adapter/flutter_adapter/client_app/text_property_widget.dart deleted file mode 100644 index 18bc774..0000000 --- a/patterns/adapter/flutter_adapter/client_app/text_property_widget.dart +++ /dev/null @@ -1,141 +0,0 @@ -import 'package:flutter/material.dart' as material; -import 'package:flutter/widgets.dart'; - -import '../../../observer/app_observer/observer/app_observer.dart'; -import '../adapter/classic_app.dart'; -import '../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; -import 'app.dart'; - -class TextPropertyWidget extends StatefulWidget { - final App classicApp; - - const TextPropertyWidget({ - Key? key, - required this.classicApp, - }) : super(key: key); - - @override - State createState() => _TextPropertyWidgetState(); -} - -class _TextPropertyWidgetState extends State { - @override - Widget build(BuildContext context) { - final app = widget.classicApp; - return SubscriberWidget( - observer: app.events, - builder: (context, event) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _buildTextSizeSlider(app), - _buildTextColorButtons(app), - ], - ); - }, - ); - } - - final colors = [ - material.Colors.black, - material.Colors.pink.shade600, - material.Colors.deepPurple.shade600, - material.Colors.blue.shade600, - material.Colors.green.shade600, - ]; - - late EventFunction _classicAppRepaintEvent; - late EventFunction _nextColorEvent; - - @override - void initState() { - // _classicAppRepaintEvent = widget.classicApp.events.subscribe( - // (ClassicAppRepaint e) => setState(() {}), - // ); - - _nextColorEvent = widget.classicApp.events.subscribe( - (NextTextColorEvent e) => setState(() { - final currColor = widget.classicApp.textColoring.color; - var nextIndex = colors.indexOf(currColor) + 1; - - if (nextIndex >= colors.length) { - nextIndex = 0; - } - - widget.classicApp.textColoring.color = colors[nextIndex]; - }), - ); - - super.initState(); - } - - @override - void dispose() { - widget.classicApp.events - ..unsubscribe(_classicAppRepaintEvent) - ..unsubscribe(_nextColorEvent); - super.dispose(); - } - - Widget _buildTextSizeSlider(App app) { - return Row( - children: [ - SizedBox( - width: 31, - child: Text( - app.textColoring.size.toString(), - textAlign: TextAlign.right, - ), - ), - SizedBox( - width: 200, - child: material.Slider( - value: app.textColoring.size.toDouble(), - max: app.textColoring.maxTextSize.toDouble(), - min: 1, - onChanged: (newVal) { - app.textColoring.size = newVal.toInt(); - }, - ), - ), - ], - ); - } - - Widget _buildTextColorButtons(App app) { - return Row( - children: [ - ...colors.map( - (Color color) { - return _buildColorButton(color, app); - }, - ).toList(), - ], - ); - } - - Widget _buildColorButton(Color color, App app) { - final isColorSelect = (color == app.textColoring.color); - return GestureDetector( - onTap: () { - app.textColoring.color = color; - }, - child: Container( - width: 20, - height: 20, - color: color, - child: isColorSelect ? _buildSelectColorIcon() : null, - ), - ); - } - - Widget _buildSelectColorIcon() { - return Center( - child: Container( - width: 4, - height: 4, - color: material.Colors.white.withOpacity(0.8), - ), - ); - } -} diff --git a/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart b/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart new file mode 100644 index 0000000..6a15514 --- /dev/null +++ b/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; + +import '../../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; +import '../../adapter/classic_app_events.dart'; +import '../app.dart'; +import 'color_buttons_widget.dart'; +import 'slider_widget.dart'; + + +class TextPropertyWidget extends StatelessWidget { + final App classicApp; + + const TextPropertyWidget({ + Key? key, + required this.classicApp, + }) : super(key: key); + + + @override + Widget build(BuildContext context) { + final app = classicApp; + return SubscriberWidget( + observer: app.events, + builder: (context, event) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SliderWidget( + current: app.textColoring.size, + max: app.textColoring.maxTextSize, + onChange: (newVal) { + app.textColoring.size = newVal; + }, + ), + ColorButtonsWidget( + currentColor: app.textColoring.color, + colors: app.colorRules.colors, + onColorSelect: (color) { + app.textColoring.color = color; + }, + ), + ], + ); + }, + ); + } +} + + + + diff --git a/patterns/adapter/flutter_adapter/main.dart b/patterns/adapter/flutter_adapter/main.dart index 2eaa997..08366d1 100644 --- a/patterns/adapter/flutter_adapter/main.dart +++ b/patterns/adapter/flutter_adapter/main.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'adapter/classic_app_widget_adapter.dart'; import 'client_app/app.dart'; -import 'client_app/text_property_widget.dart'; +import 'client_app/widgets/text_property_widget.dart'; class FlutterAdapterApp extends StatefulWidget { @override From e613530232e606a6d6a9829119563a94fe243073 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 10:44:51 +0200 Subject: [PATCH 186/479] Fix: init color rules. --- patterns/adapter/flutter_adapter/client_app/app.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/patterns/adapter/flutter_adapter/client_app/app.dart b/patterns/adapter/flutter_adapter/client_app/app.dart index 53e48c0..e32a766 100644 --- a/patterns/adapter/flutter_adapter/client_app/app.dart +++ b/patterns/adapter/flutter_adapter/client_app/app.dart @@ -1,5 +1,7 @@ import 'dart:ui'; +import 'package:flutter/material.dart'; + import '../adapter/classic_app_base.dart'; import 'business_logic/color_rules.dart'; import 'business_logic/text_coloring.dart'; @@ -10,7 +12,7 @@ class App extends ClassicAppBase { } late final TextColoring textColoring; - late final ColorRules colorRules; + final colorRules = ColorRules(); @override void onWheel(double deltaX, double deltaY) { @@ -27,5 +29,3 @@ class App extends ClassicAppBase { textColoring.paint(canvas, size); } } - - From 3ea64a6f61c9d54782bbae659f24a88e17191d45 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 10:51:21 +0200 Subject: [PATCH 187/479] Rename ClassicAppWidgetAdapter to ClassicAppAdapterWidget. --- ...pp_widget_adapter.dart => classic_app_adapter_widget.dart} | 4 ++-- patterns/adapter/flutter_adapter/main.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename patterns/adapter/flutter_adapter/adapter/{classic_app_widget_adapter.dart => classic_app_adapter_widget.dart} (78%) diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app_widget_adapter.dart b/patterns/adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart similarity index 78% rename from patterns/adapter/flutter_adapter/adapter/classic_app_widget_adapter.dart rename to patterns/adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart index 50f8154..848b4f6 100644 --- a/patterns/adapter/flutter_adapter/adapter/classic_app_widget_adapter.dart +++ b/patterns/adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart @@ -4,10 +4,10 @@ import 'package:flutter/widgets.dart'; import 'classic_app.dart'; import 'classic_app_render_object.dart'; -class ClassicAppWidgetAdapter extends LeafRenderObjectWidget { +class ClassicAppAdapterWidget extends LeafRenderObjectWidget { final ClassicApp classicApp; - ClassicAppWidgetAdapter({required this.classicApp}); + ClassicAppAdapterWidget({required this.classicApp}); @override RenderObject createRenderObject(BuildContext context) { diff --git a/patterns/adapter/flutter_adapter/main.dart b/patterns/adapter/flutter_adapter/main.dart index 08366d1..a2e4b81 100644 --- a/patterns/adapter/flutter_adapter/main.dart +++ b/patterns/adapter/flutter_adapter/main.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'adapter/classic_app_widget_adapter.dart'; +import 'adapter/classic_app_adapter_widget.dart'; import 'client_app/app.dart'; import 'client_app/widgets/text_property_widget.dart'; @@ -21,7 +21,7 @@ class _FlutterAdapterAppState extends State { classicApp: app, ), Expanded( - child: ClassicAppWidgetAdapter( + child: ClassicAppAdapterWidget( classicApp: app, ), ), From de99e7f881ffa7b78aac7555041b9d781aada933 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 10:57:09 +0200 Subject: [PATCH 188/479] Add repaintSignal to TextColoring. --- patterns/adapter/flutter_adapter/client_app/app.dart | 2 +- .../client_app/business_logic/text_coloring.dart | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/patterns/adapter/flutter_adapter/client_app/app.dart b/patterns/adapter/flutter_adapter/client_app/app.dart index e32a766..49c2ae3 100644 --- a/patterns/adapter/flutter_adapter/client_app/app.dart +++ b/patterns/adapter/flutter_adapter/client_app/app.dart @@ -8,7 +8,7 @@ import 'business_logic/text_coloring.dart'; class App extends ClassicAppBase { App() { - textColoring = TextColoring('Flutter Adapter', this); + textColoring = TextColoring('Flutter Adapter', repaint); } late final TextColoring textColoring; diff --git a/patterns/adapter/flutter_adapter/client_app/business_logic/text_coloring.dart b/patterns/adapter/flutter_adapter/client_app/business_logic/text_coloring.dart index 9ff487d..1e51811 100644 --- a/patterns/adapter/flutter_adapter/client_app/business_logic/text_coloring.dart +++ b/patterns/adapter/flutter_adapter/client_app/business_logic/text_coloring.dart @@ -1,13 +1,11 @@ import 'dart:ui'; -import '../app.dart'; - class TextColoring { - final App app; + final void Function() _repaintSignal; final String text; - TextColoring(this.text, this.app); + TextColoring(this.text, this._repaintSignal); final maxTextSize = 200; @@ -21,7 +19,7 @@ class TextColoring { } _size = newVal; - app.repaint(); + _repaintSignal(); } var _color = Color(0xffd81b60); @@ -34,7 +32,7 @@ class TextColoring { } _color = newColor; - app.repaint(); + _repaintSignal(); } void paint(Canvas canvas, Size canvasSize) { From f545c0940cde5cd1eadea5c81eb44f9e8a4d0dbc Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 11:17:10 +0200 Subject: [PATCH 189/479] Remove color from slider text. --- .../flutter_adapter/client_app/widgets/slider_widget.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/patterns/adapter/flutter_adapter/client_app/widgets/slider_widget.dart b/patterns/adapter/flutter_adapter/client_app/widgets/slider_widget.dart index 34514fb..f28abe7 100644 --- a/patterns/adapter/flutter_adapter/client_app/widgets/slider_widget.dart +++ b/patterns/adapter/flutter_adapter/client_app/widgets/slider_widget.dart @@ -18,7 +18,10 @@ class SliderWidget extends StatelessWidget { children: [ SizedBox( width: 31, - child: Text('$current', textAlign: TextAlign.right), + child: Text( + '$current', + textAlign: TextAlign.right, + ), ), SizedBox( width: 200, From 2a8eb300424a82e97246033d23d6d66713f43c72 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 11:17:43 +0200 Subject: [PATCH 190/479] Add background to TextProperty panel. --- .../widgets/text_property_widget.dart | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart b/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart index 6a15514..8d95f24 100644 --- a/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart +++ b/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart @@ -6,7 +6,6 @@ import '../app.dart'; import 'color_buttons_widget.dart'; import 'slider_widget.dart'; - class TextPropertyWidget extends StatelessWidget { final App classicApp; @@ -15,37 +14,34 @@ class TextPropertyWidget extends StatelessWidget { required this.classicApp, }) : super(key: key); - @override Widget build(BuildContext context) { final app = classicApp; return SubscriberWidget( observer: app.events, builder: (context, event) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SliderWidget( - current: app.textColoring.size, - max: app.textColoring.maxTextSize, - onChange: (newVal) { - app.textColoring.size = newVal; - }, - ), - ColorButtonsWidget( - currentColor: app.textColoring.color, - colors: app.colorRules.colors, - onColorSelect: (color) { - app.textColoring.color = color; - }, - ), - ], + return Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SliderWidget( + current: app.textColoring.size, + max: app.textColoring.maxTextSize, + onChange: (newVal) { + app.textColoring.size = newVal; + }, + ), + ColorButtonsWidget( + currentColor: app.textColoring.color, + colors: app.colorRules.colors, + onColorSelect: (color) { + app.textColoring.color = color; + }, + ), + ], + ), ); }, ); } } - - - - From 9401448cb0893dd2692139c41f22ab6e3f7f54b3 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 11:21:05 +0200 Subject: [PATCH 191/479] Delete unnecessary spread operator. --- .../client_app/widgets/color_buttons_widget.dart | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/patterns/adapter/flutter_adapter/client_app/widgets/color_buttons_widget.dart b/patterns/adapter/flutter_adapter/client_app/widgets/color_buttons_widget.dart index d3b05a0..d87faf7 100644 --- a/patterns/adapter/flutter_adapter/client_app/widgets/color_buttons_widget.dart +++ b/patterns/adapter/flutter_adapter/client_app/widgets/color_buttons_widget.dart @@ -15,13 +15,7 @@ class ColorButtonsWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( - children: [ - ...colors.map( - (Color color) { - return _buildColorButton(color); - }, - ).toList(), - ], + children: colors.map(_buildColorButton).toList(), ); } From b09558cf3c2a1c1252418bb862da92741be83714 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 11:23:44 +0200 Subject: [PATCH 192/479] Add import library name. --- patterns/adapter/flutter_adapter/main.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patterns/adapter/flutter_adapter/main.dart b/patterns/adapter/flutter_adapter/main.dart index a2e4b81..60624f5 100644 --- a/patterns/adapter/flutter_adapter/main.dart +++ b/patterns/adapter/flutter_adapter/main.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'adapter/classic_app_adapter_widget.dart'; +import 'adapter/classic_app_adapter_widget.dart' as adapter; import 'client_app/app.dart'; import 'client_app/widgets/text_property_widget.dart'; @@ -21,7 +21,7 @@ class _FlutterAdapterAppState extends State { classicApp: app, ), Expanded( - child: ClassicAppAdapterWidget( + child: adapter.ClassicAppAdapterWidget( classicApp: app, ), ), From 8c3d0012f1ce0f3a5c185c497cbc65e20b12ec48 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 17:16:06 +0200 Subject: [PATCH 193/479] Add RepaintCompatible interface --- .../{adapter => classic_app}/classic_app.dart | 11 ++--------- .../classic_app/repaint_compatible.dart | 3 +++ .../text_coloring.dart | 6 ++++-- 3 files changed, 9 insertions(+), 11 deletions(-) rename patterns/adapter/flutter_adapter/{adapter => classic_app}/classic_app.dart (70%) create mode 100644 patterns/adapter/flutter_adapter/classic_app/repaint_compatible.dart rename patterns/adapter/flutter_adapter/client_app/{business_logic => business_rules}/text_coloring.dart (88%) diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app.dart b/patterns/adapter/flutter_adapter/classic_app/classic_app.dart similarity index 70% rename from patterns/adapter/flutter_adapter/adapter/classic_app.dart rename to patterns/adapter/flutter_adapter/classic_app/classic_app.dart index 4124262..ec76406 100644 --- a/patterns/adapter/flutter_adapter/adapter/classic_app.dart +++ b/patterns/adapter/flutter_adapter/classic_app/classic_app.dart @@ -1,15 +1,8 @@ import 'dart:ui'; import '../../../observer/app_observer/observer/app_observer.dart'; - -abstract class ClassicApp { - AppObserver get events; - - void onMouseDown(); - - void onMouseUp(); - - void onMouseMove(); +import 'classic_app_events.dart'; +import 'repaint_compatible.dart'; void onMoseEnter(); diff --git a/patterns/adapter/flutter_adapter/classic_app/repaint_compatible.dart b/patterns/adapter/flutter_adapter/classic_app/repaint_compatible.dart new file mode 100644 index 0000000..e8d6804 --- /dev/null +++ b/patterns/adapter/flutter_adapter/classic_app/repaint_compatible.dart @@ -0,0 +1,3 @@ +abstract class RepaintCompatible { + void repaint(); +} diff --git a/patterns/adapter/flutter_adapter/client_app/business_logic/text_coloring.dart b/patterns/adapter/flutter_adapter/client_app/business_rules/text_coloring.dart similarity index 88% rename from patterns/adapter/flutter_adapter/client_app/business_logic/text_coloring.dart rename to patterns/adapter/flutter_adapter/client_app/business_rules/text_coloring.dart index 1e51811..bb4cd07 100644 --- a/patterns/adapter/flutter_adapter/client_app/business_logic/text_coloring.dart +++ b/patterns/adapter/flutter_adapter/client_app/business_rules/text_coloring.dart @@ -1,9 +1,11 @@ import 'dart:ui'; +import '../../classic_app/repaint_compatible.dart'; + class TextColoring { - final void Function() _repaintSignal; + final RepaintCompatible _repaintContext; - final String text; + TextColoring(this._repaintContext); TextColoring(this.text, this._repaintSignal); From 75be9eb2c730b9e4d3d2b220a280147db77e772e Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 17:17:29 +0200 Subject: [PATCH 194/479] Remove paint method from TextColoring & add new rule text font. --- .../business_rules/text_coloring.dart | 41 ++++++------------- .../widgets/text_property_widget.dart | 2 +- 2 files changed, 13 insertions(+), 30 deletions(-) diff --git a/patterns/adapter/flutter_adapter/client_app/business_rules/text_coloring.dart b/patterns/adapter/flutter_adapter/client_app/business_rules/text_coloring.dart index bb4cd07..9167de0 100644 --- a/patterns/adapter/flutter_adapter/client_app/business_rules/text_coloring.dart +++ b/patterns/adapter/flutter_adapter/client_app/business_rules/text_coloring.dart @@ -7,21 +7,26 @@ class TextColoring { TextColoring(this._repaintContext); - TextColoring(this.text, this._repaintSignal); - - final maxTextSize = 200; + final maxTextSize = 400; var _size = 50; int get size => _size; set size(int newVal) { - if (newVal == _size || newVal > maxTextSize || newVal < 1) { + if (newVal == _size) { return; } - _size = newVal; - _repaintSignal(); + if (newVal > maxTextSize) { + _size = newVal; + } else if (newVal < 1) { + _size = 1; + } else { + _size = newVal; + } + + _repaintContext.repaint(); } var _color = Color(0xffd81b60); @@ -34,28 +39,6 @@ class TextColoring { } _color = newColor; - _repaintSignal(); - } - - void paint(Canvas canvas, Size canvasSize) { - final builder = ParagraphBuilder( - ParagraphStyle( - textAlign: TextAlign.center, - fontSize: _size.toDouble(), - ), - ) - ..pushStyle( - TextStyle( - fontFamily: 'Arial', - color: _color, - ), - ) - ..addText(text); - - final paragraph = builder.build() - ..layout(ParagraphConstraints(width: canvasSize.width)); - - final centerPos = Offset(0, (canvasSize.height - paragraph.height) / 2); - canvas.drawParagraph(paragraph, centerPos); + _repaintContext.repaint(); } } diff --git a/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart b/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart index 8d95f24..26fcb4b 100644 --- a/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart +++ b/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; -import '../../adapter/classic_app_events.dart'; +import '../../classic_app/classic_app_events.dart'; import '../app.dart'; import 'color_buttons_widget.dart'; import 'slider_widget.dart'; From d2905152092bbf40d23f90dd944a8069d550f26b Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 17:18:23 +0200 Subject: [PATCH 195/479] Move paint text method to app class. --- .../flutter_adapter/client_app/app.dart | 66 +++++++++++++++---- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/patterns/adapter/flutter_adapter/client_app/app.dart b/patterns/adapter/flutter_adapter/client_app/app.dart index 49c2ae3..aea8887 100644 --- a/patterns/adapter/flutter_adapter/client_app/app.dart +++ b/patterns/adapter/flutter_adapter/client_app/app.dart @@ -1,21 +1,20 @@ import 'dart:ui'; -import 'package:flutter/material.dart'; +import '../classic_app/classic_app.dart'; +import 'business_rules/color_rules.dart'; +import 'business_rules/text_coloring.dart'; -import '../adapter/classic_app_base.dart'; -import 'business_logic/color_rules.dart'; -import 'business_logic/text_coloring.dart'; - -class App extends ClassicAppBase { +class App extends ClassicApp { App() { - textColoring = TextColoring('Flutter Adapter', repaint); + textColoring = TextColoring(this); + colorRules = ColorRules(); } late final TextColoring textColoring; - final colorRules = ColorRules(); + late final ColorRules colorRules; @override - void onWheel(double deltaX, double deltaY) { + void onPointerWheel(double deltaX, double deltaY) { textColoring.size += deltaY ~/ 10; } @@ -25,7 +24,52 @@ class App extends ClassicAppBase { } @override - void onPaint(Canvas canvas, Size size) { - textColoring.paint(canvas, size); + void onPaint(Canvas canvas, Size canvasSize) { + paintText( + canvas, + 'Flutter Adapter', + canvasSize, + textColoring.size.toDouble(), + textColoring.color, + ); + + paintText( + canvas, + 'Click on the text to change the text color.\n' + 'Scroll the mouse wheel to change the text size.', + canvasSize, + 16, + Color(0xff848484), + Offset(0, canvasSize.height - 50), + ); } } + +void paintText( + Canvas canvas, + String text, + Size boxSize, + double textSize, + Color color, [ + Offset? pos, +]) { + final builder = ParagraphBuilder( + ParagraphStyle( + textAlign: TextAlign.center, + fontSize: textSize, + ), + ) + ..pushStyle( + TextStyle( + fontFamily: 'Arial', + color: color, + ), + ) + ..addText(text); + + final paragraph = builder.build() + ..layout(ParagraphConstraints(width: boxSize.width)); + + pos ??= Offset(0, (boxSize.height - paragraph.height) / 2); + canvas.drawParagraph(paragraph, pos); +} From c7105545294fcab676f2d2fbcd5974c3af38d381 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 17:20:08 +0200 Subject: [PATCH 196/479] Simplification: remove the ClassicApp interfaces and make only one class. --- .../adapter/classic_app_adapter_widget.dart | 2 +- .../adapter/classic_app_base.dart | 44 ------------------- .../adapter/classic_app_render_object.dart | 6 +-- .../classic_app/classic_app.dart | 18 ++++---- 4 files changed, 13 insertions(+), 57 deletions(-) delete mode 100644 patterns/adapter/flutter_adapter/adapter/classic_app_base.dart diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart b/patterns/adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart index 848b4f6..421bf23 100644 --- a/patterns/adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart +++ b/patterns/adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/widgets.dart'; -import 'classic_app.dart'; +import '../classic_app/classic_app.dart'; import 'classic_app_render_object.dart'; class ClassicAppAdapterWidget extends LeafRenderObjectWidget { diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app_base.dart b/patterns/adapter/flutter_adapter/adapter/classic_app_base.dart deleted file mode 100644 index 185db73..0000000 --- a/patterns/adapter/flutter_adapter/adapter/classic_app_base.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'dart:ui'; - -import '../../../observer/app_observer/observer/app_observer.dart'; -import 'classic_app.dart'; -import 'classic_app_events.dart'; - -abstract class ClassicAppBase implements ClassicApp { - final _appEvents = AppObserver(); - - @override - AppObserver get events => _appEvents; - - @override - void onKeyDown() {} - - @override - void onKeyUp() {} - - @override - void onMoseEnter() {} - - @override - void onMouseDown() {} - - @override - void onMouseLeave() {} - - @override - void onMouseMove() {} - - @override - void onMouseUp() {} - - @override - void onWheel(double deltaX, double deltaY) {} - - @override - void onPaint(Canvas canvas, Size size); - - @override - void repaint() { - _appEvents.notify(ClassicAppRepaint()); - } -} diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart b/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart index 511b3f5..8704f22 100644 --- a/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart +++ b/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart @@ -1,8 +1,8 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; -import 'classic_app.dart'; -import 'classic_app_events.dart'; +import '../classic_app/classic_app.dart'; +import '../classic_app/classic_app_events.dart'; class ClassicAppRenderObject extends RenderBox { ClassicAppRenderObject(ClassicApp classicApp) { @@ -69,7 +69,7 @@ class ClassicAppRenderObject extends RenderBox { } else if (event.buttons == kSecondaryMouseButton) {} else if (event.buttons == kMiddleMouseButton) {} } else if (event is PointerScrollEvent) { - _classicApp.onWheel(event.scrollDelta.dx, event.scrollDelta.dy); + _classicApp.onPointerWheel(event.scrollDelta.dx, event.scrollDelta.dy); } } diff --git a/patterns/adapter/flutter_adapter/classic_app/classic_app.dart b/patterns/adapter/flutter_adapter/classic_app/classic_app.dart index ec76406..05d915d 100644 --- a/patterns/adapter/flutter_adapter/classic_app/classic_app.dart +++ b/patterns/adapter/flutter_adapter/classic_app/classic_app.dart @@ -4,17 +4,17 @@ import '../../../observer/app_observer/observer/app_observer.dart'; import 'classic_app_events.dart'; import 'repaint_compatible.dart'; - void onMoseEnter(); +abstract class ClassicApp implements RepaintCompatible { + final events = AppObserver(); - void onMouseLeave(); + void onMouseDown() {} - void onKeyDown(); + void onPointerWheel(double deltaX, double deltaY) {} - void onKeyUp(); + @override + void repaint() { + events.notify(ClassicAppRepaint()); + } - void onWheel(double deltaX, double deltaY); - - void onPaint(Canvas canvas, Size size); - - void repaint(); + void onPaint(Canvas canvas, Size canvasSize); } From 5ad44fb954a350bfa771b2809707d659c18e53ea Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 17:21:07 +0200 Subject: [PATCH 197/479] Move color_rules to business rules. --- .../{business_logic => business_rules}/color_rules.dart | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename patterns/adapter/flutter_adapter/client_app/{business_logic => business_rules}/color_rules.dart (100%) diff --git a/patterns/adapter/flutter_adapter/client_app/business_logic/color_rules.dart b/patterns/adapter/flutter_adapter/client_app/business_rules/color_rules.dart similarity index 100% rename from patterns/adapter/flutter_adapter/client_app/business_logic/color_rules.dart rename to patterns/adapter/flutter_adapter/client_app/business_rules/color_rules.dart From 43d5e02eb07f710256a3ac7f0e16efa8ed9dd302 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 17:21:51 +0200 Subject: [PATCH 198/479] Move ClassicAppRepaint to classic_app directory. --- .../{adapter => classic_app}/classic_app_events.dart | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename patterns/adapter/flutter_adapter/{adapter => classic_app}/classic_app_events.dart (100%) diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app_events.dart b/patterns/adapter/flutter_adapter/classic_app/classic_app_events.dart similarity index 100% rename from patterns/adapter/flutter_adapter/adapter/classic_app_events.dart rename to patterns/adapter/flutter_adapter/classic_app/classic_app_events.dart From 91ab81aca26933616dcd79c005e22dac2ddd9c0f Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 17:25:04 +0200 Subject: [PATCH 199/479] Format app.dart. --- patterns/adapter/flutter_adapter/client_app/app.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/patterns/adapter/flutter_adapter/client_app/app.dart b/patterns/adapter/flutter_adapter/client_app/app.dart index aea8887..77a4562 100644 --- a/patterns/adapter/flutter_adapter/client_app/app.dart +++ b/patterns/adapter/flutter_adapter/client_app/app.dart @@ -68,7 +68,11 @@ void paintText( ..addText(text); final paragraph = builder.build() - ..layout(ParagraphConstraints(width: boxSize.width)); + ..layout( + ParagraphConstraints( + width: boxSize.width, + ), + ); pos ??= Offset(0, (boxSize.height - paragraph.height) / 2); canvas.drawParagraph(paragraph, pos); From 279676db497db7eda076a90a22ad14284187a56a Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 6 Feb 2022 17:29:04 +0200 Subject: [PATCH 200/479] Move App class properties to top. --- patterns/adapter/flutter_adapter/client_app/app.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/patterns/adapter/flutter_adapter/client_app/app.dart b/patterns/adapter/flutter_adapter/client_app/app.dart index 77a4562..b648c75 100644 --- a/patterns/adapter/flutter_adapter/client_app/app.dart +++ b/patterns/adapter/flutter_adapter/client_app/app.dart @@ -5,14 +5,14 @@ import 'business_rules/color_rules.dart'; import 'business_rules/text_coloring.dart'; class App extends ClassicApp { + late final TextColoring textColoring; + late final ColorRules colorRules; + App() { textColoring = TextColoring(this); colorRules = ColorRules(); } - late final TextColoring textColoring; - late final ColorRules colorRules; - @override void onPointerWheel(double deltaX, double deltaY) { textColoring.size += deltaY ~/ 10; From 93b1fee14bba9dc34dbf48b833db2cbfe0160569 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 10:55:03 +0200 Subject: [PATCH 201/479] Add README to FlutterAdapter. --- patterns/adapter/flutter_adapter/README.md | 60 ++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 patterns/adapter/flutter_adapter/README.md diff --git a/patterns/adapter/flutter_adapter/README.md b/patterns/adapter/flutter_adapter/README.md new file mode 100644 index 0000000..f7c7ef0 --- /dev/null +++ b/patterns/adapter/flutter_adapter/README.md @@ -0,0 +1,60 @@ + +# Adapter pattern +Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate. + +Tutorial: [here](https://refactoring.guru/design-patterns/observer). + +## Example: Flutter classic application adapter widget +This example shows how to adapt a non-reactive classic type application for Flutter. + +### Dependency +This complex example includes these implementations: +- [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer)] +- [[SubscriberWidget](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] + +### Online demo: +Click on the picture to see a [demo](https://refactoringguru.github.io/design-patterns-dart/#/adapter/flutter_adapter). +[![image](https://user-images.githubusercontent.com/8049534/152689272-d4bed484-e216-4eda-8833-928ada7d4051.png)](https://refactoringguru.github.io/design-patterns-dart/#/adapter/flutter_adapter) + +### Diagram: +![image](https://user-images.githubusercontent.com/8049534/152753162-1b9006ad-a633-4132-91b6-bb348559adec.png) + +### Sequence [Classic application -> Change Text color] +When user clicked to "Flutter Adapter" text. + +![image](https://user-images.githubusercontent.com/8049534/152753714-84af5abd-85c0-4845-af2d-616f512ef633.png) + +### Sequence [Flutter Widget -> Change Text color] +When the user has selected a color in the color bar. + +![image](https://user-images.githubusercontent.com/8049534/152753870-edeab3ae-8e79-4e9d-9049-7cd5a2100afa.png) + +### Client code: +```dart +// classic application +class App extends ClassicApp { + @override + void onPointerWheel(double deltaX, double deltaY) { + // ... + } + + @override + void onMouseDown() { + // ... + repaint(); + } + + @override + void onPaint(Canvas canvas, Size canvasSize) { + //... + } +} + +void main() { + // adapting a classic application to a flutter widget tree + ClassicAppAdapterWidget( + classicApp: app, + ); +} + +``` From 6f0d2cffe7a22f8644297de7ad2357c11499c108 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 10:56:08 +0200 Subject: [PATCH 202/479] Rename ClassicAppRepaint to RepaintEvent. --- .../flutter_adapter/adapter/classic_app_render_object.dart | 4 ++-- patterns/adapter/flutter_adapter/classic_app/classic_app.dart | 4 ++-- .../{classic_app_events.dart => repaint_event.dart} | 2 +- .../client_app/widgets/text_property_widget.dart | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) rename patterns/adapter/flutter_adapter/classic_app/{classic_app_events.dart => repaint_event.dart} (60%) diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart b/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart index 8704f22..4470082 100644 --- a/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart +++ b/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart @@ -2,7 +2,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import '../classic_app/classic_app.dart'; -import '../classic_app/classic_app_events.dart'; +import '../classic_app/repaint_event.dart'; class ClassicAppRenderObject extends RenderBox { ClassicAppRenderObject(ClassicApp classicApp) { @@ -58,7 +58,7 @@ class ClassicAppRenderObject extends RenderBox { _isSubscribe = true; } - void _clientAppRepaint(ClassicAppRepaint e) => markNeedsPaint(); + void _clientAppRepaint(RepaintEvent e) => markNeedsPaint(); @override void handleEvent(PointerEvent event, covariant BoxHitTestEntry entry) { diff --git a/patterns/adapter/flutter_adapter/classic_app/classic_app.dart b/patterns/adapter/flutter_adapter/classic_app/classic_app.dart index 05d915d..f19a5c2 100644 --- a/patterns/adapter/flutter_adapter/classic_app/classic_app.dart +++ b/patterns/adapter/flutter_adapter/classic_app/classic_app.dart @@ -1,7 +1,7 @@ import 'dart:ui'; import '../../../observer/app_observer/observer/app_observer.dart'; -import 'classic_app_events.dart'; +import 'repaint_event.dart'; import 'repaint_compatible.dart'; abstract class ClassicApp implements RepaintCompatible { @@ -13,7 +13,7 @@ abstract class ClassicApp implements RepaintCompatible { @override void repaint() { - events.notify(ClassicAppRepaint()); + events.notify(RepaintEvent()); } void onPaint(Canvas canvas, Size canvasSize); diff --git a/patterns/adapter/flutter_adapter/classic_app/classic_app_events.dart b/patterns/adapter/flutter_adapter/classic_app/repaint_event.dart similarity index 60% rename from patterns/adapter/flutter_adapter/classic_app/classic_app_events.dart rename to patterns/adapter/flutter_adapter/classic_app/repaint_event.dart index 655ff78..bece697 100644 --- a/patterns/adapter/flutter_adapter/classic_app/classic_app_events.dart +++ b/patterns/adapter/flutter_adapter/classic_app/repaint_event.dart @@ -1,3 +1,3 @@ import '../../../observer/app_observer/observer/event.dart'; -class ClassicAppRepaint extends Event {} +class RepaintEvent extends Event {} diff --git a/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart b/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart index 26fcb4b..d61dfc3 100644 --- a/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart +++ b/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; -import '../../classic_app/classic_app_events.dart'; +import '../../classic_app/repaint_event.dart'; import '../app.dart'; import 'color_buttons_widget.dart'; import 'slider_widget.dart'; @@ -17,7 +17,7 @@ class TextPropertyWidget extends StatelessWidget { @override Widget build(BuildContext context) { final app = classicApp; - return SubscriberWidget( + return SubscriberWidget( observer: app.events, builder: (context, event) { return Container( From 3581615fa63e9a04da6c0e04710d657f5bd4285f Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 10:56:48 +0200 Subject: [PATCH 203/479] Fix maxTextSize. --- .../client_app/business_rules/text_coloring.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/adapter/flutter_adapter/client_app/business_rules/text_coloring.dart b/patterns/adapter/flutter_adapter/client_app/business_rules/text_coloring.dart index 9167de0..af1e04e 100644 --- a/patterns/adapter/flutter_adapter/client_app/business_rules/text_coloring.dart +++ b/patterns/adapter/flutter_adapter/client_app/business_rules/text_coloring.dart @@ -19,7 +19,7 @@ class TextColoring { } if (newVal > maxTextSize) { - _size = newVal; + _size = maxTextSize; } else if (newVal < 1) { _size = 1; } else { From df4b972a1f2dff0e458c6b60c3e2ff4c3eb59ea4 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 10:57:28 +0200 Subject: [PATCH 204/479] Remove unnecessary Container. --- .../widgets/text_property_widget.dart | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart b/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart index d61dfc3..3f96ccf 100644 --- a/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart +++ b/patterns/adapter/flutter_adapter/client_app/widgets/text_property_widget.dart @@ -20,26 +20,24 @@ class TextPropertyWidget extends StatelessWidget { return SubscriberWidget( observer: app.events, builder: (context, event) { - return Container( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SliderWidget( - current: app.textColoring.size, - max: app.textColoring.maxTextSize, - onChange: (newVal) { - app.textColoring.size = newVal; - }, - ), - ColorButtonsWidget( - currentColor: app.textColoring.color, - colors: app.colorRules.colors, - onColorSelect: (color) { - app.textColoring.color = color; - }, - ), - ], - ), + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SliderWidget( + current: app.textColoring.size, + max: app.textColoring.maxTextSize, + onChange: (newVal) { + app.textColoring.size = newVal; + }, + ), + ColorButtonsWidget( + currentColor: app.textColoring.color, + colors: app.colorRules.colors, + onColorSelect: (color) { + app.textColoring.color = color; + }, + ), + ], ); }, ); From 0fb7981add2749d46a83c871c598f51a605550f0 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 10:57:54 +0200 Subject: [PATCH 205/479] Update main README.md. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f4216c6..d425215 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ It contains **Dart** examples for all classic **GoF** design patterns. - [ ] [**Visitor**](https://refactoring.guru/design-patterns/visitor) - [ ] [**Strategy**](https://refactoring.guru/design-patterns/strategy) - [ ] **Structural** - - [x] [**Adapter**](https://refactoring.guru/design-patterns/adapter) - [[Text Graphics](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/text_graphics)] [[Square Round conflict](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/square_round_conflict)] + - [x] [**Adapter**](https://refactoring.guru/design-patterns/adapter) - [[Text Graphics](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/text_graphics)] [[Square Round conflict](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/square_round_conflict)] [[Flutter Adapter](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/flutter_adapter)] - [x] [**Bridge**](https://refactoring.guru/design-patterns/bridge) - [[Remote Device Control](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/bridge/devices_remote_control)] [[Clock](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/bridge/clock)] - [x] [**Composite**](https://refactoring.guru/design-patterns/composite) - [[Image Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/composite/image_editor)] [[Products and Boxes](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/composite/products_and_boxes)] - [x] [**Decorator**](https://refactoring.guru/design-patterns/decorator) - [[Data Source Decoder](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/decorator/data_source_decoder)] From e17aa2f20ef4f6e0c4559ac993a519e660c1323b Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 10:58:22 +0200 Subject: [PATCH 206/479] Bump version 0.17.0. --- CHANGELOG.md | 6 ++++++ pubspec.yaml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 038fa50..b64d9c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.17.0 +Add "Adapter" pattern: adapt a non-reactive classic type application for Flutter. + +## 0.16.5 +Add deploy_flutter_demos script. + ## 0.16.0 - Add complex example of an Observer pattern, connected to a Flutter application. - Add new branch "web-demos" for online examples. diff --git a/pubspec.yaml b/pubspec.yaml index fcabe61..e9e46fe 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.16.0 +version: 0.17.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 188065cd28d8c7889294223e43875f88544ab112 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 12:59:57 +0200 Subject: [PATCH 207/479] Simplifying the ternary construction. --- .../command/text_editor/application/editor.dart | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/patterns/command/text_editor/application/editor.dart b/patterns/command/text_editor/application/editor.dart index ac167a5..5b77c9f 100644 --- a/patterns/command/text_editor/application/editor.dart +++ b/patterns/command/text_editor/application/editor.dart @@ -62,16 +62,10 @@ class Editor { @override String toString() { - return cursor.isTextSelected - ? _text.replaceRange( - cursor.startSelection, - cursor.endSelection, - '[$selectedText]', - ) - : _text.replaceRange( - cursor.position, - cursor.position, - '|', - ); + return _text.replaceRange( + cursor.startSelection, + cursor.endSelection, + cursor.isTextSelected ? '[$selectedText]' : '|', + ); } } From 4c0279bb25b0fd898151f4cd8d95a027ebe83e51 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 13:03:24 +0200 Subject: [PATCH 208/479] Remover multiline comment from main README. --- README.md | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d425215..8fe73e8 100644 --- a/README.md +++ b/README.md @@ -50,20 +50,16 @@ 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. 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. - */ + // 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. + 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. ### Build Flutter examples From 5f71a0c3b6f0d74366d3a8a66dd5c3928065dc76 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 13:09:07 +0200 Subject: [PATCH 209/479] Replace repository urls to relative. --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 8fe73e8..4f1156b 100644 --- a/README.md +++ b/README.md @@ -5,27 +5,27 @@ It contains **Dart** examples for all classic **GoF** design patterns. # Implementation checklist: - [ ] **Creation** - [ ] [**Abstract Factory**](https://refactoring.guru/design-patterns/abstract-factory) - - [x] [**Builder**](https://refactoring.guru/design-patterns/builder) - [[Color Text Format](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/builder/color_text_format)] + - [x] [**Builder**](https://refactoring.guru/design-patterns/builder) - [[Color Text Format](./design-patterns-dart/tree/master/patterns/builder/color_text_format)] - [ ] [**Factory Method**](https://refactoring.guru/design-patterns/factory-method) - - [x] [**Prototype**](https://refactoring.guru/design-patterns/prototype) - [[Shapes](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/prototype/shapes)] + - [x] [**Prototype**](https://refactoring.guru/design-patterns/prototype) - [[Shapes](./design-patterns-dart/tree/master/patterns/prototype/shapes)] - [ ] [**Singleton**](https://refactoring.guru/design-patterns/singleton) - [ ] **Behavioral** - - [x] [**Chain of Responsibility**](https://refactoring.guru/design-patterns/chain-of-esponsibility) - [[Server Middleware](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/chain_of_responsibility/server_middleware)] - - [x] [**Command**](https://refactoring.guru/design-patterns/command) - [[Text Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/command/text_editor)] + - [x] [**Chain of Responsibility**](https://refactoring.guru/design-patterns/chain-of-esponsibility) - [[Server Middleware](./design-patterns-dart/tree/master/patterns/chain_of_responsibility/server_middleware)] + - [x] [**Command**](https://refactoring.guru/design-patterns/command) - [[Text Editor](./design-patterns-dart/tree/master/patterns/command/text_editor)] - [ ] Interpreter - [ ] [**Iterator**](https://refactoring.guru/design-patterns/iterator) - [ ] [**Mediator**](https://refactoring.guru/design-patterns/mediator) - - [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)] [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer)] [[Subscriber Flutter Widget](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] + - [x] [**Memento**](https://refactoring.guru/design-patterns/memento) - [[Conceptual](./design-patterns-dart/tree/master/patterns/memento/conceptual)] + - [x] [**Observer**](https://refactoring.guru/design-patterns/observer) - [[Open-Close Editor Events](./design-patterns-dart/tree/master/patterns/observer/open_close_editor_events)] [[AppObserver](./design-patterns-dart/tree/master/patterns/observer/app_observer)] [[Subscriber Flutter Widget](./design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] - [ ] [**State**](https://refactoring.guru/design-patterns/state) - [ ] [**Template Method**](https://refactoring.guru/design-patterns/template-method) - [ ] [**Visitor**](https://refactoring.guru/design-patterns/visitor) - [ ] [**Strategy**](https://refactoring.guru/design-patterns/strategy) - [ ] **Structural** - - [x] [**Adapter**](https://refactoring.guru/design-patterns/adapter) - [[Text Graphics](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/text_graphics)] [[Square Round conflict](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/square_round_conflict)] [[Flutter Adapter](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/flutter_adapter)] - - [x] [**Bridge**](https://refactoring.guru/design-patterns/bridge) - [[Remote Device Control](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/bridge/devices_remote_control)] [[Clock](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/bridge/clock)] - - [x] [**Composite**](https://refactoring.guru/design-patterns/composite) - [[Image Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/composite/image_editor)] [[Products and Boxes](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/composite/products_and_boxes)] - - [x] [**Decorator**](https://refactoring.guru/design-patterns/decorator) - [[Data Source Decoder](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/decorator/data_source_decoder)] + - [x] [**Adapter**](https://refactoring.guru/design-patterns/adapter) - [[Text Graphics](./design-patterns-dart/tree/master/patterns/adapter/text_graphics)] [[Square Round conflict](./design-patterns-dart/tree/master/patterns/adapter/square_round_conflict)] [[Flutter Adapter](./design-patterns-dart/tree/master/patterns/adapter/flutter_adapter)] + - [x] [**Bridge**](https://refactoring.guru/design-patterns/bridge) - [[Remote Device Control](./design-patterns-dart/tree/master/patterns/bridge/devices_remote_control)] [[Clock](./design-patterns-dart/tree/master/patterns/bridge/clock)] + - [x] [**Composite**](https://refactoring.guru/design-patterns/composite) - [[Image Editor](./design-patterns-dart/tree/master/patterns/composite/image_editor)] [[Products and Boxes](./design-patterns-dart/tree/master/patterns/composite/products_and_boxes)] + - [x] [**Decorator**](https://refactoring.guru/design-patterns/decorator) - [[Data Source Decoder](./design-patterns-dart/tree/master/patterns/decorator/data_source_decoder)] - [ ] [**Facade**](https://refactoring.guru/design-patterns/facade) - [ ] [**Flyweight**](https://refactoring.guru/design-patterns/flyweight) - [ ] [**Proxy**](https://refactoring.guru/design-patterns/proxy) @@ -51,11 +51,11 @@ Here's a style guide which might help you to keep your changes consistent with o 5. 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. + // 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 From 33835cf36e29c430cb2932ef344895b84195100a Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 13:14:50 +0200 Subject: [PATCH 210/479] Fix relative urls. --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 4f1156b..72c78a9 100644 --- a/README.md +++ b/README.md @@ -5,27 +5,27 @@ It contains **Dart** examples for all classic **GoF** design patterns. # Implementation checklist: - [ ] **Creation** - [ ] [**Abstract Factory**](https://refactoring.guru/design-patterns/abstract-factory) - - [x] [**Builder**](https://refactoring.guru/design-patterns/builder) - [[Color Text Format](./design-patterns-dart/tree/master/patterns/builder/color_text_format)] + - [x] [**Builder**](https://refactoring.guru/design-patterns/builder) - [[Color Text Format](/design-patterns-dart/tree/master/patterns/builder/color_text_format)] - [ ] [**Factory Method**](https://refactoring.guru/design-patterns/factory-method) - - [x] [**Prototype**](https://refactoring.guru/design-patterns/prototype) - [[Shapes](./design-patterns-dart/tree/master/patterns/prototype/shapes)] + - [x] [**Prototype**](https://refactoring.guru/design-patterns/prototype) - [[Shapes](/design-patterns-dart/tree/master/patterns/prototype/shapes)] - [ ] [**Singleton**](https://refactoring.guru/design-patterns/singleton) - [ ] **Behavioral** - - [x] [**Chain of Responsibility**](https://refactoring.guru/design-patterns/chain-of-esponsibility) - [[Server Middleware](./design-patterns-dart/tree/master/patterns/chain_of_responsibility/server_middleware)] - - [x] [**Command**](https://refactoring.guru/design-patterns/command) - [[Text Editor](./design-patterns-dart/tree/master/patterns/command/text_editor)] + - [x] [**Chain of Responsibility**](https://refactoring.guru/design-patterns/chain-of-esponsibility) - [[Server Middleware](/design-patterns-dart/tree/master/patterns/chain_of_responsibility/server_middleware)] + - [x] [**Command**](https://refactoring.guru/design-patterns/command) - [[Text Editor](/design-patterns-dart/tree/master/patterns/command/text_editor)] - [ ] Interpreter - [ ] [**Iterator**](https://refactoring.guru/design-patterns/iterator) - [ ] [**Mediator**](https://refactoring.guru/design-patterns/mediator) - - [x] [**Memento**](https://refactoring.guru/design-patterns/memento) - [[Conceptual](./design-patterns-dart/tree/master/patterns/memento/conceptual)] - - [x] [**Observer**](https://refactoring.guru/design-patterns/observer) - [[Open-Close Editor Events](./design-patterns-dart/tree/master/patterns/observer/open_close_editor_events)] [[AppObserver](./design-patterns-dart/tree/master/patterns/observer/app_observer)] [[Subscriber Flutter Widget](./design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] + - [x] [**Memento**](https://refactoring.guru/design-patterns/memento) - [[Conceptual](/design-patterns-dart/tree/master/patterns/memento/conceptual)] + - [x] [**Observer**](https://refactoring.guru/design-patterns/observer) - [[Open-Close Editor Events](/design-patterns-dart/tree/master/patterns/observer/open_close_editor_events)] [[AppObserver](/design-patterns-dart/tree/master/patterns/observer/app_observer)] [[Subscriber Flutter Widget](/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] - [ ] [**State**](https://refactoring.guru/design-patterns/state) - [ ] [**Template Method**](https://refactoring.guru/design-patterns/template-method) - [ ] [**Visitor**](https://refactoring.guru/design-patterns/visitor) - [ ] [**Strategy**](https://refactoring.guru/design-patterns/strategy) - [ ] **Structural** - - [x] [**Adapter**](https://refactoring.guru/design-patterns/adapter) - [[Text Graphics](./design-patterns-dart/tree/master/patterns/adapter/text_graphics)] [[Square Round conflict](./design-patterns-dart/tree/master/patterns/adapter/square_round_conflict)] [[Flutter Adapter](./design-patterns-dart/tree/master/patterns/adapter/flutter_adapter)] - - [x] [**Bridge**](https://refactoring.guru/design-patterns/bridge) - [[Remote Device Control](./design-patterns-dart/tree/master/patterns/bridge/devices_remote_control)] [[Clock](./design-patterns-dart/tree/master/patterns/bridge/clock)] - - [x] [**Composite**](https://refactoring.guru/design-patterns/composite) - [[Image Editor](./design-patterns-dart/tree/master/patterns/composite/image_editor)] [[Products and Boxes](./design-patterns-dart/tree/master/patterns/composite/products_and_boxes)] - - [x] [**Decorator**](https://refactoring.guru/design-patterns/decorator) - [[Data Source Decoder](./design-patterns-dart/tree/master/patterns/decorator/data_source_decoder)] + - [x] [**Adapter**](https://refactoring.guru/design-patterns/adapter) - [[Text Graphics](/design-patterns-dart/tree/master/patterns/adapter/text_graphics)] [[Square Round conflict](/design-patterns-dart/tree/master/patterns/adapter/square_round_conflict)] [[Flutter Adapter](/design-patterns-dart/tree/master/patterns/adapter/flutter_adapter)] + - [x] [**Bridge**](https://refactoring.guru/design-patterns/bridge) - [[Remote Device Control](/design-patterns-dart/tree/master/patterns/bridge/devices_remote_control)] [[Clock](/design-patterns-dart/tree/master/patterns/bridge/clock)] + - [x] [**Composite**](https://refactoring.guru/design-patterns/composite) - [[Image Editor](/design-patterns-dart/tree/master/patterns/composite/image_editor)] [[Products and Boxes](/design-patterns-dart/tree/master/patterns/composite/products_and_boxes)] + - [x] [**Decorator**](https://refactoring.guru/design-patterns/decorator) - [[Data Source Decoder](/design-patterns-dart/tree/master/patterns/decorator/data_source_decoder)] - [ ] [**Facade**](https://refactoring.guru/design-patterns/facade) - [ ] [**Flyweight**](https://refactoring.guru/design-patterns/flyweight) - [ ] [**Proxy**](https://refactoring.guru/design-patterns/proxy) From fd8f4d782ec2674969d1355a3f704419fd49a688 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 13:30:03 +0200 Subject: [PATCH 211/479] Fix name on diagram "Builder", rename JsonFormat to JsonConverter. --- patterns/builder/color_text_format/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/builder/color_text_format/README.md b/patterns/builder/color_text_format/README.md index fd8707a..dd03358 100644 --- a/patterns/builder/color_text_format/README.md +++ b/patterns/builder/color_text_format/README.md @@ -8,7 +8,7 @@ https://refactoring.guru/design-patterns/builder Using different text formats. ### Class diagram -![image](https://user-images.githubusercontent.com/8049534/146023073-5d7644a4-d3b9-4420-bffe-f72ac3fd83dd.png) +![image](https://user-images.githubusercontent.com/8049534/152779648-13301589-27c7-4818-966b-ad1773384997.png) ### Client code: ```dart From 422e72050b112ea315dc3e33cba0b62570f15eb6 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 13:35:22 +0200 Subject: [PATCH 212/479] Select pattern "Command" at the diagram. --- patterns/builder/color_text_format/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/builder/color_text_format/README.md b/patterns/builder/color_text_format/README.md index dd03358..4e949c5 100644 --- a/patterns/builder/color_text_format/README.md +++ b/patterns/builder/color_text_format/README.md @@ -8,7 +8,7 @@ https://refactoring.guru/design-patterns/builder Using different text formats. ### Class diagram -![image](https://user-images.githubusercontent.com/8049534/152779648-13301589-27c7-4818-966b-ad1773384997.png) +![image](https://user-images.githubusercontent.com/8049534/152780645-4eed27fc-a483-432e-99b0-4a77f2adbd5d.png) ### Client code: ```dart From d77cf4652096d69ff824d3bc8035fe4e04c5d776 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 13:39:10 +0200 Subject: [PATCH 213/479] Add client code to "Bridge" pattern. --- .../bridge/devices_remote_control/README.md | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/patterns/bridge/devices_remote_control/README.md b/patterns/bridge/devices_remote_control/README.md index c0166f1..ab12e86 100644 --- a/patterns/bridge/devices_remote_control/README.md +++ b/patterns/bridge/devices_remote_control/README.md @@ -2,13 +2,38 @@ This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/master/src/refactoring_guru/bridge/example) But removed the ability to use null for devices. Instead, the EmptyDevice class is used. -**Diagram:** +### Remote device example + +Example describe this: https://refactoring.guru/design-patterns/bridge?#pseudocode + +### Diagram: ![image](https://user-images.githubusercontent.com/8049534/145878324-3cbc52f5-51f4-4642-921d-69fbe2886f8c.png) -**Description:** +### Client code: + +```batch +void main() { + testDevice(Tv()); + testDevice(Radio()); + testDevice(EmptyDevice()); +} -https://refactoring.guru/design-patterns/bridge?#pseudocode +void testDevice(Device device) { + print(''.padRight(36, '=')); + print(device.runtimeType); + print("Tests with basic remote."); + final basicRemote = BasicRemote.fromDevice(device); + basicRemote.power(); + device.printStatus(); + + print("Tests with advanced remote."); + final advancedRemote = AdvancedRemote.fromDevice(device); + advancedRemote.power(); + advancedRemote.mute(); + device.printStatus(); +} +``` **Output:** From 721b2d0b379bbb4dc337b18d488691e4cc1f6415 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 13:44:33 +0200 Subject: [PATCH 214/479] Fix url position & add example name to ProductsAndBoxes example. --- patterns/composite/products_and_boxes/README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/patterns/composite/products_and_boxes/README.md b/patterns/composite/products_and_boxes/README.md index 4d3c647..c85d59d 100644 --- a/patterns/composite/products_and_boxes/README.md +++ b/patterns/composite/products_and_boxes/README.md @@ -2,9 +2,14 @@ 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: +## Example: Products and boxes ![problem-en](https://user-images.githubusercontent.com/8049534/147579298-0c60c4a7-6acb-4ab3-a973-e06524c5a061.png) -https://refactoring.guru/design-patterns/composite?#problem + +For example, imagine that you have two types of objects: Products and Boxes. A Box can contain +several Products as well as a number of smaller Boxes. These little Boxes can also hold some +Products or even smaller Boxes, and so on. + +Full description can be found [here](https://refactoring.guru/design-patterns/composite?#problem) ### Folder description: - `/products` - represent product and box (composite pattern) From 6b7a3fda51366af98abcc4bc8c47974c5a0a1b07 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 13:48:10 +0200 Subject: [PATCH 215/479] Add example name to Decorator pattern. --- patterns/decorator/data_source_decoder/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/patterns/decorator/data_source_decoder/README.md b/patterns/decorator/data_source_decoder/README.md index 7300eb0..95bf53c 100644 --- a/patterns/decorator/data_source_decoder/README.md +++ b/patterns/decorator/data_source_decoder/README.md @@ -1,13 +1,15 @@ # 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. -## Example Description: -https://refactoring.guru/design-patterns/decorator?#pseudocode +## Data source decoder example +In this example, the Decorator pattern lets you compress and encrypt sensitive data independently +from the code that actually uses this data. + +Full description can be found [here](https://refactoring.guru/design-patterns/decorator?#pseudocode) ### Diagram: ![image](https://user-images.githubusercontent.com/8049534/148954932-edc22d7b-becd-4e2f-bae8-d0d8200d8918.png) - ### Client code: ```dart void main() { From aa77eb3790a13520a1e8b712a38ffa03719d0e2a Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 13:49:19 +0200 Subject: [PATCH 216/479] Format app_observer.dart. --- patterns/observer/app_observer/main.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/patterns/observer/app_observer/main.dart b/patterns/observer/app_observer/main.dart index 620fb21..459bb9e 100644 --- a/patterns/observer/app_observer/main.dart +++ b/patterns/observer/app_observer/main.dart @@ -14,7 +14,6 @@ void main() { print('First'); }); - observer.subscribe((SecondEvent e) { print('Second'); }); From 7afc17d96167d376865c84e7df05e9f5f5a0b225 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 13:56:25 +0200 Subject: [PATCH 217/479] Connect Application to events on the AppObserver diagram. --- patterns/observer/app_observer/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/observer/app_observer/README.md b/patterns/observer/app_observer/README.md index 4e509eb..b1caf8e 100644 --- a/patterns/observer/app_observer/README.md +++ b/patterns/observer/app_observer/README.md @@ -9,7 +9,7 @@ This example was created to be used in a more complex example. A complex example will be implemented later. ### Diagram: -![image](https://user-images.githubusercontent.com/8049534/152049751-b111e02a-1d33-4796-810c-b7ed069cecdc.png) +![image](https://user-images.githubusercontent.com/8049534/152783537-0c39c119-2e5b-44fb-9be1-a88840c7f7a1.png) ### Sequence ![image](https://user-images.githubusercontent.com/8049534/152049996-72131655-402d-4b92-b5d0-10e3f2dd0e79.png) From 4195bf600fc1478329221e37c6fed4f47e2d6e28 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 13:59:39 +0200 Subject: [PATCH 218/479] Split into separate files the "Memento" project. --- patterns/memento/conceptual/app/command.dart | 25 ++++++ patterns/memento/conceptual/app/editor.dart | 28 ++++++ patterns/memento/conceptual/app/snapshot.dart | 33 +++++++ patterns/memento/conceptual/main.dart | 85 +------------------ 4 files changed, 89 insertions(+), 82 deletions(-) create mode 100644 patterns/memento/conceptual/app/command.dart create mode 100644 patterns/memento/conceptual/app/editor.dart create mode 100644 patterns/memento/conceptual/app/snapshot.dart diff --git a/patterns/memento/conceptual/app/command.dart b/patterns/memento/conceptual/app/command.dart new file mode 100644 index 0000000..61730b5 --- /dev/null +++ b/patterns/memento/conceptual/app/command.dart @@ -0,0 +1,25 @@ +import 'editor.dart'; +import 'snapshot.dart'; + +/// 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(); + } +} diff --git a/patterns/memento/conceptual/app/editor.dart b/patterns/memento/conceptual/app/editor.dart new file mode 100644 index 0000000..e0c3226 --- /dev/null +++ b/patterns/memento/conceptual/app/editor.dart @@ -0,0 +1,28 @@ +import 'snapshot.dart'; + +/// 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); + } +} diff --git a/patterns/memento/conceptual/app/snapshot.dart b/patterns/memento/conceptual/app/snapshot.dart new file mode 100644 index 0000000..a53e596 --- /dev/null +++ b/patterns/memento/conceptual/app/snapshot.dart @@ -0,0 +1,33 @@ +import 'editor.dart'; + +/// 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; + } +} diff --git a/patterns/memento/conceptual/main.dart b/patterns/memento/conceptual/main.dart index 4590559..07a0486 100644 --- a/patterns/memento/conceptual/main.dart +++ b/patterns/memento/conceptual/main.dart @@ -1,3 +1,6 @@ +import 'app/command.dart'; +import 'app/editor.dart'; + void main() { final editor = Editor('New Document'); final firstState = Command.makeBackup(editor); @@ -12,85 +15,3 @@ void main() { 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 630804501c217462b0ef2860061ab178531e0dc3 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 14:02:40 +0200 Subject: [PATCH 219/479] Remover unnecessary import. --- patterns/composite/products_and_boxes/diagram/diagram.dart | 1 - .../render_elements/render_connecting_lines.dart | 2 -- .../products_and_boxes/render_elements/render_text.dart | 1 - 3 files changed, 4 deletions(-) diff --git a/patterns/composite/products_and_boxes/diagram/diagram.dart b/patterns/composite/products_and_boxes/diagram/diagram.dart index 097ab8b..2d40c65 100644 --- a/patterns/composite/products_and_boxes/diagram/diagram.dart +++ b/patterns/composite/products_and_boxes/diagram/diagram.dart @@ -1,5 +1,4 @@ import 'package:design_patterns_dart/text_canvas.dart'; -import 'package:design_patterns_dart/text_canvas/primitives.dart'; import '../products/product.dart'; import '../render_elements/render_connecting_lines.dart'; 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 1f3d0cc..e1e7ce5 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 @@ -1,6 +1,4 @@ import 'package:design_patterns_dart/text_canvas.dart'; -import 'package:design_patterns_dart/text_canvas/canvas.dart'; -import 'package:design_patterns_dart/text_canvas/primitives.dart'; import '../render_elements/render_column.dart'; import 'render_element.dart'; 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 e7d8d1c..78a60d7 100644 --- a/patterns/composite/products_and_boxes/render_elements/render_text.dart +++ b/patterns/composite/products_and_boxes/render_elements/render_text.dart @@ -1,5 +1,4 @@ import 'package:design_patterns_dart/text_canvas.dart'; -import 'package:design_patterns_dart/text_canvas/primitives.dart'; import 'render_element.dart'; From a3398ad98236711470bc8c5f15fa3f76e24c6649 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 14:53:30 +0200 Subject: [PATCH 220/479] Move hash generator to application. --- .../observer/subscriber_flutter_widget/main.dart | 10 +++++++++- .../widgets/hash_generator_widget.dart | 14 ++------------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/patterns/observer/subscriber_flutter_widget/main.dart b/patterns/observer/subscriber_flutter_widget/main.dart index 87f7e35..4423ccf 100644 --- a/patterns/observer/subscriber_flutter_widget/main.dart +++ b/patterns/observer/subscriber_flutter_widget/main.dart @@ -1,5 +1,8 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; import '../app_observer/observer/app_observer.dart'; +import 'events/new_hash_event.dart'; import 'widgets/hash_generator_widget.dart'; import 'widgets/hash_user_widget.dart'; @@ -19,10 +22,15 @@ class _SubscriberFlutterAppState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ HashUserWidget(observer: observer), - HashGeneratorWidget(observer: observer), + HashGeneratorWidget(onHashGenerate: onHashGenerate), ], ), ), ); } + + void onHashGenerate() { + final hash = Random().nextDouble().hashCode.toString(); + observer.notify(NewHashEvent(hash)); + } } diff --git a/patterns/observer/subscriber_flutter_widget/widgets/hash_generator_widget.dart b/patterns/observer/subscriber_flutter_widget/widgets/hash_generator_widget.dart index e19caa2..cc65ac7 100644 --- a/patterns/observer/subscriber_flutter_widget/widgets/hash_generator_widget.dart +++ b/patterns/observer/subscriber_flutter_widget/widgets/hash_generator_widget.dart @@ -1,16 +1,11 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; -import '../../app_observer/observer/app_observer.dart'; -import '../events/new_hash_event.dart'; - class HashGeneratorWidget extends StatelessWidget { - final AppObserver observer; + final void Function() onHashGenerate; const HashGeneratorWidget({ Key? key, - required this.observer, + required this.onHashGenerate, }) : super(key: key); @override @@ -20,9 +15,4 @@ class HashGeneratorWidget extends StatelessWidget { onPressed: onHashGenerate, ); } - - void onHashGenerate() { - final hash = Random().nextDouble().hashCode.toString(); - observer.notify(NewHashEvent(hash)); - } } From 908eee1703ca366960734e7091d257efce4a2e41 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 14:54:02 +0200 Subject: [PATCH 221/479] Update SubscriberWidget diagram. --- .../subscriber_flutter_widget/README.md | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/patterns/observer/subscriber_flutter_widget/README.md b/patterns/observer/subscriber_flutter_widget/README.md index 71ca3b9..a2863a4 100644 --- a/patterns/observer/subscriber_flutter_widget/README.md +++ b/patterns/observer/subscriber_flutter_widget/README.md @@ -4,27 +4,24 @@ multiple objects about any events that happen to the object they’re observing. Tutorial: [here](https://refactoring.guru/design-patterns/observer). -## Subscriber Flutter Widget example +## Subscriber flutter widget example This is a complex example of an Observer pattern, connected to a Flutter application. The example includes, -the previously implemented **AppObserver** example pattern, which you can see [here](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer). +the previously implemented **AppObserver** example pattern, +which you can see [here](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer). -### Diagram: -![image](https://user-images.githubusercontent.com/8049534/152418114-f040bcb4-3bf5-4581-8b9b-a264e103ff76.png) +### Online demo: +Click on the picture to see a [demo](https://ilopx.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget). -### Sequence -![image](https://user-images.githubusercontent.com/8049534/152418299-3f6f3e5d-7132-42bc-9b27-11ed53ca6434.png) +[![image](https://user-images.githubusercontent.com/8049534/152419178-f40a07fd-728d-4f99-befa-0935bbdd7b71.png)](https://ilopx.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget) ### Client code: ```dart void main() { - // widget that generates a new event - ElevatedButton( - child: Text('Generate new hash'), - onPressed: () { - final hash = 'some hash'; + // generates a new event + void onHashGenerate() { + final hash = '...'; observer.notify(NewHashEvent(hash)); - }, - ); + } // widget event consumer NewHashEvent SubscriberWidget( @@ -38,7 +35,8 @@ void main() { } ``` -### Online demo: -Click on the picture to see a [demo](https://ilopx.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget). +### Diagram: +![image](https://user-images.githubusercontent.com/8049534/152789070-cc53b0c0-bb5c-4191-ac96-f542ce75c1d7.png) -[![image](https://user-images.githubusercontent.com/8049534/152419178-f40a07fd-728d-4f99-befa-0935bbdd7b71.png)](https://ilopx.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget) +### Sequence +![image](https://user-images.githubusercontent.com/8049534/152791540-d6ec1d24-a3a1-4340-8805-10df6de12067.png) From f1449b0b33cc96ed974402eae3b4797b1b277023 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 14:55:52 +0200 Subject: [PATCH 222/479] Fix url position in FlutterAdapter README.md. --- patterns/adapter/flutter_adapter/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/patterns/adapter/flutter_adapter/README.md b/patterns/adapter/flutter_adapter/README.md index f7c7ef0..d68af02 100644 --- a/patterns/adapter/flutter_adapter/README.md +++ b/patterns/adapter/flutter_adapter/README.md @@ -14,6 +14,7 @@ This complex example includes these implementations: ### Online demo: Click on the picture to see a [demo](https://refactoringguru.github.io/design-patterns-dart/#/adapter/flutter_adapter). + [![image](https://user-images.githubusercontent.com/8049534/152689272-d4bed484-e216-4eda-8833-928ada7d4051.png)](https://refactoringguru.github.io/design-patterns-dart/#/adapter/flutter_adapter) ### Diagram: From 227850ddcc577bc04c04a8c104938bb6f6c24369 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 14:57:33 +0200 Subject: [PATCH 223/479] Move client code to top. --- patterns/adapter/flutter_adapter/README.md | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/patterns/adapter/flutter_adapter/README.md b/patterns/adapter/flutter_adapter/README.md index d68af02..41320ff 100644 --- a/patterns/adapter/flutter_adapter/README.md +++ b/patterns/adapter/flutter_adapter/README.md @@ -17,19 +17,6 @@ Click on the picture to see a [demo](https://refactoringguru.github.io/design-pa [![image](https://user-images.githubusercontent.com/8049534/152689272-d4bed484-e216-4eda-8833-928ada7d4051.png)](https://refactoringguru.github.io/design-patterns-dart/#/adapter/flutter_adapter) -### Diagram: -![image](https://user-images.githubusercontent.com/8049534/152753162-1b9006ad-a633-4132-91b6-bb348559adec.png) - -### Sequence [Classic application -> Change Text color] -When user clicked to "Flutter Adapter" text. - -![image](https://user-images.githubusercontent.com/8049534/152753714-84af5abd-85c0-4845-af2d-616f512ef633.png) - -### Sequence [Flutter Widget -> Change Text color] -When the user has selected a color in the color bar. - -![image](https://user-images.githubusercontent.com/8049534/152753870-edeab3ae-8e79-4e9d-9049-7cd5a2100afa.png) - ### Client code: ```dart // classic application @@ -57,5 +44,17 @@ void main() { classicApp: app, ); } - ``` + +### Diagram: +![image](https://user-images.githubusercontent.com/8049534/152753162-1b9006ad-a633-4132-91b6-bb348559adec.png) + +### Sequence [Classic application -> Change Text color] +When user clicked to "Flutter Adapter" text. + +![image](https://user-images.githubusercontent.com/8049534/152753714-84af5abd-85c0-4845-af2d-616f512ef633.png) + +### Sequence [Flutter Widget -> Change Text color] +When the user has selected a color in the color bar. + +![image](https://user-images.githubusercontent.com/8049534/152753870-edeab3ae-8e79-4e9d-9049-7cd5a2100afa.png) From a269ea455841e2573756ec4db0dd54fc0e48974d Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 7 Feb 2022 15:02:10 +0200 Subject: [PATCH 224/479] Bump version 0.17.16. --- CHANGELOG.md | 18 ++++++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b64d9c2..b55e030 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## 0.17.16 - refactoring +- Simplifying the ternary construction. +- Remove multiline comment from main README. +- Replace repository urls to relative. +- Fix relative urls. +- Fix name on diagram "Builder", rename JsonFormat to JsonConverter. +- Select pattern "Command" at the diagram. +- Add client code to "Bridge" pattern. +- Fix url position & add example name to ProductsAndBoxes example. +- Add example name to Decorator pattern. +- Format app_observer.dart. +- Connect Application to events on the AppObserver diagram. +- Split into separate files the "Memento" project. +- Remover unnecessary import. +- Move hash generator to application. +- Update SubscriberWidget diagram. +- Fix url position in FlutterAdapter README.md. + ## 0.17.0 Add "Adapter" pattern: adapt a non-reactive classic type application for Flutter. diff --git a/pubspec.yaml b/pubspec.yaml index e9e46fe..9499ccb 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.17.0 +version: 0.17.16 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 1819043f6fe6a87da9fc777bea8c7e729ad3dada Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 8 Feb 2022 14:33:14 +0200 Subject: [PATCH 225/479] Fix FlutterDemo url. --- patterns/observer/subscriber_flutter_widget/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/observer/subscriber_flutter_widget/README.md b/patterns/observer/subscriber_flutter_widget/README.md index a2863a4..4db5713 100644 --- a/patterns/observer/subscriber_flutter_widget/README.md +++ b/patterns/observer/subscriber_flutter_widget/README.md @@ -12,7 +12,7 @@ which you can see [here](https://github.com/RefactoringGuru/design-patterns-dart ### Online demo: Click on the picture to see a [demo](https://ilopx.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget). -[![image](https://user-images.githubusercontent.com/8049534/152419178-f40a07fd-728d-4f99-befa-0935bbdd7b71.png)](https://ilopx.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget) +[![image](https://user-images.githubusercontent.com/8049534/152419178-f40a07fd-728d-4f99-befa-0935bbdd7b71.png)](https://refactoringguru.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget) ### Client code: ```dart From fc6ef8ac2c64a7b6f36edb9316a2eb82a9d40d4f Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 8 Feb 2022 17:02:19 +0200 Subject: [PATCH 226/479] Fix urls. Change ilopX host to RefactoringGuru. --- README.md | 20 +++++++++---------- .../subscriber_flutter_widget/README.md | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 72c78a9..8b61bce 100644 --- a/README.md +++ b/README.md @@ -5,27 +5,27 @@ It contains **Dart** examples for all classic **GoF** design patterns. # Implementation checklist: - [ ] **Creation** - [ ] [**Abstract Factory**](https://refactoring.guru/design-patterns/abstract-factory) - - [x] [**Builder**](https://refactoring.guru/design-patterns/builder) - [[Color Text Format](/design-patterns-dart/tree/master/patterns/builder/color_text_format)] + - [x] [**Builder**](https://refactoring.guru/design-patterns/builder) - [[Color Text Format](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/builder/color_text_format)] - [ ] [**Factory Method**](https://refactoring.guru/design-patterns/factory-method) - - [x] [**Prototype**](https://refactoring.guru/design-patterns/prototype) - [[Shapes](/design-patterns-dart/tree/master/patterns/prototype/shapes)] + - [x] [**Prototype**](https://refactoring.guru/design-patterns/prototype) - [[Shapes](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/prototype/shapes)] - [ ] [**Singleton**](https://refactoring.guru/design-patterns/singleton) - [ ] **Behavioral** - - [x] [**Chain of Responsibility**](https://refactoring.guru/design-patterns/chain-of-esponsibility) - [[Server Middleware](/design-patterns-dart/tree/master/patterns/chain_of_responsibility/server_middleware)] - - [x] [**Command**](https://refactoring.guru/design-patterns/command) - [[Text Editor](/design-patterns-dart/tree/master/patterns/command/text_editor)] + - [x] [**Chain of Responsibility**](https://refactoring.guru/design-patterns/chain-of-esponsibility) - [[Server Middleware](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/chain_of_responsibility/server_middleware)] + - [x] [**Command**](https://refactoring.guru/design-patterns/command) - [[Text Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/command/text_editor)] - [ ] Interpreter - [ ] [**Iterator**](https://refactoring.guru/design-patterns/iterator) - [ ] [**Mediator**](https://refactoring.guru/design-patterns/mediator) - - [x] [**Memento**](https://refactoring.guru/design-patterns/memento) - [[Conceptual](/design-patterns-dart/tree/master/patterns/memento/conceptual)] - - [x] [**Observer**](https://refactoring.guru/design-patterns/observer) - [[Open-Close Editor Events](/design-patterns-dart/tree/master/patterns/observer/open_close_editor_events)] [[AppObserver](/design-patterns-dart/tree/master/patterns/observer/app_observer)] [[Subscriber Flutter Widget](/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] + - [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)] [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer)] [[Subscriber Flutter Widget](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] - [ ] [**State**](https://refactoring.guru/design-patterns/state) - [ ] [**Template Method**](https://refactoring.guru/design-patterns/template-method) - [ ] [**Visitor**](https://refactoring.guru/design-patterns/visitor) - [ ] [**Strategy**](https://refactoring.guru/design-patterns/strategy) - [ ] **Structural** - - [x] [**Adapter**](https://refactoring.guru/design-patterns/adapter) - [[Text Graphics](/design-patterns-dart/tree/master/patterns/adapter/text_graphics)] [[Square Round conflict](/design-patterns-dart/tree/master/patterns/adapter/square_round_conflict)] [[Flutter Adapter](/design-patterns-dart/tree/master/patterns/adapter/flutter_adapter)] - - [x] [**Bridge**](https://refactoring.guru/design-patterns/bridge) - [[Remote Device Control](/design-patterns-dart/tree/master/patterns/bridge/devices_remote_control)] [[Clock](/design-patterns-dart/tree/master/patterns/bridge/clock)] - - [x] [**Composite**](https://refactoring.guru/design-patterns/composite) - [[Image Editor](/design-patterns-dart/tree/master/patterns/composite/image_editor)] [[Products and Boxes](/design-patterns-dart/tree/master/patterns/composite/products_and_boxes)] - - [x] [**Decorator**](https://refactoring.guru/design-patterns/decorator) - [[Data Source Decoder](/design-patterns-dart/tree/master/patterns/decorator/data_source_decoder)] + - [x] [**Adapter**](https://refactoring.guru/design-patterns/adapter) - [[Text Graphics](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/text_graphics)] [[Square Round conflict](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/square_round_conflict)] [[Flutter Adapter](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/flutter_adapter)] + - [x] [**Bridge**](https://refactoring.guru/design-patterns/bridge) - [[Remote Device Control](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/bridge/devices_remote_control)] [[Clock](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/bridge/clock)] + - [x] [**Composite**](https://refactoring.guru/design-patterns/composite) - [[Image Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/composite/image_editor)] [[Products and Boxes](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/composite/products_and_boxes)] + - [x] [**Decorator**](https://refactoring.guru/design-patterns/decorator) - [[Data Source Decoder](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/decorator/data_source_decoder)] - [ ] [**Facade**](https://refactoring.guru/design-patterns/facade) - [ ] [**Flyweight**](https://refactoring.guru/design-patterns/flyweight) - [ ] [**Proxy**](https://refactoring.guru/design-patterns/proxy) diff --git a/patterns/observer/subscriber_flutter_widget/README.md b/patterns/observer/subscriber_flutter_widget/README.md index 4db5713..f1b586e 100644 --- a/patterns/observer/subscriber_flutter_widget/README.md +++ b/patterns/observer/subscriber_flutter_widget/README.md @@ -10,7 +10,7 @@ the previously implemented **AppObserver** example pattern, which you can see [here](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer). ### Online demo: -Click on the picture to see a [demo](https://ilopx.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget). +Click on the picture to see a [demo](https://RefactoringGuru.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget). [![image](https://user-images.githubusercontent.com/8049534/152419178-f40a07fd-728d-4f99-befa-0935bbdd7b71.png)](https://refactoringguru.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget) From 0786980e1018a9a19f1685f91711b0fe70d89d62 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 8 Feb 2022 17:05:55 +0200 Subject: [PATCH 227/479] Fix all "Client code" text convert to h3. --- patterns/bridge/devices_remote_control/README.md | 3 +-- patterns/command/text_editor/README.md | 2 +- patterns/composite/image_editor/README.md | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/patterns/bridge/devices_remote_control/README.md b/patterns/bridge/devices_remote_control/README.md index ab12e86..12bd5ed 100644 --- a/patterns/bridge/devices_remote_control/README.md +++ b/patterns/bridge/devices_remote_control/README.md @@ -11,8 +11,7 @@ Example describe this: https://refactoring.guru/design-patterns/bridge?#pseudoco ![image](https://user-images.githubusercontent.com/8049534/145878324-3cbc52f5-51f4-4642-921d-69fbe2886f8c.png) ### Client code: - -```batch +```dart void main() { testDevice(Tv()); testDevice(Radio()); diff --git a/patterns/command/text_editor/README.md b/patterns/command/text_editor/README.md index a8248e8..52c2260 100644 --- a/patterns/command/text_editor/README.md +++ b/patterns/command/text_editor/README.md @@ -17,7 +17,7 @@ But slightly changed, see the class diagram. ## Diagram: ![image](https://user-images.githubusercontent.com/8049534/149918792-fccae912-2e67-4068-88d1-5cf824f0df2c.png) -## Client code: +### Client code: ```dart void main() { final app = Application(); diff --git a/patterns/composite/image_editor/README.md b/patterns/composite/image_editor/README.md index de15d64..1dc597c 100644 --- a/patterns/composite/image_editor/README.md +++ b/patterns/composite/image_editor/README.md @@ -7,10 +7,10 @@ https://refactoring.guru/design-patterns/composite?#pseudocode ## Origin source code: This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/master/src/refactoring_guru/composite/example) -## Diagram: +### Diagram: ![image](https://user-images.githubusercontent.com/8049534/149174388-25ca21b1-d762-40b5-a853-528abe18b66c.png) -**Client code:** +### Client code: ```dart void main() { final editor = ImageEditor(); From 379ed44e63401fc5632829eb98b591599fe1c12e Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 8 Feb 2022 17:49:59 +0200 Subject: [PATCH 228/479] Remove refactoringGuru urls from main README. --- README.md | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 8b61bce..87b9ef5 100644 --- a/README.md +++ b/README.md @@ -4,31 +4,31 @@ It contains **Dart** examples for all classic **GoF** design patterns. # Implementation checklist: - [ ] **Creation** - - [ ] [**Abstract Factory**](https://refactoring.guru/design-patterns/abstract-factory) - - [x] [**Builder**](https://refactoring.guru/design-patterns/builder) - [[Color Text Format](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/builder/color_text_format)] - - [ ] [**Factory Method**](https://refactoring.guru/design-patterns/factory-method) - - [x] [**Prototype**](https://refactoring.guru/design-patterns/prototype) - [[Shapes](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/prototype/shapes)] - - [ ] [**Singleton**](https://refactoring.guru/design-patterns/singleton) + - [ ] **Abstract Factory** + - [x] **Builder** - [[Color Text Format](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/builder/color_text_format)] + - [ ] **Factory Method** + - [x] **Prototype**] - [[Shapes](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/prototype/shapes)] + - [ ] **Singleton** - [ ] **Behavioral** - - [x] [**Chain of Responsibility**](https://refactoring.guru/design-patterns/chain-of-esponsibility) - [[Server Middleware](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/chain_of_responsibility/server_middleware)] - - [x] [**Command**](https://refactoring.guru/design-patterns/command) - [[Text Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/command/text_editor)] + - [x] **Chain of Responsibility** - [[Server Middleware](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/chain_of_responsibility/server_middleware)] + - [x] **Command** - [[Text Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/command/text_editor)] - [ ] Interpreter - - [ ] [**Iterator**](https://refactoring.guru/design-patterns/iterator) - - [ ] [**Mediator**](https://refactoring.guru/design-patterns/mediator) - - [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)] [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer)] [[Subscriber Flutter Widget](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] - - [ ] [**State**](https://refactoring.guru/design-patterns/state) - - [ ] [**Template Method**](https://refactoring.guru/design-patterns/template-method) - - [ ] [**Visitor**](https://refactoring.guru/design-patterns/visitor) - - [ ] [**Strategy**](https://refactoring.guru/design-patterns/strategy) + - [ ] **Iterator** + - [ ] **Mediator** + - [x] **Memento** - [[Conceptual](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/memento/conceptual)] + - [x] **Observer** - [[Open-Close Editor Events](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/open_close_editor_events)] [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer)] [[Subscriber Flutter Widget](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] + - [ ] **State** + - [ ] **Template Method** + - [ ] **Visitor** + - [ ] **Strategy** - [ ] **Structural** - - [x] [**Adapter**](https://refactoring.guru/design-patterns/adapter) - [[Text Graphics](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/text_graphics)] [[Square Round conflict](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/square_round_conflict)] [[Flutter Adapter](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/flutter_adapter)] - - [x] [**Bridge**](https://refactoring.guru/design-patterns/bridge) - [[Remote Device Control](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/bridge/devices_remote_control)] [[Clock](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/bridge/clock)] - - [x] [**Composite**](https://refactoring.guru/design-patterns/composite) - [[Image Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/composite/image_editor)] [[Products and Boxes](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/composite/products_and_boxes)] - - [x] [**Decorator**](https://refactoring.guru/design-patterns/decorator) - [[Data Source Decoder](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/decorator/data_source_decoder)] - - [ ] [**Facade**](https://refactoring.guru/design-patterns/facade) - - [ ] [**Flyweight**](https://refactoring.guru/design-patterns/flyweight) - - [ ] [**Proxy**](https://refactoring.guru/design-patterns/proxy) + - [x] **Adapter** - [[Text Graphics](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/text_graphics)] [[Square Round conflict](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/square_round_conflict)] [[Flutter Adapter](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/flutter_adapter)] + - [x] **Bridge** - [[Remote Device Control](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/bridge/devices_remote_control)] [[Clock](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/bridge/clock)] + - [x] **Composite** - [[Image Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/composite/image_editor)] [[Products and Boxes](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/composite/products_and_boxes)] + - [x] **Decorator** - [[Data Source Decoder](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/decorator/data_source_decoder)] + - [ ] **Facade** + - [ ] **Flyweight** + - [ ] **Proxy** ## Requirements @@ -69,7 +69,7 @@ flutter build web -t bin\main.dart --web-renderer html ``` ### Deploy flutter demos -1. Fork this repo: https://github.com/RefactoringGuru/design-patterns-dart +1. Fork this repo: `https://github.com/RefactoringGuru/design-patterns-dart` 2. Apply your changes. 3. Run the script `dart bin\deploy_flutter_demos.dart`. This script will build a web platform flutter app and push the changes to your **web-demos** branch on github. From b1dbf934ee38dd9a636d66ab8cd5b249ead7106f Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 13 Feb 2022 17:40:03 +0200 Subject: [PATCH 229/479] Add empty classic flutter app. --- bin/main.dart | 4 ++- .../memento/memento_editor/application.dart | 13 ++++++++ patterns/memento/memento_editor/main.dart | 31 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 patterns/memento/memento_editor/application.dart create mode 100644 patterns/memento/memento_editor/main.dart diff --git a/bin/main.dart b/bin/main.dart index 2a23b38..935d592 100644 --- a/bin/main.dart +++ b/bin/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../patterns/observer/subscriber_flutter_widget/main.dart'; import '../patterns/adapter/flutter_adapter/main.dart'; +import '../patterns/memento/memento_editor/main.dart'; void main() { runApp(MyApp()); @@ -12,10 +13,11 @@ class MyApp extends StatelessWidget { return MaterialApp( title: 'Refactoring Guru: Flutter launcher', theme: ThemeData(primarySwatch: Colors.pink), - initialRoute: '/adapter/flutter_adapter', + initialRoute: '/memento/flutter_memento_editor', routes: { '/observer/subscriber_flutter_widget': (_) => SubscriberFlutterApp(), '/adapter/flutter_adapter': (_) => FlutterAdapterApp(), + '/memento/flutter_memento_editor': (_) => FlutterMementoEditorApp(), }, ); } diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart new file mode 100644 index 0000000..c448933 --- /dev/null +++ b/patterns/memento/memento_editor/application.dart @@ -0,0 +1,13 @@ +import 'dart:ui'; + +import '../../adapter/flutter_adapter/classic_app/classic_app.dart'; + +class MementoEditorApplication extends ClassicApp { + @override + void onPaint(Canvas canvas, Size canvasSize) { + canvas.drawRect( + Offset.zero & canvasSize, + Paint()..color = Color(0xff2ac932), + ); + } +} diff --git a/patterns/memento/memento_editor/main.dart b/patterns/memento/memento_editor/main.dart new file mode 100644 index 0000000..d09c661 --- /dev/null +++ b/patterns/memento/memento_editor/main.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +import '../../adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart' as adapter; +import 'application.dart'; +import 'widgets/right_panel_widget.dart'; + +class FlutterMementoEditorApp extends StatefulWidget { + @override + State createState() => _FlutterMementoEditorAppState(); +} + +class _FlutterMementoEditorAppState extends State { + final app = MementoEditorApplication(); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Row( + children: [ + Expanded( + child: adapter.ClassicAppAdapterWidget( + classicApp: app, + ), + ), + RightPanelWidget(), + ], + ), + ); + } +} + From 087e7c9a168fa7d635cb8ebbfef52501ff322b34 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 13 Feb 2022 19:35:58 +0200 Subject: [PATCH 230/479] Add flutter right panel properties. --- .../widgets/right_panel_widget.dart | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 patterns/memento/memento_editor/widgets/right_panel_widget.dart diff --git a/patterns/memento/memento_editor/widgets/right_panel_widget.dart b/patterns/memento/memento_editor/widgets/right_panel_widget.dart new file mode 100644 index 0000000..42c5486 --- /dev/null +++ b/patterns/memento/memento_editor/widgets/right_panel_widget.dart @@ -0,0 +1,171 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class RightPanelWidget extends StatelessWidget { + RightPanelWidget({Key? key}) : super(key: key); + + final colors = [ + Color(0xFFFFFFFF), + Color(0xFFD81B60), + Color(0xFF5E35B1), + Color(0xFF1E88E5), + Color(0xFF43A047), + ]; + + static const rowHeight = 60.0; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 300, + child: Column( + children: [ + Padding( + padding: EdgeInsets.only(left: 20, right: 20, top: 20, bottom: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'SHAPE PROPERTIES', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 20), + Row( + children: [ + _buildNumberField('x:'), + SizedBox(width: 20), + _buildNumberField('y:'), + ], + ), + SizedBox(height: 20), + _buildNumberField('size:'), + SizedBox(height: 20), + Padding( + padding: EdgeInsets.symmetric(vertical: 14), + child: Row( + children: [ + Text('color:'), + SizedBox(width: 10), + ...colors + .map( + (color) => Container( + color: color, + width: 20, + height: 20, + ), + ) + .toList(), + ], + ), + ), + ], + ), + ), + Container( + height: 2, + color: Colors.grey.withOpacity(0.5), + ), + Expanded( + child: Padding( + padding: + EdgeInsets.only(left: 20, right: 20, top: 15, bottom: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'MEMENTO', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 10), + Text( + '1. Select the shape.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 5), + Text( + '2. Change color, size or position.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 5), + Text( + '3. Click the "save state" button.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 5), + Text( + 'Now you can restore states by selecting them from the list.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + OutlinedButton( + child: Text('Save state'), + onPressed: () {}, + ), + ], + ), + SizedBox(height: 5), + Expanded( + child: ColoredBox( + color: Colors.white, + child: Material( + type: MaterialType.transparency, + child: ListView( + padding: EdgeInsets.all(5), + children: [ + ColoredBox( + color: Colors.black.withOpacity(0.02), + child: ListTile( + leading: Container( + color: Colors.green, + width: 50, + height: double.infinity, + child: Icon(Icons.animation), + ), + title: Text('Snapshot 1'), + subtitle: Text('fefefefsfsdfas'), + onTap: () {}, + ), + ) + ], + ), + ), + ), + ), + ], + ), + ), + ) + ], + ), + ); + } + + Widget _buildNumberField(String name) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(name), + SizedBox(width: 10), + SizedBox( + width: 50, + child: TextField( + controller: TextEditingController(text: '0'), + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + ), + ), + ), + ], + ); + } +} From 026ea37584c5d103a052ca42d397fd0f52221472 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 13 Feb 2022 19:38:12 +0200 Subject: [PATCH 231/479] Add fake shape painter. --- .../memento/memento_editor/application.dart | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index c448933..fe8eecb 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -1,5 +1,8 @@ +import 'dart:math'; import 'dart:ui'; +import 'package:flutter/cupertino.dart'; + import '../../adapter/flutter_adapter/classic_app/classic_app.dart'; class MementoEditorApplication extends ClassicApp { @@ -7,7 +10,26 @@ class MementoEditorApplication extends ClassicApp { void onPaint(Canvas canvas, Size canvasSize) { canvas.drawRect( Offset.zero & canvasSize, - Paint()..color = Color(0xff2ac932), + Paint()..color = Color(0xff404040), ); + + final paintStroke = Paint() + ..style = PaintingStyle.stroke + ..color = Color(0xFFD81B60); + + final paintFill = Paint() + ..style = PaintingStyle.fill + ..color = Color(0xffffffff); + + const positionRadius = 300.0; + for (var i = 0; i < 5; i++) { + final x = + positionRadius / 2 + positionRadius + cos(i * 8) * positionRadius; + final y = + positionRadius / 2 + positionRadius + sin(i * 8) * positionRadius; + + canvas.drawCircle(Offset(x, y), 60, paintFill); + canvas.drawCircle(Offset(x, y), 60, paintStroke); + } } } From 4b9695b8bbe2c5ba4e5d43911eb9764b7fffe083 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 10:41:56 +0200 Subject: [PATCH 232/479] Add mouseUp & mouseMove events. --- .../adapter/classic_app_render_object.dart | 13 ++++++++----- .../flutter_adapter/classic_app/classic_app.dart | 6 +++++- .../adapter/flutter_adapter/client_app/app.dart | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart b/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart index 4470082..0811cda 100644 --- a/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart +++ b/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart @@ -63,13 +63,16 @@ class ClassicAppRenderObject extends RenderBox { @override void handleEvent(PointerEvent event, covariant BoxHitTestEntry entry) { if (event is PointerHoverEvent || event is PointerMoveEvent) { - } else if (event is PointerDownEvent) { - if (event.buttons == kPrimaryMouseButton) { - _classicApp.onMouseDown(); - } else if (event.buttons == kSecondaryMouseButton) {} - else if (event.buttons == kMiddleMouseButton) {} + _classicApp.onMouseMove(event.position.dx, event.position.dy); } else if (event is PointerScrollEvent) { _classicApp.onPointerWheel(event.scrollDelta.dx, event.scrollDelta.dy); + } else if (event is PointerDownEvent) { + if (event.buttons == kPrimaryMouseButton) { + _classicApp.onMouseDown(event.position.dx, event.position.dy); + } else if (event.buttons == kSecondaryMouseButton) { + } else if (event.buttons == kMiddleMouseButton) {} + } else if (event is PointerUpEvent) { + _classicApp.onMouseUp(); } } diff --git a/patterns/adapter/flutter_adapter/classic_app/classic_app.dart b/patterns/adapter/flutter_adapter/classic_app/classic_app.dart index f19a5c2..7e56111 100644 --- a/patterns/adapter/flutter_adapter/classic_app/classic_app.dart +++ b/patterns/adapter/flutter_adapter/classic_app/classic_app.dart @@ -7,7 +7,11 @@ import 'repaint_compatible.dart'; abstract class ClassicApp implements RepaintCompatible { final events = AppObserver(); - void onMouseDown() {} + void onMouseDown(double x, double y) {} + + void onMouseUp() {} + + void onMouseMove(double x, double y) {} void onPointerWheel(double deltaX, double deltaY) {} diff --git a/patterns/adapter/flutter_adapter/client_app/app.dart b/patterns/adapter/flutter_adapter/client_app/app.dart index b648c75..7a4f5a7 100644 --- a/patterns/adapter/flutter_adapter/client_app/app.dart +++ b/patterns/adapter/flutter_adapter/client_app/app.dart @@ -19,7 +19,7 @@ class App extends ClassicApp { } @override - void onMouseDown() { + void onMouseDown(_, __) { textColoring.color = colorRules.nextColor(textColoring.color); } From a515748d43e87d4909966d2d185261d38f469cef Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 10:43:10 +0200 Subject: [PATCH 233/479] Create Editor class. --- .../memento/memento_editor/application.dart | 36 +----- .../memento/memento_editor/editor/editor.dart | 116 ++++++++++++++++++ patterns/memento/memento_editor/main.dart | 2 +- 3 files changed, 120 insertions(+), 34 deletions(-) create mode 100644 patterns/memento/memento_editor/editor/editor.dart diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index fe8eecb..e6d4ac2 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -1,35 +1,5 @@ -import 'dart:math'; -import 'dart:ui'; +import 'editor/editor.dart'; -import 'package:flutter/cupertino.dart'; - -import '../../adapter/flutter_adapter/classic_app/classic_app.dart'; - -class MementoEditorApplication extends ClassicApp { - @override - void onPaint(Canvas canvas, Size canvasSize) { - canvas.drawRect( - Offset.zero & canvasSize, - Paint()..color = Color(0xff404040), - ); - - final paintStroke = Paint() - ..style = PaintingStyle.stroke - ..color = Color(0xFFD81B60); - - final paintFill = Paint() - ..style = PaintingStyle.fill - ..color = Color(0xffffffff); - - const positionRadius = 300.0; - for (var i = 0; i < 5; i++) { - final x = - positionRadius / 2 + positionRadius + cos(i * 8) * positionRadius; - final y = - positionRadius / 2 + positionRadius + sin(i * 8) * positionRadius; - - canvas.drawCircle(Offset(x, y), 60, paintFill); - canvas.drawCircle(Offset(x, y), 60, paintStroke); - } - } +class MementoEditorApplication { + final editor = Editor(); } diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart new file mode 100644 index 0000000..9ea7f6c --- /dev/null +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -0,0 +1,116 @@ +import 'dart:math'; +import 'dart:ui'; + +import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; + +class Circle { + double x; + double y; + var color = Color(0xFFFFFFFF); + var size = 60.0; + + Circle(this.x, this.y); + + static final paintStroke = Paint() + ..style = PaintingStyle.stroke + ..color = Color(0xFFD81B60) + ..strokeWidth = 2; + + void paint(Canvas canvas) { + final paintFill = Paint() + ..style = PaintingStyle.fill + ..color = color; + + canvas.drawCircle(Offset(x, y), size, paintFill); + canvas.drawCircle(Offset(x, y), size, paintStroke); + } + + bool isBounded(double x, double y) { + return ((x - this.x) * (x - this.x) + (y - this.y) * (y - this.y) <= + size * size); + } +} + +class Editor extends ClassicApp { + final shapes = []; + + Editor() { + const positionRadius = 300.0; + for (var i = 0; i < 7; i++) { + final x = 60 + positionRadius + cos(i / 1.15) * positionRadius; + final y = 60 + positionRadius + sin(i / 1.15) * positionRadius; + shapes.add(Circle(x, y)); + } + } + + var isMouseDown = false; + + var d = 60.0; + + Circle? selectedCircle; + + @override + void onMouseDown(double x, double y) { + isMouseDown = true; + for (final circle in shapes) { + if (circle.isBounded(x, y)) { + selectedCircle = circle; + xStart = circle.x - x; + yStart = circle.y - y; + repaint(); + return; + } + } + selectedCircle = null; + repaint(); + } + + var xStart = 0.0; + var yStart = 0.0; + + @override + void onMouseMove(double x, double y) { + if (isMouseDown && selectedCircle != null) { + selectedCircle!.x = x + xStart; + selectedCircle!.y = y + yStart; + repaint(); + } + } + + @override + void onPointerWheel(double deltaX, double deltaY) { + if (selectedCircle != null) { + selectedCircle!.size -= deltaY / 5; + repaint(); + } + } + + @override + void onMouseUp() { + isMouseDown = false; + } + + @override + void onPaint(Canvas canvas, Size canvasSize) { + canvas.drawRect( + Offset.zero & canvasSize, + Paint()..color = Color(0xff404040), + ); + + for (final circle in shapes.reversed) { + circle.paint(canvas); + } + + if (selectedCircle != null) { + final circleSize = selectedCircle!.size; + final x = (selectedCircle!.x - circleSize).roundToDouble() - 1.5; + final y = (selectedCircle!.y - circleSize).roundToDouble() - 1.5; + canvas.drawRect( + Rect.fromLTWH(x, y, circleSize * 2 + 3, circleSize * 2 + 3), + Paint() + ..style = PaintingStyle.stroke + ..color = Color(0xff26e6ff), + ); + } + } +} diff --git a/patterns/memento/memento_editor/main.dart b/patterns/memento/memento_editor/main.dart index d09c661..3e20002 100644 --- a/patterns/memento/memento_editor/main.dart +++ b/patterns/memento/memento_editor/main.dart @@ -19,7 +19,7 @@ class _FlutterMementoEditorAppState extends State { children: [ Expanded( child: adapter.ClassicAppAdapterWidget( - classicApp: app, + classicApp: app.editor, ), ), RightPanelWidget(), From 6b8924532b00188da86c3ba43a01e230ef0eaccf Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 10:43:44 +0200 Subject: [PATCH 234/479] Add black color to palette. --- .../memento/memento_editor/widgets/right_panel_widget.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patterns/memento/memento_editor/widgets/right_panel_widget.dart b/patterns/memento/memento_editor/widgets/right_panel_widget.dart index 42c5486..4ec69e4 100644 --- a/patterns/memento/memento_editor/widgets/right_panel_widget.dart +++ b/patterns/memento/memento_editor/widgets/right_panel_widget.dart @@ -1,11 +1,11 @@ -import 'package:flutter/cupertino.dart'; + import 'package:flutter/material.dart'; class RightPanelWidget extends StatelessWidget { RightPanelWidget({Key? key}) : super(key: key); final colors = [ - Color(0xFFFFFFFF), + Color(0xFF000000), Color(0xFFD81B60), Color(0xFF5E35B1), Color(0xFF1E88E5), From 301906e1fd4934e16841611b1a19b1d87cb8a542 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 20:53:23 +0200 Subject: [PATCH 235/479] Add shape class. --- .../memento/memento_editor/editor/shape.dart | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 patterns/memento/memento_editor/editor/shape.dart diff --git a/patterns/memento/memento_editor/editor/shape.dart b/patterns/memento/memento_editor/editor/shape.dart new file mode 100644 index 0000000..a7c4a3f --- /dev/null +++ b/patterns/memento/memento_editor/editor/shape.dart @@ -0,0 +1,40 @@ +import 'dart:ui'; + +class Shape { + double x; + double y; + var color = Color(0xFFFFFFFF); + var _size = 60.0; + + double get size => _size; + + set size(double newSize) { + if (newSize < 10) { + newSize = 10; + } else if (newSize > 200) { + newSize = 200; + } + _size = newSize; + } + + Shape(this.x, this.y); + + static final paintStroke = Paint() + ..style = PaintingStyle.stroke + ..color = Color(0xFFD81B60) + ..strokeWidth = 2; + + void paint(Canvas canvas) { + final paintFill = Paint() + ..style = PaintingStyle.fill + ..color = color; + + canvas.drawCircle(Offset(x, y), _size, paintFill); + canvas.drawCircle(Offset(x, y), _size, paintStroke); + } + + bool isBounded(double x, double y) { + return ((x - this.x) * (x - this.x) + (y - this.y) * (y - this.y) <= + _size * _size); + } +} From 3a5bb4a54775dd6d23feaa2bb6c75005d4cf9945 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 20:53:45 +0200 Subject: [PATCH 236/479] Add Shapes. --- .../memento/memento_editor/editor/shapes.dart | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 patterns/memento/memento_editor/editor/shapes.dart diff --git a/patterns/memento/memento_editor/editor/shapes.dart b/patterns/memento/memento_editor/editor/shapes.dart new file mode 100644 index 0000000..51b0645 --- /dev/null +++ b/patterns/memento/memento_editor/editor/shapes.dart @@ -0,0 +1,57 @@ +import 'dart:ui'; + +import 'shape.dart'; + +mixin Shapes { + final shapes = []; + + void paintShapes(Canvas canvas) { + for (final shape in shapes.reversed) { + shape.paint(canvas); + } + } + + Shape? _selectedShape; + + Shape? get selectedShape => _selectedShape; + + void select(double x, double y) { + _selectedShape = findCircle(x, y); + + if (_selectedShape != null) { + _xStart = _selectedShape!.x - x; + _yStart = _selectedShape!.y - y; + } + } + + void unSelect() { + _selectedShape = null; + } + + void changeSize(double delta) { + final currentSize = _selectedShape?.size; + + if (currentSize != null) { + final newSize = currentSize - delta; + if (newSize != _selectedShape!.size) { + _selectedShape!.size = newSize; + } + } + } + + void drag(double x, double y) { + _selectedShape!.x = x + _xStart; + _selectedShape!.y = y + _yStart; + } + + var _xStart = 0.0; + var _yStart = 0.0; + + Shape? findCircle(double x, double y) { + for (final shape in shapes) { + if (shape.isBounded(x, y)) { + return shape; + } + } + } +} From 95ff8c68b30e58b5af7b33eda64f5c67ebb570a3 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 20:54:01 +0200 Subject: [PATCH 237/479] Add Manipulator. --- .../memento_editor/editor/manipulator.dart | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 patterns/memento/memento_editor/editor/manipulator.dart diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart new file mode 100644 index 0000000..0598a1c --- /dev/null +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -0,0 +1,61 @@ +import 'dart:ui'; + +import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; +import 'shapes.dart'; + +mixin Manipulator implements ClassicApp, Shapes { + void paintSelectFrame(Canvas canvas) { + if (selectedShape != null) { + final shapeSize = selectedShape!.size; + final x = (selectedShape!.x - shapeSize).roundToDouble() - 1.5; + final y = (selectedShape!.y - shapeSize).roundToDouble() - 1.5; + canvas.drawRect( + Rect.fromLTWH(x, y, shapeSize * 2 + 3, shapeSize * 2 + 3), + Paint() + ..style = PaintingStyle.stroke + ..color = Color(0xff26e6ff), + ); + } + } + + var _isMouseDown = false; + + @override + void onMouseDown(double x, double y) { + _isMouseDown = true; + final currSelection = selectedShape; + + select(x, y); + + if (currSelection == selectedShape) { + return; + } + + if (selectedShape == null) { + unSelect(); + } + + repaint(); + } + + @override + void onMouseMove(double x, double y) { + if (_isMouseDown && selectedShape != null) { + drag(x, y); + repaint(); + } + } + + @override + void onPointerWheel(double deltaX, double deltaY) { + if (selectedShape != null) { + changeSize(deltaY / 5); + repaint(); + } + } + + @override + void onMouseUp() { + _isMouseDown = false; + } +} From 4676aa475bbe0c8898c7c4f9c71bfc23635db34a Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 20:54:22 +0200 Subject: [PATCH 238/479] Add ColorsWidget. --- .../memento_editor/widgets/colors_widget.dart | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 patterns/memento/memento_editor/widgets/colors_widget.dart diff --git a/patterns/memento/memento_editor/widgets/colors_widget.dart b/patterns/memento/memento_editor/widgets/colors_widget.dart new file mode 100644 index 0000000..9e62582 --- /dev/null +++ b/patterns/memento/memento_editor/widgets/colors_widget.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; + +import '../../../bridge/devices_remote_control/devices/radio.dart'; + +class ColorsWidget extends StatelessWidget { + final Color? currentColor; + final List colors; + final void Function(Color color) onColorSelect; + + const ColorsWidget({ + Key? key, + required this.currentColor, + required this.colors, + required this.onColorSelect, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Opacity( + opacity: currentColor == null ? 0.2 : 1.0, + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.black87), + ), + child: Row( + children: colors.map(_buildColorButton).toList(), + ), + ), + ); + } + + Widget _buildColorButton(Color color) { + final isColorSelect = (color == currentColor); + return GestureDetector( + onTap: () { + onColorSelect(color); + }, + child: Container( + width: 20, + height: 20, + color: color, + child: isColorSelect ? _buildSelectColorIcon() : null, + ), + ); + } + + Widget _buildSelectColorIcon() { + return Center( + child: Container( + width: 5, + height: 5, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.8), + borderRadius: BorderRadius.all(Radius.circular(2)), + border: Border.all(color: Colors.black.withOpacity(0.2))), + ), + ); + } +} From 879e467f3659a22e1b3d1b18fa36896ac6022f72 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 20:55:11 +0200 Subject: [PATCH 239/479] Add SubscriberWidget to property panel. --- .../widgets/right_panel_widget.dart | 119 +++++++++++------- 1 file changed, 72 insertions(+), 47 deletions(-) diff --git a/patterns/memento/memento_editor/widgets/right_panel_widget.dart b/patterns/memento/memento_editor/widgets/right_panel_widget.dart index 4ec69e4..83dc512 100644 --- a/patterns/memento/memento_editor/widgets/right_panel_widget.dart +++ b/patterns/memento/memento_editor/widgets/right_panel_widget.dart @@ -1,8 +1,14 @@ - import 'package:flutter/material.dart'; +import '../../../adapter/flutter_adapter/classic_app/repaint_event.dart'; +import '../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; +import '../editor/editor.dart'; +import 'colors_widget.dart'; + class RightPanelWidget extends StatelessWidget { - RightPanelWidget({Key? key}) : super(key: key); + final Editor editor; + + RightPanelWidget({Key? key, required this.editor}) : super(key: key); final colors = [ Color(0xFF000000), @@ -10,6 +16,7 @@ class RightPanelWidget extends StatelessWidget { Color(0xFF5E35B1), Color(0xFF1E88E5), Color(0xFF43A047), + Color(0xFFFFFFFF), ]; static const rowHeight = 60.0; @@ -22,47 +29,57 @@ class RightPanelWidget extends StatelessWidget { children: [ Padding( padding: EdgeInsets.only(left: 20, right: 20, top: 20, bottom: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'SHAPE PROPERTIES', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), - SizedBox(height: 20), - Row( - children: [ - _buildNumberField('x:'), - SizedBox(width: 20), - _buildNumberField('y:'), - ], - ), - SizedBox(height: 20), - _buildNumberField('size:'), - SizedBox(height: 20), - Padding( - padding: EdgeInsets.symmetric(vertical: 14), - child: Row( + child: SubscriberWidget( + observer: editor.events, + builder: (buildContext, event) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('color:'), - SizedBox(width: 10), - ...colors - .map( - (color) => Container( - color: color, - width: 20, - height: 20, + Text( + 'SHAPE PROPERTIES', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 20), + Row( + children: [ + _buildNumberField('x:', editor.selectedShape?.x), + SizedBox(width: 20), + _buildNumberField('y:', editor.selectedShape?.y), + ], + ), + SizedBox(height: 20), + _buildNumberField('size:', editor.selectedShape?.size), + SizedBox(height: 10), + Padding( + padding: EdgeInsets.symmetric(vertical: 14), + child: Row( + children: [ + Text( + 'color:', + style: TextStyle( + color: Colors.black.withOpacity( + editor.selectedShape == null ? 0.5 : 1.0, + ), + ), ), - ) - .toList(), + SizedBox(width: 10), + ColorsWidget( + currentColor: editor.selectedShape?.color, + colors: colors, + onColorSelect: (newColor) { + editor.selectedShape?.color = newColor; + editor.repaint(); + }, + ), + ], + ), + ), ], - ), - ), - ], - ), + ); + }), ), Container( height: 2, @@ -104,7 +121,7 @@ class RightPanelWidget extends StatelessWidget { ), SizedBox(height: 20), Row( - mainAxisAlignment: MainAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.start, children: [ OutlinedButton( child: Text('Save state'), @@ -143,24 +160,32 @@ class RightPanelWidget extends StatelessWidget { ], ), ), - ) + ), ], ), ); } - Widget _buildNumberField(String name) { + Widget _buildNumberField(String name, double? value) { return Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text(name), + Text( + name, + style: TextStyle( + color: Colors.black.withOpacity(value == null ? 0.5 : 1.0), + ), + ), SizedBox(width: 10), SizedBox( - width: 50, + width: 60, child: TextField( - controller: TextEditingController(text: '0'), + enabled: value != null, + controller: TextEditingController( + text: value == null ? '' : value.toStringAsFixed(0), + ), decoration: InputDecoration( - filled: true, + filled: value != null, fillColor: Colors.white, ), ), From a60f7414e2ca0ef0a5fc692d10bdce91f0f77303 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 20:55:58 +0200 Subject: [PATCH 240/479] Format main.dart. --- patterns/memento/memento_editor/main.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/patterns/memento/memento_editor/main.dart b/patterns/memento/memento_editor/main.dart index 3e20002..bee7de3 100644 --- a/patterns/memento/memento_editor/main.dart +++ b/patterns/memento/memento_editor/main.dart @@ -1,12 +1,14 @@ import 'package:flutter/material.dart'; -import '../../adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart' as adapter; +import '../../adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart' + as adapter; import 'application.dart'; import 'widgets/right_panel_widget.dart'; class FlutterMementoEditorApp extends StatefulWidget { @override - State createState() => _FlutterMementoEditorAppState(); + State createState() => + _FlutterMementoEditorAppState(); } class _FlutterMementoEditorAppState extends State { @@ -22,10 +24,9 @@ class _FlutterMementoEditorAppState extends State { classicApp: app.editor, ), ), - RightPanelWidget(), + RightPanelWidget(editor: app.editor), ], ), ); } } - From 4e837870e8144b17c39c4b161bbb1985c94bd8c2 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 20:57:34 +0200 Subject: [PATCH 241/479] Move Manipulator and Shape out of Editor. --- .../memento/memento_editor/editor/editor.dart | 104 ++---------------- .../memento_editor/widgets/colors_widget.dart | 2 - 2 files changed, 10 insertions(+), 96 deletions(-) diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index 9ea7f6c..1b66e4f 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -2,115 +2,31 @@ import 'dart:math'; import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; +import 'shape.dart'; +import 'manipulator.dart'; +import 'shapes.dart'; -class Circle { - double x; - double y; - var color = Color(0xFFFFFFFF); - var size = 60.0; - - Circle(this.x, this.y); - - static final paintStroke = Paint() - ..style = PaintingStyle.stroke - ..color = Color(0xFFD81B60) - ..strokeWidth = 2; - - void paint(Canvas canvas) { - final paintFill = Paint() - ..style = PaintingStyle.fill - ..color = color; - - canvas.drawCircle(Offset(x, y), size, paintFill); - canvas.drawCircle(Offset(x, y), size, paintStroke); - } - - bool isBounded(double x, double y) { - return ((x - this.x) * (x - this.x) + (y - this.y) * (y - this.y) <= - size * size); - } -} - -class Editor extends ClassicApp { - final shapes = []; - +class Editor extends ClassicApp with Manipulator, Shapes { Editor() { const positionRadius = 300.0; for (var i = 0; i < 7; i++) { final x = 60 + positionRadius + cos(i / 1.15) * positionRadius; final y = 60 + positionRadius + sin(i / 1.15) * positionRadius; - shapes.add(Circle(x, y)); + shapes.add(Shape(x, y)); } } - var isMouseDown = false; - - var d = 60.0; - - Circle? selectedCircle; - @override - void onMouseDown(double x, double y) { - isMouseDown = true; - for (final circle in shapes) { - if (circle.isBounded(x, y)) { - selectedCircle = circle; - xStart = circle.x - x; - yStart = circle.y - y; - repaint(); - return; - } - } - selectedCircle = null; - repaint(); - } - - var xStart = 0.0; - var yStart = 0.0; - - @override - void onMouseMove(double x, double y) { - if (isMouseDown && selectedCircle != null) { - selectedCircle!.x = x + xStart; - selectedCircle!.y = y + yStart; - repaint(); - } - } - - @override - void onPointerWheel(double deltaX, double deltaY) { - if (selectedCircle != null) { - selectedCircle!.size -= deltaY / 5; - repaint(); - } - } - - @override - void onMouseUp() { - isMouseDown = false; + void onPaint(Canvas canvas, Size canvasSize) { + _paintBackground(canvas, canvasSize); + paintShapes(canvas); + paintSelectFrame(canvas); } - @override - void onPaint(Canvas canvas, Size canvasSize) { + void _paintBackground(Canvas canvas, Size canvasSize) { canvas.drawRect( Offset.zero & canvasSize, Paint()..color = Color(0xff404040), ); - - for (final circle in shapes.reversed) { - circle.paint(canvas); - } - - if (selectedCircle != null) { - final circleSize = selectedCircle!.size; - final x = (selectedCircle!.x - circleSize).roundToDouble() - 1.5; - final y = (selectedCircle!.y - circleSize).roundToDouble() - 1.5; - canvas.drawRect( - Rect.fromLTWH(x, y, circleSize * 2 + 3, circleSize * 2 + 3), - Paint() - ..style = PaintingStyle.stroke - ..color = Color(0xff26e6ff), - ); - } } } diff --git a/patterns/memento/memento_editor/widgets/colors_widget.dart b/patterns/memento/memento_editor/widgets/colors_widget.dart index 9e62582..8aae037 100644 --- a/patterns/memento/memento_editor/widgets/colors_widget.dart +++ b/patterns/memento/memento_editor/widgets/colors_widget.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; -import '../../../bridge/devices_remote_control/devices/radio.dart'; - class ColorsWidget extends StatelessWidget { final Color? currentColor; final List colors; From b5246bfee784666d1dc6739dbff280ec3f0891a3 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 23:20:00 +0200 Subject: [PATCH 242/479] Add visible args to Shape. --- patterns/memento/memento_editor/editor/shape.dart | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/patterns/memento/memento_editor/editor/shape.dart b/patterns/memento/memento_editor/editor/shape.dart index a7c4a3f..28369c5 100644 --- a/patterns/memento/memento_editor/editor/shape.dart +++ b/patterns/memento/memento_editor/editor/shape.dart @@ -3,8 +3,8 @@ import 'dart:ui'; class Shape { double x; double y; - var color = Color(0xFFFFFFFF); - var _size = 60.0; + Color color; + double _size; double get size => _size; @@ -17,7 +17,12 @@ class Shape { _size = newSize; } - Shape(this.x, this.y); + Shape( + this.x, + this.y, [ + this.color = const Color(0xFFFFFFFF), + this._size = 60.0, + ]); static final paintStroke = Paint() ..style = PaintingStyle.stroke From 04dd1d68c6aa9c401a4e79db3a26b43de30a2e65 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 23:22:35 +0200 Subject: [PATCH 243/479] Add backup & replace editor to app. --- .../memento/memento_editor/application.dart | 1 + .../memento/memento_editor/editor/editor.dart | 46 ++++++++++++ patterns/memento/memento_editor/main.dart | 3 +- .../widgets/right_panel_widget.dart | 75 ++++++++++++------- 4 files changed, 95 insertions(+), 30 deletions(-) diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index e6d4ac2..4860989 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -2,4 +2,5 @@ import 'editor/editor.dart'; class MementoEditorApplication { final editor = Editor(); + final snapshots = []; } diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index 1b66e4f..b4efa16 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -1,4 +1,6 @@ +import 'dart:convert'; import 'dart:math'; +import 'dart:typed_data'; import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; @@ -6,7 +8,51 @@ import 'shape.dart'; import 'manipulator.dart'; import 'shapes.dart'; +typedef Snapshot = String; + + class Editor extends ClassicApp with Manipulator, Shapes { + Snapshot backup() { + final byteSize = shapes.length * 16; + final data = ByteData(byteSize); + var byteOffset = 0; + + for (final shape in shapes) { + data + ..setFloat32(byteOffset, shape.x) + ..setFloat32(byteOffset + 4, shape.y) + ..setInt32(byteOffset + 8, shape.color.value) + ..setFloat32(byteOffset + 12, shape.size); + byteOffset += 16; + } + + return data.buffer + .asUint8List().map((e) => e.toRadixString(16).padLeft(3, '0')) + .join(); + } + + void restore(Snapshot snapshot) { + final unBase = Base64Decoder().convert(snapshot); + final byteData = ByteData.sublistView(unBase); + final shapeCount = byteData.lengthInBytes ~/ 16; + + shapes.clear(); + var byteOffset = 0; + + for (var i = 0; i < shapeCount; i++) { + final shape = Shape( + byteData.getFloat32(byteOffset), + byteData.getFloat32(byteOffset + 4), + Color(byteData.getInt32(byteOffset + 8)), + byteData.getFloat32(byteOffset + 12), + ); + shapes.add(shape); + byteOffset += 16; + } + + repaint(); + } + Editor() { const positionRadius = 300.0; for (var i = 0; i < 7; i++) { diff --git a/patterns/memento/memento_editor/main.dart b/patterns/memento/memento_editor/main.dart index bee7de3..dc13777 100644 --- a/patterns/memento/memento_editor/main.dart +++ b/patterns/memento/memento_editor/main.dart @@ -5,6 +5,7 @@ import '../../adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart' import 'application.dart'; import 'widgets/right_panel_widget.dart'; + class FlutterMementoEditorApp extends StatefulWidget { @override State createState() => @@ -24,7 +25,7 @@ class _FlutterMementoEditorAppState extends State { classicApp: app.editor, ), ), - RightPanelWidget(editor: app.editor), + RightPanelWidget(app: app), ], ), ); diff --git a/patterns/memento/memento_editor/widgets/right_panel_widget.dart b/patterns/memento/memento_editor/widgets/right_panel_widget.dart index 83dc512..83dd8c3 100644 --- a/patterns/memento/memento_editor/widgets/right_panel_widget.dart +++ b/patterns/memento/memento_editor/widgets/right_panel_widget.dart @@ -2,13 +2,13 @@ import 'package:flutter/material.dart'; import '../../../adapter/flutter_adapter/classic_app/repaint_event.dart'; import '../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; -import '../editor/editor.dart'; +import '../application.dart'; import 'colors_widget.dart'; class RightPanelWidget extends StatelessWidget { - final Editor editor; + final MementoEditorApplication app; - RightPanelWidget({Key? key, required this.editor}) : super(key: key); + RightPanelWidget({Key? key, required this.app}) : super(key: key); final colors = [ Color(0xFF000000), @@ -30,7 +30,7 @@ class RightPanelWidget extends StatelessWidget { Padding( padding: EdgeInsets.only(left: 20, right: 20, top: 20, bottom: 10), child: SubscriberWidget( - observer: editor.events, + observer: app.editor.events, builder: (buildContext, event) { return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -45,13 +45,16 @@ class RightPanelWidget extends StatelessWidget { SizedBox(height: 20), Row( children: [ - _buildNumberField('x:', editor.selectedShape?.x), + _buildNumberField('x:', app.editor.selectedShape?.x), SizedBox(width: 20), - _buildNumberField('y:', editor.selectedShape?.y), + _buildNumberField('y:', app.editor.selectedShape?.y), ], ), SizedBox(height: 20), - _buildNumberField('size:', editor.selectedShape?.size), + _buildNumberField( + 'size:', + app.editor.selectedShape?.size, + ), SizedBox(height: 10), Padding( padding: EdgeInsets.symmetric(vertical: 14), @@ -61,17 +64,17 @@ class RightPanelWidget extends StatelessWidget { 'color:', style: TextStyle( color: Colors.black.withOpacity( - editor.selectedShape == null ? 0.5 : 1.0, + app.editor.selectedShape == null ? 0.5 : 1.0, ), ), ), SizedBox(width: 10), ColorsWidget( - currentColor: editor.selectedShape?.color, + currentColor: app.editor.selectedShape?.color, colors: colors, onColorSelect: (newColor) { - editor.selectedShape?.color = newColor; - editor.repaint(); + app.editor.selectedShape?.color = newColor; + app.editor.repaint(); }, ), ], @@ -125,7 +128,10 @@ class RightPanelWidget extends StatelessWidget { children: [ OutlinedButton( child: Text('Save state'), - onPressed: () {}, + onPressed: () { + app.snapshots.add(app.editor.backup()); + app.editor.repaint(); + }, ), ], ), @@ -135,24 +141,35 @@ class RightPanelWidget extends StatelessWidget { color: Colors.white, child: Material( type: MaterialType.transparency, - child: ListView( - padding: EdgeInsets.all(5), - children: [ - ColoredBox( - color: Colors.black.withOpacity(0.02), - child: ListTile( - leading: Container( - color: Colors.green, - width: 50, - height: double.infinity, - child: Icon(Icons.animation), - ), - title: Text('Snapshot 1'), - subtitle: Text('fefefefsfsdfas'), - onTap: () {}, + child: SubscriberWidget( + observer: app.editor.events, + builder: (_, __) => ListView( + padding: EdgeInsets.all(5), + children: [ + ...app.snapshots.map( + (e) { + return Container( + margin: EdgeInsets.only(bottom: 4), + color: Colors.black.withOpacity(0.02), + child: ListTile( + leading: Container( + color: Colors.green, + width: 50, + height: double.infinity, + child: Icon(Icons.animation), + ), + title: Text('Snapshot'), + subtitle: SingleChildScrollView( + child: Text(e), + scrollDirection: Axis.horizontal, + ), + onTap: () {}, + ), + ); + }, ), - ) - ], + ], + ), ), ), ), From 6edfaf293b15aa28857a3316fabbbed534369f84 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 15 Feb 2022 14:37:13 +0200 Subject: [PATCH 244/479] Fix deploy_flutter: remove "&" from commit text. --- bin/deploy_flutter_demos.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/deploy_flutter_demos.dart b/bin/deploy_flutter_demos.dart index b6e0509..387cd87 100644 --- a/bin/deploy_flutter_demos.dart +++ b/bin/deploy_flutter_demos.dart @@ -119,7 +119,7 @@ Future repositoryOriginUrl(Directory workingDir) async { Future lastProjectCommit() async { final rawCommit = await cmd('git log -1 --pretty=%B', workingDirectory: projectDir); - final formatCommit = rawCommit.replaceAll(' ', '_'); + final formatCommit = rawCommit.replaceAll(' ', '_').replaceAll('&', ''); return 'auto_commit:_$formatCommit'; } From 7d03cfd38d8ef047e798f430d9a2672cabed45b7 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 15 Feb 2022 22:31:48 +0200 Subject: [PATCH 245/479] Add separate project directories. --- .../memento/memento_editor/application.dart | 1 + .../memento/memento_editor/editor/editor.dart | 53 +---- .../memento_editor/editor/manipulator.dart | 2 +- .../memento_pattern/originator.dart | 53 +++++ .../{editor => shapes}/shape.dart | 0 .../{editor => shapes}/shapes.dart | 5 +- .../{ => composite}/colors_widget.dart | 9 +- .../widgets/composite/named_panel.dart | 30 +++ .../panels/shape_properties_widget.dart | 91 +++++++++ .../widgets/panels/snapshot_widget.dart | 92 +++++++++ .../widgets/right_panel_widget.dart | 192 +----------------- 11 files changed, 286 insertions(+), 242 deletions(-) create mode 100644 patterns/memento/memento_editor/memento_pattern/originator.dart rename patterns/memento/memento_editor/{editor => shapes}/shape.dart (100%) rename patterns/memento/memento_editor/{editor => shapes}/shapes.dart (92%) rename patterns/memento/memento_editor/widgets/{ => composite}/colors_widget.dart (85%) create mode 100644 patterns/memento/memento_editor/widgets/composite/named_panel.dart create mode 100644 patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart create mode 100644 patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index 4860989..5420f65 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -1,4 +1,5 @@ import 'editor/editor.dart'; +import 'memento_pattern/originator.dart'; class MementoEditorApplication { final editor = Editor(); diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index b4efa16..af306c6 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -1,58 +1,13 @@ -import 'dart:convert'; import 'dart:math'; -import 'dart:typed_data'; import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; -import 'shape.dart'; +import '../memento_pattern/originator.dart'; +import '../shapes/shape.dart'; import 'manipulator.dart'; -import 'shapes.dart'; - -typedef Snapshot = String; - - -class Editor extends ClassicApp with Manipulator, Shapes { - Snapshot backup() { - final byteSize = shapes.length * 16; - final data = ByteData(byteSize); - var byteOffset = 0; - - for (final shape in shapes) { - data - ..setFloat32(byteOffset, shape.x) - ..setFloat32(byteOffset + 4, shape.y) - ..setInt32(byteOffset + 8, shape.color.value) - ..setFloat32(byteOffset + 12, shape.size); - byteOffset += 16; - } - - return data.buffer - .asUint8List().map((e) => e.toRadixString(16).padLeft(3, '0')) - .join(); - } - - void restore(Snapshot snapshot) { - final unBase = Base64Decoder().convert(snapshot); - final byteData = ByteData.sublistView(unBase); - final shapeCount = byteData.lengthInBytes ~/ 16; - - shapes.clear(); - var byteOffset = 0; - - for (var i = 0; i < shapeCount; i++) { - final shape = Shape( - byteData.getFloat32(byteOffset), - byteData.getFloat32(byteOffset + 4), - Color(byteData.getInt32(byteOffset + 8)), - byteData.getFloat32(byteOffset + 12), - ); - shapes.add(shape); - byteOffset += 16; - } - - repaint(); - } +import '../shapes/shapes.dart'; +class Editor extends ClassicApp with Manipulator, Shapes, Originator { Editor() { const positionRadius = 300.0; for (var i = 0; i < 7; i++) { diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart index 0598a1c..ce8d7ee 100644 --- a/patterns/memento/memento_editor/editor/manipulator.dart +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -1,7 +1,7 @@ import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; -import 'shapes.dart'; +import '../shapes/shapes.dart'; mixin Manipulator implements ClassicApp, Shapes { void paintSelectFrame(Canvas canvas) { diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart new file mode 100644 index 0000000..9ac5bbf --- /dev/null +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -0,0 +1,53 @@ +import 'dart:convert'; +import 'dart:typed_data'; +import 'dart:ui'; + +import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; +import '../shapes/shape.dart'; +import '../shapes/shapes.dart'; + +typedef Snapshot = String; + +mixin Originator implements Shapes, ClassicApp { + Snapshot backup() { + final byteSize = shapes.length * 16; + final data = ByteData(byteSize); + var byteOffset = 0; + + for (final shape in shapes) { + data + ..setFloat32(byteOffset, shape.x) + ..setFloat32(byteOffset + 4, shape.y) + ..setInt32(byteOffset + 8, shape.color.value) + ..setFloat32(byteOffset + 12, shape.size); + byteOffset += 16; + } + + return data.buffer + .asUint8List() + .map((e) => e.toRadixString(16).padLeft(3, '0')) + .join(); + } + + void restore(Snapshot snapshot) { + final unBase = Base64Decoder().convert(snapshot); + final byteData = ByteData.sublistView(unBase); + final shapeCount = byteData.lengthInBytes ~/ 16; + + shapes.clear(); + var byteOffset = 0; + + for (var i = 0; i < shapeCount; i++) { + final shape = Shape( + byteData.getFloat32(byteOffset), + byteData.getFloat32(byteOffset + 4), + Color(byteData.getInt32(byteOffset + 8)), + byteData.getFloat32(byteOffset + 12), + ); + shapes.add(shape); + byteOffset += 16; + } + + repaint(); + } +} diff --git a/patterns/memento/memento_editor/editor/shape.dart b/patterns/memento/memento_editor/shapes/shape.dart similarity index 100% rename from patterns/memento/memento_editor/editor/shape.dart rename to patterns/memento/memento_editor/shapes/shape.dart diff --git a/patterns/memento/memento_editor/editor/shapes.dart b/patterns/memento/memento_editor/shapes/shapes.dart similarity index 92% rename from patterns/memento/memento_editor/editor/shapes.dart rename to patterns/memento/memento_editor/shapes/shapes.dart index 51b0645..669f8ec 100644 --- a/patterns/memento/memento_editor/editor/shapes.dart +++ b/patterns/memento/memento_editor/shapes/shapes.dart @@ -1,5 +1,4 @@ import 'dart:ui'; - import 'shape.dart'; mixin Shapes { @@ -16,7 +15,7 @@ mixin Shapes { Shape? get selectedShape => _selectedShape; void select(double x, double y) { - _selectedShape = findCircle(x, y); + _selectedShape = findShape(x, y); if (_selectedShape != null) { _xStart = _selectedShape!.x - x; @@ -47,7 +46,7 @@ mixin Shapes { var _xStart = 0.0; var _yStart = 0.0; - Shape? findCircle(double x, double y) { + Shape? findShape(double x, double y) { for (final shape in shapes) { if (shape.isBounded(x, y)) { return shape; diff --git a/patterns/memento/memento_editor/widgets/colors_widget.dart b/patterns/memento/memento_editor/widgets/composite/colors_widget.dart similarity index 85% rename from patterns/memento/memento_editor/widgets/colors_widget.dart rename to patterns/memento/memento_editor/widgets/composite/colors_widget.dart index 8aae037..829fff6 100644 --- a/patterns/memento/memento_editor/widgets/colors_widget.dart +++ b/patterns/memento/memento_editor/widgets/composite/colors_widget.dart @@ -48,9 +48,12 @@ class ColorsWidget extends StatelessWidget { width: 5, height: 5, decoration: BoxDecoration( - color: Colors.white.withOpacity(0.8), - borderRadius: BorderRadius.all(Radius.circular(2)), - border: Border.all(color: Colors.black.withOpacity(0.2))), + color: Colors.white.withOpacity(0.8), + borderRadius: BorderRadius.all(Radius.circular(2)), + border: Border.all( + color: Colors.black.withOpacity(0.2), + ), + ), ), ); } diff --git a/patterns/memento/memento_editor/widgets/composite/named_panel.dart b/patterns/memento/memento_editor/widgets/composite/named_panel.dart new file mode 100644 index 0000000..4f7c810 --- /dev/null +++ b/patterns/memento/memento_editor/widgets/composite/named_panel.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +class NamedPanel extends StatelessWidget { + final String name; + final List children; + + NamedPanel({ + Key? key, + required this.name, + required this.children, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + name, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 20), + ...children, + ], + ); + } +} diff --git a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart new file mode 100644 index 0000000..cb92d76 --- /dev/null +++ b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; + +import '../../application.dart'; +import '../composite/colors_widget.dart'; +import '../composite/named_panel.dart'; + + +class ShapePropertiesWidget extends StatelessWidget { + final MementoEditorApplication app; + final List colors; + + const ShapePropertiesWidget({ + Key? key, + required this.app, + required this.colors, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return NamedPanel( + name: 'SHAPE PROPERTIES', + children: [ + Row( + children: [ + _buildNumberField('x:', app.editor.selectedShape?.x), + SizedBox(width: 20), + _buildNumberField('y:', app.editor.selectedShape?.y), + ], + ), + SizedBox(height: 20), + _buildNumberField( + 'size:', + app.editor.selectedShape?.size, + ), + SizedBox(height: 10), + Padding( + padding: EdgeInsets.symmetric(vertical: 14), + child: Row( + children: [ + Text( + 'color:', + style: TextStyle( + color: Colors.black.withOpacity( + app.editor.selectedShape == null ? 0.5 : 1.0, + ), + ), + ), + SizedBox(width: 10), + ColorsWidget( + currentColor: app.editor.selectedShape?.color, + colors: colors, + onColorSelect: (newColor) { + app.editor.selectedShape?.color = newColor; + app.editor.repaint(); + }, + ), + ], + ), + ), + ], + ); + } + + Widget _buildNumberField(String name, double? value) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + name, + style: TextStyle( + color: Colors.black.withOpacity(value == null ? 0.5 : 1.0), + ), + ), + SizedBox(width: 10), + SizedBox( + width: 60, + child: TextField( + enabled: value != null, + controller: TextEditingController( + text: value == null ? '' : value.toStringAsFixed(0), + ), + decoration: InputDecoration( + filled: value != null, + fillColor: Colors.white, + ), + ), + ), + ], + ); + } +} diff --git a/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart b/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart new file mode 100644 index 0000000..8066ab5 --- /dev/null +++ b/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; + +import '../../../../adapter/flutter_adapter/classic_app/repaint_event.dart'; +import '../../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; +import '../../application.dart'; +import '../composite/named_panel.dart'; + +class SnapshotWidget extends StatelessWidget { + final MementoEditorApplication app; + + const SnapshotWidget({Key? key, required this.app}) : super(key: key); + + @override + Widget build(BuildContext context) { + return NamedPanel( + name: 'MEMENTO', + children: [ + Text( + '1. Select the shape.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 5), + Text( + '2. Change color, size or position.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 5), + Text( + '3. Click the "save state" button.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 5), + Text( + 'Now you can restore states by selecting them from the list.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + OutlinedButton( + child: Text('Save state'), + onPressed: () { + app.snapshots.add(app.editor.backup()); + app.editor.repaint(); + }, + ), + ], + ), + SizedBox(height: 5), + Expanded( + child: ColoredBox( + color: Colors.white, + child: Material( + type: MaterialType.transparency, + child: SubscriberWidget( + observer: app.editor.events, + builder: (_, __) => ListView( + padding: EdgeInsets.all(5), + children: [ + ...app.snapshots.map( + (e) { + return Container( + margin: EdgeInsets.only(bottom: 4), + color: Colors.black.withOpacity(0.02), + child: ListTile( + leading: Container( + color: Colors.green, + width: 50, + height: double.infinity, + child: Icon(Icons.animation), + ), + title: Text('Snapshot'), + subtitle: SingleChildScrollView( + child: Text(e), + scrollDirection: Axis.horizontal, + ), + onTap: () {}, + ), + ); + }, + ), + ], + ), + ), + ), + ), + ), + ], + ); + } +} diff --git a/patterns/memento/memento_editor/widgets/right_panel_widget.dart b/patterns/memento/memento_editor/widgets/right_panel_widget.dart index 83dd8c3..197e8ad 100644 --- a/patterns/memento/memento_editor/widgets/right_panel_widget.dart +++ b/patterns/memento/memento_editor/widgets/right_panel_widget.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; -import '../../../adapter/flutter_adapter/classic_app/repaint_event.dart'; -import '../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; import '../application.dart'; -import 'colors_widget.dart'; +import 'panels/shape_properties_widget.dart'; +import 'panels/snapshot_widget.dart'; class RightPanelWidget extends StatelessWidget { final MementoEditorApplication app; @@ -19,195 +18,16 @@ class RightPanelWidget extends StatelessWidget { Color(0xFFFFFFFF), ]; - static const rowHeight = 60.0; - @override Widget build(BuildContext context) { - return SizedBox( - width: 300, + return Padding( + padding: EdgeInsets.all(20), child: Column( children: [ - Padding( - padding: EdgeInsets.only(left: 20, right: 20, top: 20, bottom: 10), - child: SubscriberWidget( - observer: app.editor.events, - builder: (buildContext, event) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'SHAPE PROPERTIES', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), - SizedBox(height: 20), - Row( - children: [ - _buildNumberField('x:', app.editor.selectedShape?.x), - SizedBox(width: 20), - _buildNumberField('y:', app.editor.selectedShape?.y), - ], - ), - SizedBox(height: 20), - _buildNumberField( - 'size:', - app.editor.selectedShape?.size, - ), - SizedBox(height: 10), - Padding( - padding: EdgeInsets.symmetric(vertical: 14), - child: Row( - children: [ - Text( - 'color:', - style: TextStyle( - color: Colors.black.withOpacity( - app.editor.selectedShape == null ? 0.5 : 1.0, - ), - ), - ), - SizedBox(width: 10), - ColorsWidget( - currentColor: app.editor.selectedShape?.color, - colors: colors, - onColorSelect: (newColor) { - app.editor.selectedShape?.color = newColor; - app.editor.repaint(); - }, - ), - ], - ), - ), - ], - ); - }), - ), - Container( - height: 2, - color: Colors.grey.withOpacity(0.5), - ), - Expanded( - child: Padding( - padding: - EdgeInsets.only(left: 20, right: 20, top: 15, bottom: 20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'MEMENTO', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), - SizedBox(height: 10), - Text( - '1. Select the shape.', - style: Theme.of(context).textTheme.bodyMedium, - ), - SizedBox(height: 5), - Text( - '2. Change color, size or position.', - style: Theme.of(context).textTheme.bodyMedium, - ), - SizedBox(height: 5), - Text( - '3. Click the "save state" button.', - style: Theme.of(context).textTheme.bodyMedium, - ), - SizedBox(height: 5), - Text( - 'Now you can restore states by selecting them from the list.', - style: Theme.of(context).textTheme.bodyMedium, - ), - SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - OutlinedButton( - child: Text('Save state'), - onPressed: () { - app.snapshots.add(app.editor.backup()); - app.editor.repaint(); - }, - ), - ], - ), - SizedBox(height: 5), - Expanded( - child: ColoredBox( - color: Colors.white, - child: Material( - type: MaterialType.transparency, - child: SubscriberWidget( - observer: app.editor.events, - builder: (_, __) => ListView( - padding: EdgeInsets.all(5), - children: [ - ...app.snapshots.map( - (e) { - return Container( - margin: EdgeInsets.only(bottom: 4), - color: Colors.black.withOpacity(0.02), - child: ListTile( - leading: Container( - color: Colors.green, - width: 50, - height: double.infinity, - child: Icon(Icons.animation), - ), - title: Text('Snapshot'), - subtitle: SingleChildScrollView( - child: Text(e), - scrollDirection: Axis.horizontal, - ), - onTap: () {}, - ), - ); - }, - ), - ], - ), - ), - ), - ), - ), - ], - ), - ), - ), + ShapePropertiesWidget(app: app, colors: colors), + SnapshotWidget(app: app), ], ), ); } - - Widget _buildNumberField(String name, double? value) { - return Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - name, - style: TextStyle( - color: Colors.black.withOpacity(value == null ? 0.5 : 1.0), - ), - ), - SizedBox(width: 10), - SizedBox( - width: 60, - child: TextField( - enabled: value != null, - controller: TextEditingController( - text: value == null ? '' : value.toStringAsFixed(0), - ), - decoration: InputDecoration( - filled: value != null, - fillColor: Colors.white, - ), - ), - ), - ], - ); - } } From 843bb1686da50b7fd3edda7bfc60247cf1d315fd Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 15 Feb 2022 22:42:40 +0200 Subject: [PATCH 246/479] Add SelectedShape class. --- .../memento_editor/editor/manipulator.dart | 22 ++++----- .../memento_editor/shapes/selected_shape.dart | 24 ++++++++++ .../memento/memento_editor/shapes/shapes.dart | 47 ++++++------------- .../panels/shape_properties_widget.dart | 12 ++--- 4 files changed, 56 insertions(+), 49 deletions(-) create mode 100644 patterns/memento/memento_editor/shapes/selected_shape.dart diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart index ce8d7ee..cf8ba47 100644 --- a/patterns/memento/memento_editor/editor/manipulator.dart +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -5,10 +5,10 @@ import '../shapes/shapes.dart'; mixin Manipulator implements ClassicApp, Shapes { void paintSelectFrame(Canvas canvas) { - if (selectedShape != null) { - final shapeSize = selectedShape!.size; - final x = (selectedShape!.x - shapeSize).roundToDouble() - 1.5; - final y = (selectedShape!.y - shapeSize).roundToDouble() - 1.5; + if (selected != null) { + final shapeSize = selected!.shape.size; + final x = (selected!.shape.x - shapeSize).roundToDouble() - 1.5; + final y = (selected!.shape.y - shapeSize).roundToDouble() - 1.5; canvas.drawRect( Rect.fromLTWH(x, y, shapeSize * 2 + 3, shapeSize * 2 + 3), Paint() @@ -23,15 +23,15 @@ mixin Manipulator implements ClassicApp, Shapes { @override void onMouseDown(double x, double y) { _isMouseDown = true; - final currSelection = selectedShape; + final currSelection = selected; select(x, y); - if (currSelection == selectedShape) { + if (currSelection == selected) { return; } - if (selectedShape == null) { + if (selected == null) { unSelect(); } @@ -40,16 +40,16 @@ mixin Manipulator implements ClassicApp, Shapes { @override void onMouseMove(double x, double y) { - if (_isMouseDown && selectedShape != null) { - drag(x, y); + if (_isMouseDown) { + selected?.dragTo(x, y); repaint(); } } @override void onPointerWheel(double deltaX, double deltaY) { - if (selectedShape != null) { - changeSize(deltaY / 5); + if (selected != null) { + selected!.changeSize(deltaY / 5); repaint(); } } diff --git a/patterns/memento/memento_editor/shapes/selected_shape.dart b/patterns/memento/memento_editor/shapes/selected_shape.dart new file mode 100644 index 0000000..52ba568 --- /dev/null +++ b/patterns/memento/memento_editor/shapes/selected_shape.dart @@ -0,0 +1,24 @@ +import 'shape.dart'; + +class SelectedShape { + final Shape shape; + + SelectedShape(this.shape, this._xStart, this._yStart); + + void changeSize(double delta) { + final currentSize = shape.size; + final newSize = currentSize - delta; + + if (newSize != shape.size) { + shape.size = newSize; + } + } + + final double _xStart; + final double _yStart; + + void dragTo(double x, double y) { + shape.x = x + _xStart; + shape.y = y + _yStart; + } +} diff --git a/patterns/memento/memento_editor/shapes/shapes.dart b/patterns/memento/memento_editor/shapes/shapes.dart index 669f8ec..fd93213 100644 --- a/patterns/memento/memento_editor/shapes/shapes.dart +++ b/patterns/memento/memento_editor/shapes/shapes.dart @@ -1,51 +1,28 @@ import 'dart:ui'; +import 'selected_shape.dart'; import 'shape.dart'; mixin Shapes { final shapes = []; - void paintShapes(Canvas canvas) { - for (final shape in shapes.reversed) { - shape.paint(canvas); - } - } + SelectedShape? _selected; - Shape? _selectedShape; - - Shape? get selectedShape => _selectedShape; + SelectedShape? get selected => _selected; void select(double x, double y) { - _selectedShape = findShape(x, y); + final shape = findShape(x, y); - if (_selectedShape != null) { - _xStart = _selectedShape!.x - x; - _yStart = _selectedShape!.y - y; + if (shape != null) { + _selected = SelectedShape(shape, shape.x - x, shape.y - y); + } else { + _selected = null; } } void unSelect() { - _selectedShape = null; - } - - void changeSize(double delta) { - final currentSize = _selectedShape?.size; - - if (currentSize != null) { - final newSize = currentSize - delta; - if (newSize != _selectedShape!.size) { - _selectedShape!.size = newSize; - } - } - } - - void drag(double x, double y) { - _selectedShape!.x = x + _xStart; - _selectedShape!.y = y + _yStart; + _selected = null; } - var _xStart = 0.0; - var _yStart = 0.0; - Shape? findShape(double x, double y) { for (final shape in shapes) { if (shape.isBounded(x, y)) { @@ -53,4 +30,10 @@ mixin Shapes { } } } + + void paintShapes(Canvas canvas) { + for (final shape in shapes.reversed) { + shape.paint(canvas); + } + } } diff --git a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart index cb92d76..813dbba 100644 --- a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart @@ -22,15 +22,15 @@ class ShapePropertiesWidget extends StatelessWidget { children: [ Row( children: [ - _buildNumberField('x:', app.editor.selectedShape?.x), + _buildNumberField('x:', app.editor.selected?.shape.x), SizedBox(width: 20), - _buildNumberField('y:', app.editor.selectedShape?.y), + _buildNumberField('y:', app.editor.selected?.shape.y), ], ), SizedBox(height: 20), _buildNumberField( 'size:', - app.editor.selectedShape?.size, + app.editor.selected?.shape.size, ), SizedBox(height: 10), Padding( @@ -41,16 +41,16 @@ class ShapePropertiesWidget extends StatelessWidget { 'color:', style: TextStyle( color: Colors.black.withOpacity( - app.editor.selectedShape == null ? 0.5 : 1.0, + app.editor.selected == null ? 0.5 : 1.0, ), ), ), SizedBox(width: 10), ColorsWidget( - currentColor: app.editor.selectedShape?.color, + currentColor: app.editor.selected?.shape.color, colors: colors, onColorSelect: (newColor) { - app.editor.selectedShape?.color = newColor; + app.editor.selected?.shape.color = newColor; app.editor.repaint(); }, ), From 04a4d26b325f312704cdf9e8f2dce1ca5b7a1c77 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 15 Feb 2022 22:48:25 +0200 Subject: [PATCH 247/479] Move "paintSelectFrame" from manipulator to "SelectedShape" class. --- .../memento/memento_editor/editor/editor.dart | 2 +- .../memento_editor/editor/manipulator.dart | 16 ---------------- .../memento_editor/shapes/selected_shape.dart | 13 +++++++++++++ 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index af306c6..ce9add8 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -21,7 +21,7 @@ class Editor extends ClassicApp with Manipulator, Shapes, Originator { void onPaint(Canvas canvas, Size canvasSize) { _paintBackground(canvas, canvasSize); paintShapes(canvas); - paintSelectFrame(canvas); + selected?.paintSelectFrame(canvas); } void _paintBackground(Canvas canvas, Size canvasSize) { diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart index cf8ba47..76fd0c0 100644 --- a/patterns/memento/memento_editor/editor/manipulator.dart +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -1,23 +1,7 @@ -import 'dart:ui'; - import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; import '../shapes/shapes.dart'; mixin Manipulator implements ClassicApp, Shapes { - void paintSelectFrame(Canvas canvas) { - if (selected != null) { - final shapeSize = selected!.shape.size; - final x = (selected!.shape.x - shapeSize).roundToDouble() - 1.5; - final y = (selected!.shape.y - shapeSize).roundToDouble() - 1.5; - canvas.drawRect( - Rect.fromLTWH(x, y, shapeSize * 2 + 3, shapeSize * 2 + 3), - Paint() - ..style = PaintingStyle.stroke - ..color = Color(0xff26e6ff), - ); - } - } - var _isMouseDown = false; @override diff --git a/patterns/memento/memento_editor/shapes/selected_shape.dart b/patterns/memento/memento_editor/shapes/selected_shape.dart index 52ba568..371c6b7 100644 --- a/patterns/memento/memento_editor/shapes/selected_shape.dart +++ b/patterns/memento/memento_editor/shapes/selected_shape.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'shape.dart'; class SelectedShape { @@ -21,4 +23,15 @@ class SelectedShape { shape.x = x + _xStart; shape.y = y + _yStart; } + + void paintSelectFrame(Canvas canvas) { + final x = (shape.x - shape.size).roundToDouble() - 1.5; + final y = (shape.y - shape.size).roundToDouble() - 1.5; + canvas.drawRect( + Rect.fromLTWH(x, y, shape.size * 2 + 3, shape.size * 2 + 3), + Paint() + ..style = PaintingStyle.stroke + ..color = Color(0xff26e6ff), + ); + } } From bf9d0e9fd4a341774165918255ce7496e23e658a Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 11:34:07 +0200 Subject: [PATCH 248/479] Add SnapshotWidget. --- .../composite/snapshot_list_widget.dart | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart diff --git a/patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart b/patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart new file mode 100644 index 0000000..dfaf3ee --- /dev/null +++ b/patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +class SnapshotListWidget extends StatelessWidget { + final List snapshots; + + const SnapshotListWidget({Key? key, required this.snapshots}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return ColoredBox( + color: Colors.white, + child: Material( + type: MaterialType.transparency, + child: ListView( + padding: EdgeInsets.all(5), + children: snapshots.map((e) => _buildItem('Snapshot', e)).toList(), + ), + ), + ); + } + + Widget _buildItem(String name, String hash) { + return Container( + margin: EdgeInsets.only(bottom: 4), + color: Colors.black.withOpacity(0.02), + child: ListTile( + leading: Container( + color: Colors.green, + width: 50, + height: double.infinity, + child: Icon(Icons.animation), + ), + title: Text(name), + subtitle: SingleChildScrollView( + child: Text(hash), + scrollDirection: Axis.horizontal, + ), + onTap: () {}, + ), + ); + } +} From 95f88e79fd1a5e07974bcf8790f9c6a56edb8dfd Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 11:34:36 +0200 Subject: [PATCH 249/479] Fix vertical space. --- .../memento/memento_editor/widgets/composite/named_panel.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/memento/memento_editor/widgets/composite/named_panel.dart b/patterns/memento/memento_editor/widgets/composite/named_panel.dart index 4f7c810..a6637d4 100644 --- a/patterns/memento/memento_editor/widgets/composite/named_panel.dart +++ b/patterns/memento/memento_editor/widgets/composite/named_panel.dart @@ -22,7 +22,7 @@ class NamedPanel extends StatelessWidget { fontWeight: FontWeight.bold, ), ), - SizedBox(height: 20), + SizedBox(height: 15), ...children, ], ); From cb0c152763724f71175d33f5a15873b9d251fb91 Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 11:35:30 +0200 Subject: [PATCH 250/479] Fix right panel width & add space line. --- .../memento_editor/widgets/right_panel_widget.dart | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/patterns/memento/memento_editor/widgets/right_panel_widget.dart b/patterns/memento/memento_editor/widgets/right_panel_widget.dart index 197e8ad..689335d 100644 --- a/patterns/memento/memento_editor/widgets/right_panel_widget.dart +++ b/patterns/memento/memento_editor/widgets/right_panel_widget.dart @@ -20,12 +20,20 @@ class RightPanelWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( + return Container( padding: EdgeInsets.all(20), + width: 300, child: Column( children: [ ShapePropertiesWidget(app: app, colors: colors), - SnapshotWidget(app: app), + Container( + margin: EdgeInsets.symmetric(vertical: 20), + height: 2, + color: Colors.black.withOpacity(.2), + ), + Expanded( + child: SnapshotWidget(app: app), + ), ], ), ); From 086add1b09361d5d0d91f6e26c83b20fcf75e5ac Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 11:36:32 +0200 Subject: [PATCH 251/479] Remove SubscriberWidget from ShapeProperties. --- .../panels/shape_properties_widget.dart | 81 ++++++++++--------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart index 813dbba..bfbc90a 100644 --- a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart @@ -1,10 +1,11 @@ import 'package:flutter/material.dart'; +import '../../../../adapter/flutter_adapter/classic_app/repaint_event.dart'; +import '../../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; import '../../application.dart'; import '../composite/colors_widget.dart'; import '../composite/named_panel.dart'; - class ShapePropertiesWidget extends StatelessWidget { final MementoEditorApplication app; final List colors; @@ -17,47 +18,49 @@ class ShapePropertiesWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return NamedPanel( - name: 'SHAPE PROPERTIES', - children: [ - Row( + return SubscriberWidget( + observer: app.editor.events, + builder: (buildContext, event) { + return NamedPanel( + name: 'SHAPE PROPERTIES', children: [ - _buildNumberField('x:', app.editor.selected?.shape.x), - SizedBox(width: 20), - _buildNumberField('y:', app.editor.selected?.shape.y), - ], - ), - SizedBox(height: 20), - _buildNumberField( - 'size:', - app.editor.selected?.shape.size, - ), - SizedBox(height: 10), - Padding( - padding: EdgeInsets.symmetric(vertical: 14), - child: Row( - children: [ - Text( - 'color:', - style: TextStyle( - color: Colors.black.withOpacity( - app.editor.selected == null ? 0.5 : 1.0, + Row( + children: [ + _buildNumberField('x:', app.editor.selected?.shape.x), + SizedBox(width: 20), + _buildNumberField('y:', app.editor.selected?.shape.y), + ], + ), + SizedBox(height: 20), + _buildNumberField( + 'size:', + app.editor.selected?.shape.size, + ), + SizedBox(height: 20), + Row( + children: [ + Text( + 'color:', + style: TextStyle( + color: Colors.black.withOpacity( + app.editor.selected == null ? 0.5 : 1.0, + ), ), ), - ), - SizedBox(width: 10), - ColorsWidget( - currentColor: app.editor.selected?.shape.color, - colors: colors, - onColorSelect: (newColor) { - app.editor.selected?.shape.color = newColor; - app.editor.repaint(); - }, - ), - ], - ), - ), - ], + SizedBox(width: 10), + ColorsWidget( + currentColor: app.editor.selected?.shape.color, + colors: colors, + onColorSelect: (newColor) { + app.editor.selected?.shape.color = newColor; + app.editor.repaint(); + }, + ), + ], + ), + ], + ); + }, ); } From dcb55595bfea6146f75368d13f9e0f0c920f164f Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 11:37:34 +0200 Subject: [PATCH 252/479] Add separate method: "_buildSaveStateButton", "_buildDescription" to "SnapshotWidget". --- .../widgets/panels/snapshot_widget.dart | 112 +++++++----------- 1 file changed, 44 insertions(+), 68 deletions(-) diff --git a/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart b/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart index 8066ab5..c0b23a1 100644 --- a/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; -import '../../../../adapter/flutter_adapter/classic_app/repaint_event.dart'; -import '../../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; import '../../application.dart'; import '../composite/named_panel.dart'; +import '../composite/snapshot_list_widget.dart'; class SnapshotWidget extends StatelessWidget { final MementoEditorApplication app; @@ -15,78 +14,55 @@ class SnapshotWidget extends StatelessWidget { return NamedPanel( name: 'MEMENTO', children: [ - Text( - '1. Select the shape.', - style: Theme.of(context).textTheme.bodyMedium, - ), - SizedBox(height: 5), - Text( - '2. Change color, size or position.', - style: Theme.of(context).textTheme.bodyMedium, - ), - SizedBox(height: 5), - Text( - '3. Click the "save state" button.', - style: Theme.of(context).textTheme.bodyMedium, - ), - SizedBox(height: 5), - Text( - 'Now you can restore states by selecting them from the list.', - style: Theme.of(context).textTheme.bodyMedium, - ), + ..._buildDescription(Theme.of(context).textTheme.bodyMedium!), SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - OutlinedButton( - child: Text('Save state'), - onPressed: () { - app.snapshots.add(app.editor.backup()); - app.editor.repaint(); - }, - ), - ], - ), + _buildSaveStateButton(), SizedBox(height: 5), Expanded( - child: ColoredBox( - color: Colors.white, - child: Material( - type: MaterialType.transparency, - child: SubscriberWidget( - observer: app.editor.events, - builder: (_, __) => ListView( - padding: EdgeInsets.all(5), - children: [ - ...app.snapshots.map( - (e) { - return Container( - margin: EdgeInsets.only(bottom: 4), - color: Colors.black.withOpacity(0.02), - child: ListTile( - leading: Container( - color: Colors.green, - width: 50, - height: double.infinity, - child: Icon(Icons.animation), - ), - title: Text('Snapshot'), - subtitle: SingleChildScrollView( - child: Text(e), - scrollDirection: Axis.horizontal, - ), - onTap: () {}, - ), - ); - }, - ), - ], - ), - ), - ), + child: SnapshotListWidget( + snapshots: app.snapshots, ), ), ], ); } + + List _buildDescription(TextStyle style) { + return [ + Text( + '1. Select the shape.', + style: style, + ), + SizedBox(height: 5), + Text( + '2. Change color, size or position.', + style: style, + ), + SizedBox(height: 5), + Text( + '3. Click the "save state" button.', + style: style, + ), + SizedBox(height: 5), + Text( + 'Now you can restore states by selecting them from the list.', + style: style, + ), + ]; + } + + Widget _buildSaveStateButton() { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + OutlinedButton( + child: Text('Save state'), + onPressed: () { + app.snapshots.add(app.editor.backup()); + app.editor.repaint(); + }, + ), + ], + ); + } } From f6f022343555732711327d3154837eafe3bec5bf Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 11:40:42 +0200 Subject: [PATCH 253/479] Add separate class Snapshot. --- patterns/memento/memento_editor/application.dart | 2 +- patterns/memento/memento_editor/memento_pattern/caretaker.dart | 3 +++ patterns/memento/memento_editor/memento_pattern/memento.dart | 3 +++ .../memento/memento_editor/memento_pattern/originator.dart | 3 +-- patterns/memento/memento_editor/memento_pattern/snapshot.dart | 1 + 5 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 patterns/memento/memento_editor/memento_pattern/caretaker.dart create mode 100644 patterns/memento/memento_editor/memento_pattern/memento.dart create mode 100644 patterns/memento/memento_editor/memento_pattern/snapshot.dart diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index 5420f65..6adf10d 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -1,5 +1,5 @@ import 'editor/editor.dart'; -import 'memento_pattern/originator.dart'; +import 'memento_pattern/snapshot.dart'; class MementoEditorApplication { final editor = Editor(); diff --git a/patterns/memento/memento_editor/memento_pattern/caretaker.dart b/patterns/memento/memento_editor/memento_pattern/caretaker.dart new file mode 100644 index 0000000..d85f19a --- /dev/null +++ b/patterns/memento/memento_editor/memento_pattern/caretaker.dart @@ -0,0 +1,3 @@ +class Caretaker { + //final snapshot +} diff --git a/patterns/memento/memento_editor/memento_pattern/memento.dart b/patterns/memento/memento_editor/memento_pattern/memento.dart new file mode 100644 index 0000000..9c9c41d --- /dev/null +++ b/patterns/memento/memento_editor/memento_pattern/memento.dart @@ -0,0 +1,3 @@ +class Memento { + +} diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index 9ac5bbf..77b0403 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -5,8 +5,7 @@ import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; import '../shapes/shape.dart'; import '../shapes/shapes.dart'; - -typedef Snapshot = String; +import 'snapshot.dart'; mixin Originator implements Shapes, ClassicApp { Snapshot backup() { diff --git a/patterns/memento/memento_editor/memento_pattern/snapshot.dart b/patterns/memento/memento_editor/memento_pattern/snapshot.dart new file mode 100644 index 0000000..e71d8a3 --- /dev/null +++ b/patterns/memento/memento_editor/memento_pattern/snapshot.dart @@ -0,0 +1 @@ +typedef Snapshot = String; From 46283bbb4bc5aaf923ca3ac68836d6c5a0985aae Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 13:22:23 +0200 Subject: [PATCH 254/479] Remove initialization to MementoEditorApplication. --- patterns/memento/memento_editor/application.dart | 12 ++++++++++++ patterns/memento/memento_editor/editor/editor.dart | 13 +------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index 6adf10d..2cc8a17 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -1,7 +1,19 @@ +import 'dart:math'; + import 'editor/editor.dart'; import 'memento_pattern/snapshot.dart'; +import 'shapes/shape.dart'; class MementoEditorApplication { final editor = Editor(); final snapshots = []; + + MementoEditorApplication() { + const positionRadius = 300.0; + for (var i = 0; i < 7; i++) { + final x = 60 + positionRadius + cos(i / 1.15) * positionRadius; + final y = 60 + positionRadius + sin(i / 1.15) * positionRadius; + editor.shapes.add(Shape(x, y)); + } + } } diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index ce9add8..5ddc2a6 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -1,22 +1,11 @@ -import 'dart:math'; import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; import '../memento_pattern/originator.dart'; -import '../shapes/shape.dart'; -import 'manipulator.dart'; import '../shapes/shapes.dart'; +import 'manipulator.dart'; class Editor extends ClassicApp with Manipulator, Shapes, Originator { - Editor() { - const positionRadius = 300.0; - for (var i = 0; i < 7; i++) { - final x = 60 + positionRadius + cos(i / 1.15) * positionRadius; - final y = 60 + positionRadius + sin(i / 1.15) * positionRadius; - shapes.add(Shape(x, y)); - } - } - @override void onPaint(Canvas canvas, Size canvasSize) { _paintBackground(canvas, canvasSize); From c26c817f113882b59b6616de00c96d34ea84b1d4 Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 13:30:26 +0200 Subject: [PATCH 255/479] Rename SnapshotWidget to MementoWidget. --- .../panels/{snapshot_widget.dart => memento_widget.dart} | 4 ++-- .../memento/memento_editor/widgets/right_panel_widget.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename patterns/memento/memento_editor/widgets/panels/{snapshot_widget.dart => memento_widget.dart} (92%) diff --git a/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart b/patterns/memento/memento_editor/widgets/panels/memento_widget.dart similarity index 92% rename from patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart rename to patterns/memento/memento_editor/widgets/panels/memento_widget.dart index c0b23a1..7789e59 100644 --- a/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/memento_widget.dart @@ -4,10 +4,10 @@ import '../../application.dart'; import '../composite/named_panel.dart'; import '../composite/snapshot_list_widget.dart'; -class SnapshotWidget extends StatelessWidget { +class MementoWidget extends StatelessWidget { final MementoEditorApplication app; - const SnapshotWidget({Key? key, required this.app}) : super(key: key); + const MementoWidget({Key? key, required this.app}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/patterns/memento/memento_editor/widgets/right_panel_widget.dart b/patterns/memento/memento_editor/widgets/right_panel_widget.dart index 689335d..6c29bb9 100644 --- a/patterns/memento/memento_editor/widgets/right_panel_widget.dart +++ b/patterns/memento/memento_editor/widgets/right_panel_widget.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import '../application.dart'; import 'panels/shape_properties_widget.dart'; -import 'panels/snapshot_widget.dart'; +import 'panels/memento_widget.dart'; class RightPanelWidget extends StatelessWidget { final MementoEditorApplication app; @@ -32,7 +32,7 @@ class RightPanelWidget extends StatelessWidget { color: Colors.black.withOpacity(.2), ), Expanded( - child: SnapshotWidget(app: app), + child: MementoWidget(app: app), ), ], ), From 57802be642c170485bc59be41be2bbea262030a7 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 26 Apr 2022 18:37:42 +0300 Subject: [PATCH 256/479] Add "Caretaker". --- .../memento/memento_editor/application.dart | 35 ++++++++++++++++--- .../editor/create_memento_event.dart | 3 ++ .../memento/memento_editor/editor/editor.dart | 2 +- .../memento_pattern/caretaker.dart | 17 ++++++++- .../memento_pattern/memento.dart | 6 ++++ .../memento_pattern/originator.dart | 7 +--- .../memento_editor/shapes/selected_shape.dart | 2 +- .../memento/memento_editor/shapes/shapes.dart | 2 ++ .../composite/snapshot_list_widget.dart | 28 +++++++++------ .../widgets/panels/memento_widget.dart | 17 +++++---- pubspec.yaml | 6 ++-- 11 files changed, 93 insertions(+), 32 deletions(-) create mode 100644 patterns/memento/memento_editor/editor/create_memento_event.dart diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index 2cc8a17..af31cdb 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -1,19 +1,44 @@ import 'dart:math'; +import 'editor/create_memento_event.dart'; import 'editor/editor.dart'; -import 'memento_pattern/snapshot.dart'; +import 'memento_pattern/caretaker.dart'; +import 'memento_pattern/memento.dart'; import 'shapes/shape.dart'; class MementoEditorApplication { final editor = Editor(); - final snapshots = []; + final caretaker = Caretaker(); MementoEditorApplication() { - const positionRadius = 300.0; + createDefaultShapes(); + } + + void createDefaultShapes() { + const radius = 300.0; for (var i = 0; i < 7; i++) { - final x = 60 + positionRadius + cos(i / 1.15) * positionRadius; - final y = 60 + positionRadius + sin(i / 1.15) * positionRadius; + final x = 60 + radius + cos(i / 1.15) * radius; + final y = 60 + radius + sin(i / 1.15) * radius; editor.shapes.add(Shape(x, y)); } } + + void saveState() { + final snapshot = editor.backup(); + + if (caretaker.isSnapshotExists(snapshot)) { + return; + } + + final memento = Memento(DateTime.now(), snapshot); + caretaker.addMemento(memento); + editor.events.notify(CreateMementoEvent()); + } + + void restoreState(Memento memento) { + editor + ..unSelect() + ..restore(memento.snapshot) + ..repaint(); + } } diff --git a/patterns/memento/memento_editor/editor/create_memento_event.dart b/patterns/memento/memento_editor/editor/create_memento_event.dart new file mode 100644 index 0000000..51acd4c --- /dev/null +++ b/patterns/memento/memento_editor/editor/create_memento_event.dart @@ -0,0 +1,3 @@ +import '../../../observer/app_observer/observer/event.dart'; + +class CreateMementoEvent extends Event {} diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index 5ddc2a6..24480a9 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -10,7 +10,7 @@ class Editor extends ClassicApp with Manipulator, Shapes, Originator { void onPaint(Canvas canvas, Size canvasSize) { _paintBackground(canvas, canvasSize); paintShapes(canvas); - selected?.paintSelectFrame(canvas); + selected?.paintSelectionBox(canvas); } void _paintBackground(Canvas canvas, Size canvasSize) { diff --git a/patterns/memento/memento_editor/memento_pattern/caretaker.dart b/patterns/memento/memento_editor/memento_pattern/caretaker.dart index d85f19a..971e3f5 100644 --- a/patterns/memento/memento_editor/memento_pattern/caretaker.dart +++ b/patterns/memento/memento_editor/memento_pattern/caretaker.dart @@ -1,3 +1,18 @@ +import 'memento.dart'; +import 'snapshot.dart'; + class Caretaker { - //final snapshot + final _mementoList = []; + + List get list => List.unmodifiable(_mementoList); + + void addMemento(Memento memento) { + _mementoList.add(memento); + } + + bool isSnapshotExists(Snapshot snapshot) { + return list.any( + (e) => e.snapshot == snapshot, + ); + } } diff --git a/patterns/memento/memento_editor/memento_pattern/memento.dart b/patterns/memento/memento_editor/memento_pattern/memento.dart index 9c9c41d..2bfb0b7 100644 --- a/patterns/memento/memento_editor/memento_pattern/memento.dart +++ b/patterns/memento/memento_editor/memento_pattern/memento.dart @@ -1,3 +1,9 @@ + +import 'snapshot.dart'; + class Memento { + final DateTime time; + final Snapshot snapshot; + Memento(this.time, this.snapshot); } diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index 77b0403..47fab15 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -22,10 +22,7 @@ mixin Originator implements Shapes, ClassicApp { byteOffset += 16; } - return data.buffer - .asUint8List() - .map((e) => e.toRadixString(16).padLeft(3, '0')) - .join(); + return Base64Encoder().convert(data.buffer.asUint8List()); } void restore(Snapshot snapshot) { @@ -46,7 +43,5 @@ mixin Originator implements Shapes, ClassicApp { shapes.add(shape); byteOffset += 16; } - - repaint(); } } diff --git a/patterns/memento/memento_editor/shapes/selected_shape.dart b/patterns/memento/memento_editor/shapes/selected_shape.dart index 371c6b7..6797977 100644 --- a/patterns/memento/memento_editor/shapes/selected_shape.dart +++ b/patterns/memento/memento_editor/shapes/selected_shape.dart @@ -24,7 +24,7 @@ class SelectedShape { shape.y = y + _yStart; } - void paintSelectFrame(Canvas canvas) { + void paintSelectionBox(Canvas canvas) { final x = (shape.x - shape.size).roundToDouble() - 1.5; final y = (shape.y - shape.size).roundToDouble() - 1.5; canvas.drawRect( diff --git a/patterns/memento/memento_editor/shapes/shapes.dart b/patterns/memento/memento_editor/shapes/shapes.dart index fd93213..730190e 100644 --- a/patterns/memento/memento_editor/shapes/shapes.dart +++ b/patterns/memento/memento_editor/shapes/shapes.dart @@ -29,6 +29,8 @@ mixin Shapes { return shape; } } + + return null; } void paintShapes(Canvas canvas) { diff --git a/patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart b/patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart index dfaf3ee..33968f2 100644 --- a/patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart +++ b/patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart @@ -1,10 +1,16 @@ import 'package:flutter/material.dart'; +import '../../memento_pattern/memento.dart'; + class SnapshotListWidget extends StatelessWidget { - final List snapshots; + final List mementoList; + final void Function(Memento) onMementoRestore; - const SnapshotListWidget({Key? key, required this.snapshots}) - : super(key: key); + const SnapshotListWidget({ + Key? key, + required this.mementoList, + required this.onMementoRestore, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -14,29 +20,31 @@ class SnapshotListWidget extends StatelessWidget { type: MaterialType.transparency, child: ListView( padding: EdgeInsets.all(5), - children: snapshots.map((e) => _buildItem('Snapshot', e)).toList(), + children: mementoList.map((e) => _buildItem('Snapshot', e)).toList(), ), ), ); } - Widget _buildItem(String name, String hash) { + Widget _buildItem(String name, Memento memento) { return Container( margin: EdgeInsets.only(bottom: 4), color: Colors.black.withOpacity(0.02), child: ListTile( leading: Container( - color: Colors.green, + color: Colors.grey.shade200, width: 50, height: double.infinity, - child: Icon(Icons.animation), + child: Icon(Icons.backup), ), - title: Text(name), + title: Text(name ), subtitle: SingleChildScrollView( - child: Text(hash), + child: Text(memento.time.toIso8601String()), scrollDirection: Axis.horizontal, ), - onTap: () {}, + onTap: () { + onMementoRestore(memento); + }, ), ); } diff --git a/patterns/memento/memento_editor/widgets/panels/memento_widget.dart b/patterns/memento/memento_editor/widgets/panels/memento_widget.dart index 7789e59..13c5c53 100644 --- a/patterns/memento/memento_editor/widgets/panels/memento_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/memento_widget.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; +import '../../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; import '../../application.dart'; +import '../../editor/create_memento_event.dart'; import '../composite/named_panel.dart'; import '../composite/snapshot_list_widget.dart'; @@ -19,8 +21,14 @@ class MementoWidget extends StatelessWidget { _buildSaveStateButton(), SizedBox(height: 5), Expanded( - child: SnapshotListWidget( - snapshots: app.snapshots, + child: SubscriberWidget( + observer: app.editor.events, + builder: (buildContext, event) { + return SnapshotListWidget( + mementoList: app.caretaker.list, + onMementoRestore: app.restoreState, + ); + }, ), ), ], @@ -57,10 +65,7 @@ class MementoWidget extends StatelessWidget { children: [ OutlinedButton( child: Text('Save state'), - onPressed: () { - app.snapshots.add(app.editor.backup()); - app.editor.repaint(); - }, + onPressed: app.saveState, ), ], ); diff --git a/pubspec.yaml b/pubspec.yaml index 9499ccb..b2c58fc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,8 +12,10 @@ dependencies: collection: ^1.15.0 flutter: sdk: flutter - + cupertino_icons: ^1.0.2 + dev_dependencies: lints: ^1.0.0 - +flutter: + uses-material-design: true From a56469f30069696c71ee13650548cb52fa26eb13 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 26 Apr 2022 19:48:20 +0300 Subject: [PATCH 257/479] add: save/restore selected shape. --- patterns/memento/memento_editor/application.dart | 1 - .../memento_pattern/originator.dart | 15 +++++++++++++-- .../memento/memento_editor/shapes/shapes.dart | 10 ++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index af31cdb..8c8dd8f 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -37,7 +37,6 @@ class MementoEditorApplication { void restoreState(Memento memento) { editor - ..unSelect() ..restore(memento.snapshot) ..repaint(); } diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index 47fab15..2c3092f 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -9,7 +9,7 @@ import 'snapshot.dart'; mixin Originator implements Shapes, ClassicApp { Snapshot backup() { - final byteSize = shapes.length * 16; + final byteSize = shapes.length * 16 + 4; final data = ByteData(byteSize); var byteOffset = 0; @@ -22,13 +22,17 @@ mixin Originator implements Shapes, ClassicApp { byteOffset += 16; } + // save selected shape + final index = (selected != null) ? shapes.indexOf(selected!.shape) : -1; + data.setInt32(byteOffset, index); + return Base64Encoder().convert(data.buffer.asUint8List()); } void restore(Snapshot snapshot) { final unBase = Base64Decoder().convert(snapshot); final byteData = ByteData.sublistView(unBase); - final shapeCount = byteData.lengthInBytes ~/ 16; + final shapeCount = (byteData.lengthInBytes - 4) ~/ 16; shapes.clear(); var byteOffset = 0; @@ -43,5 +47,12 @@ mixin Originator implements Shapes, ClassicApp { shapes.add(shape); byteOffset += 16; } + + // load selection shape index + final selectedIndex = byteData.getInt32(byteOffset); + + if (selectedIndex != -1) { + selectByIndex(selectedIndex); + } } } diff --git a/patterns/memento/memento_editor/shapes/shapes.dart b/patterns/memento/memento_editor/shapes/shapes.dart index 730190e..72029eb 100644 --- a/patterns/memento/memento_editor/shapes/shapes.dart +++ b/patterns/memento/memento_editor/shapes/shapes.dart @@ -19,6 +19,16 @@ mixin Shapes { } } + void selectByIndex(int index) { + if (index == -1) { + return; + } + + if (index <= shapes.length - 1) { + _selected = SelectedShape(shapes[index], 0, 0); + } + } + void unSelect() { _selected = null; } From 22359dba7152ee11c1e30d9ea67d878c2c4bdbb7 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 26 Apr 2022 21:56:24 +0300 Subject: [PATCH 258/479] Refactoring originator. --- .../memento/memento_editor/application.dart | 1 + .../memento_pattern/originator.dart | 77 +++++++++++++++---- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index 8c8dd8f..af31cdb 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -37,6 +37,7 @@ class MementoEditorApplication { void restoreState(Memento memento) { editor + ..unSelect() ..restore(memento.snapshot) ..repaint(); } diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index 2c3092f..2b92c75 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -9,8 +9,49 @@ import 'snapshot.dart'; mixin Originator implements Shapes, ClassicApp { Snapshot backup() { - final byteSize = shapes.length * 16 + 4; - final data = ByteData(byteSize); + final data = _allocateBuffer(); + _writeShapes(data); + _writeSelectedIndex(data); + return _toSnapshot(data); + } + + void restore(Snapshot snapshot) { + final byteData = _fromSnapshotToByteData(snapshot); + final newShapes = _readShapes(byteData); + final selectedIndex = _readSelectedIndex(byteData); + shapes.clear(); + shapes.addAll(newShapes); + selectByIndex(selectedIndex); + } + + static const _shapeByteSize = 16; + static const _selectedIndexByteSize = 4; + + ByteData _allocateBuffer() { + final byteSize = shapes.length * _shapeByteSize + _selectedIndexByteSize; + return ByteData(byteSize); + } + + ByteData _fromSnapshotToByteData(Snapshot snapshot) { + final unBase = Base64Decoder().convert(snapshot); + final byteData = ByteData.sublistView(unBase); + return byteData; + } + + void _writeSelectedIndex(ByteData data) { + late final int selectedIndex; + + if (selected == null) { + selectedIndex = -1; + } else { + selectedIndex = shapes.indexOf(selected!.shape); + } + + final byteOffset = data.lengthInBytes - _selectedIndexByteSize; + data.setInt32(byteOffset, selectedIndex); + } + + int _writeShapes(ByteData data) { var byteOffset = 0; for (final shape in shapes) { @@ -22,20 +63,17 @@ mixin Originator implements Shapes, ClassicApp { byteOffset += 16; } - // save selected shape - final index = (selected != null) ? shapes.indexOf(selected!.shape) : -1; - data.setInt32(byteOffset, index); - - return Base64Encoder().convert(data.buffer.asUint8List()); + return byteOffset; } - void restore(Snapshot snapshot) { - final unBase = Base64Decoder().convert(snapshot); - final byteData = ByteData.sublistView(unBase); - final shapeCount = (byteData.lengthInBytes - 4) ~/ 16; + int _getNumberOfShapes(ByteData byteData) { + return (byteData.lengthInBytes - _selectedIndexByteSize) ~/ _shapeByteSize; + } - shapes.clear(); + List _readShapes(ByteData byteData) { + final shapeCount = _getNumberOfShapes(byteData); var byteOffset = 0; + final shapes = []; for (var i = 0; i < shapeCount; i++) { final shape = Shape( @@ -48,11 +86,16 @@ mixin Originator implements Shapes, ClassicApp { byteOffset += 16; } - // load selection shape index - final selectedIndex = byteData.getInt32(byteOffset); + return shapes; + } - if (selectedIndex != -1) { - selectByIndex(selectedIndex); - } + int _readSelectedIndex(ByteData byteData) { + return byteData.getInt32(byteData.lengthInBytes - _selectedIndexByteSize); + } + + Snapshot _toSnapshot(ByteData data) { + return Base64Encoder().convert( + data.buffer.asUint8List(), + ); } } From 1552c21b6df97d4ed64e49052c3c29eeb719d0d2 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 26 Apr 2022 21:58:18 +0300 Subject: [PATCH 259/479] Rename variable selected to selectedShape. --- patterns/memento/memento_editor/editor/editor.dart | 2 +- .../memento/memento_editor/editor/manipulator.dart | 12 ++++++------ .../memento_editor/memento_pattern/originator.dart | 4 ++-- patterns/memento/memento_editor/shapes/shapes.dart | 12 ++++++------ .../widgets/panels/shape_properties_widget.dart | 12 ++++++------ 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index 24480a9..e4ea879 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -10,7 +10,7 @@ class Editor extends ClassicApp with Manipulator, Shapes, Originator { void onPaint(Canvas canvas, Size canvasSize) { _paintBackground(canvas, canvasSize); paintShapes(canvas); - selected?.paintSelectionBox(canvas); + selectedShape?.paintSelectionBox(canvas); } void _paintBackground(Canvas canvas, Size canvasSize) { diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart index 76fd0c0..56d4c5e 100644 --- a/patterns/memento/memento_editor/editor/manipulator.dart +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -7,15 +7,15 @@ mixin Manipulator implements ClassicApp, Shapes { @override void onMouseDown(double x, double y) { _isMouseDown = true; - final currSelection = selected; + final currSelection = selectedShape; select(x, y); - if (currSelection == selected) { + if (currSelection == selectedShape) { return; } - if (selected == null) { + if (selectedShape == null) { unSelect(); } @@ -25,15 +25,15 @@ mixin Manipulator implements ClassicApp, Shapes { @override void onMouseMove(double x, double y) { if (_isMouseDown) { - selected?.dragTo(x, y); + selectedShape?.dragTo(x, y); repaint(); } } @override void onPointerWheel(double deltaX, double deltaY) { - if (selected != null) { - selected!.changeSize(deltaY / 5); + if (selectedShape != null) { + selectedShape!.changeSize(deltaY / 5); repaint(); } } diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index 2b92c75..47d4a58 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -41,10 +41,10 @@ mixin Originator implements Shapes, ClassicApp { void _writeSelectedIndex(ByteData data) { late final int selectedIndex; - if (selected == null) { + if (selectedShape == null) { selectedIndex = -1; } else { - selectedIndex = shapes.indexOf(selected!.shape); + selectedIndex = shapes.indexOf(selectedShape!.shape); } final byteOffset = data.lengthInBytes - _selectedIndexByteSize; diff --git a/patterns/memento/memento_editor/shapes/shapes.dart b/patterns/memento/memento_editor/shapes/shapes.dart index 72029eb..c360503 100644 --- a/patterns/memento/memento_editor/shapes/shapes.dart +++ b/patterns/memento/memento_editor/shapes/shapes.dart @@ -5,17 +5,17 @@ import 'shape.dart'; mixin Shapes { final shapes = []; - SelectedShape? _selected; + SelectedShape? _selectedShape; - SelectedShape? get selected => _selected; + SelectedShape? get selectedShape => _selectedShape; void select(double x, double y) { final shape = findShape(x, y); if (shape != null) { - _selected = SelectedShape(shape, shape.x - x, shape.y - y); + _selectedShape = SelectedShape(shape, shape.x - x, shape.y - y); } else { - _selected = null; + _selectedShape = null; } } @@ -25,12 +25,12 @@ mixin Shapes { } if (index <= shapes.length - 1) { - _selected = SelectedShape(shapes[index], 0, 0); + _selectedShape = SelectedShape(shapes[index], 0, 0); } } void unSelect() { - _selected = null; + _selectedShape = null; } Shape? findShape(double x, double y) { diff --git a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart index bfbc90a..70d5cfd 100644 --- a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart @@ -26,15 +26,15 @@ class ShapePropertiesWidget extends StatelessWidget { children: [ Row( children: [ - _buildNumberField('x:', app.editor.selected?.shape.x), + _buildNumberField('x:', app.editor.selectedShape?.shape.x), SizedBox(width: 20), - _buildNumberField('y:', app.editor.selected?.shape.y), + _buildNumberField('y:', app.editor.selectedShape?.shape.y), ], ), SizedBox(height: 20), _buildNumberField( 'size:', - app.editor.selected?.shape.size, + app.editor.selectedShape?.shape.size, ), SizedBox(height: 20), Row( @@ -43,16 +43,16 @@ class ShapePropertiesWidget extends StatelessWidget { 'color:', style: TextStyle( color: Colors.black.withOpacity( - app.editor.selected == null ? 0.5 : 1.0, + app.editor.selectedShape == null ? 0.5 : 1.0, ), ), ), SizedBox(width: 10), ColorsWidget( - currentColor: app.editor.selected?.shape.color, + currentColor: app.editor.selectedShape?.shape.color, colors: colors, onColorSelect: (newColor) { - app.editor.selected?.shape.color = newColor; + app.editor.selectedShape?.shape.color = newColor; app.editor.repaint(); }, ), From 89bee4ede0b0972b2d5b39d5dc1bebf6f00dfa4e Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 26 Apr 2022 22:43:04 +0300 Subject: [PATCH 260/479] Make Shape setter fields is private. --- .../memento_editor/editor/manipulator.dart | 5 +-- .../memento_pattern/originator.dart | 2 +- .../memento_editor/shapes/selected_shape.dart | 37 +++++++++++++---- .../memento/memento_editor/shapes/shape.dart | 41 +++++++++++-------- .../memento/memento_editor/shapes/shapes.dart | 9 ++-- .../panels/shape_properties_widget.dart | 4 +- 6 files changed, 60 insertions(+), 38 deletions(-) diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart index 56d4c5e..90e9b14 100644 --- a/patterns/memento/memento_editor/editor/manipulator.dart +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -32,10 +32,7 @@ mixin Manipulator implements ClassicApp, Shapes { @override void onPointerWheel(double deltaX, double deltaY) { - if (selectedShape != null) { - selectedShape!.changeSize(deltaY / 5); - repaint(); - } + selectedShape?.changeSize(deltaY / 5); } @override diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index 47d4a58..078d2df 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -3,8 +3,8 @@ import 'dart:typed_data'; import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; -import '../shapes/shape.dart'; import '../shapes/shapes.dart'; +import '../shapes/shape.dart'; import 'snapshot.dart'; mixin Originator implements Shapes, ClassicApp { diff --git a/patterns/memento/memento_editor/shapes/selected_shape.dart b/patterns/memento/memento_editor/shapes/selected_shape.dart index 6797977..941b3da 100644 --- a/patterns/memento/memento_editor/shapes/selected_shape.dart +++ b/patterns/memento/memento_editor/shapes/selected_shape.dart @@ -1,27 +1,46 @@ -import 'dart:ui'; - -import 'shape.dart'; +part of 'shape.dart'; class SelectedShape { final Shape shape; + final void Function() repaint; - SelectedShape(this.shape, this._xStart, this._yStart); + SelectedShape(this.shape, this._xStart, this._yStart, this.repaint); void changeSize(double delta) { final currentSize = shape.size; - final newSize = currentSize - delta; + var newSize = currentSize - delta; + + if (newSize == shape.size) { + return; + } - if (newSize != shape.size) { - shape.size = newSize; + if (newSize < 10) { + newSize = 10; + } else if (newSize > 200) { + newSize = 200; } + + shape._size = newSize; + repaint(); } final double _xStart; final double _yStart; void dragTo(double x, double y) { - shape.x = x + _xStart; - shape.y = y + _yStart; + shape._x = x + _xStart; + shape._y = y + _yStart; + + repaint(); + } + + void changeColor(Color newColor) { + if (shape.color == newColor) { + return; + } + + shape._color = newColor; + repaint(); } void paintSelectionBox(Canvas canvas) { diff --git a/patterns/memento/memento_editor/shapes/shape.dart b/patterns/memento/memento_editor/shapes/shape.dart index 28369c5..09233c3 100644 --- a/patterns/memento/memento_editor/shapes/shape.dart +++ b/patterns/memento/memento_editor/shapes/shape.dart @@ -1,30 +1,34 @@ +// ignore_for_file: prefer_final_fields + import 'dart:ui'; +part 'selected_shape.dart'; + class Shape { - double x; - double y; - Color color; + double _x; + + double get x => _x; + + double _y; + + double get y => _y; + + Color _color; + + Color get color => _color; + double _size; double get size => _size; - set size(double newSize) { - if (newSize < 10) { - newSize = 10; - } else if (newSize > 200) { - newSize = 200; - } - _size = newSize; - } - Shape( - this.x, - this.y, [ - this.color = const Color(0xFFFFFFFF), + this._x, + this._y, [ + this._color = const Color(0xFFFFFFFF), this._size = 60.0, ]); - static final paintStroke = Paint() + static final _paintStroke = Paint() ..style = PaintingStyle.stroke ..color = Color(0xFFD81B60) ..strokeWidth = 2; @@ -34,8 +38,9 @@ class Shape { ..style = PaintingStyle.fill ..color = color; - canvas.drawCircle(Offset(x, y), _size, paintFill); - canvas.drawCircle(Offset(x, y), _size, paintStroke); + final offset = Offset(x, y); + canvas.drawCircle(offset, _size, paintFill); + canvas.drawCircle(offset, _size, _paintStroke); } bool isBounded(double x, double y) { diff --git a/patterns/memento/memento_editor/shapes/shapes.dart b/patterns/memento/memento_editor/shapes/shapes.dart index c360503..09e4e58 100644 --- a/patterns/memento/memento_editor/shapes/shapes.dart +++ b/patterns/memento/memento_editor/shapes/shapes.dart @@ -1,8 +1,9 @@ import 'dart:ui'; -import 'selected_shape.dart'; +import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; import 'shape.dart'; -mixin Shapes { + +mixin Shapes implements ClassicApp{ final shapes = []; SelectedShape? _selectedShape; @@ -13,7 +14,7 @@ mixin Shapes { final shape = findShape(x, y); if (shape != null) { - _selectedShape = SelectedShape(shape, shape.x - x, shape.y - y); + _selectedShape = SelectedShape(shape, shape.x - x, shape.y - y, repaint); } else { _selectedShape = null; } @@ -25,7 +26,7 @@ mixin Shapes { } if (index <= shapes.length - 1) { - _selectedShape = SelectedShape(shapes[index], 0, 0); + _selectedShape = SelectedShape(shapes[index], 0, 0, repaint); } } diff --git a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart index 70d5cfd..a9c3259 100644 --- a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart @@ -52,8 +52,8 @@ class ShapePropertiesWidget extends StatelessWidget { currentColor: app.editor.selectedShape?.shape.color, colors: colors, onColorSelect: (newColor) { - app.editor.selectedShape?.shape.color = newColor; - app.editor.repaint(); + app.editor.selectedShape?.changeColor(newColor); + }, ), ], From aa2e582fe7de7ad5ac781e262ea5cbb9bea4d9fc Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 26 Apr 2022 22:53:26 +0300 Subject: [PATCH 261/479] Rename SelectedShape to ActiveShape. --- .../memento/memento_editor/editor/editor.dart | 2 +- .../memento_editor/editor/manipulator.dart | 10 +++++----- .../memento_pattern/caretaker.dart | 2 +- .../memento_pattern/originator.dart | 4 ++-- ...{selected_shape.dart => active_shape.dart} | 4 ++-- .../memento/memento_editor/shapes/shape.dart | 2 +- .../memento/memento_editor/shapes/shapes.dart | 19 +++++++++---------- .../panels/shape_properties_widget.dart | 12 ++++++------ 8 files changed, 27 insertions(+), 28 deletions(-) rename patterns/memento/memento_editor/shapes/{selected_shape.dart => active_shape.dart} (91%) diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index e4ea879..8ab97ac 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -10,7 +10,7 @@ class Editor extends ClassicApp with Manipulator, Shapes, Originator { void onPaint(Canvas canvas, Size canvasSize) { _paintBackground(canvas, canvasSize); paintShapes(canvas); - selectedShape?.paintSelectionBox(canvas); + activeShape?.paintSelectionBox(canvas); } void _paintBackground(Canvas canvas, Size canvasSize) { diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart index 90e9b14..c883e97 100644 --- a/patterns/memento/memento_editor/editor/manipulator.dart +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -7,15 +7,15 @@ mixin Manipulator implements ClassicApp, Shapes { @override void onMouseDown(double x, double y) { _isMouseDown = true; - final currSelection = selectedShape; + final currSelection = activeShape; select(x, y); - if (currSelection == selectedShape) { + if (currSelection == activeShape) { return; } - if (selectedShape == null) { + if (activeShape == null) { unSelect(); } @@ -25,14 +25,14 @@ mixin Manipulator implements ClassicApp, Shapes { @override void onMouseMove(double x, double y) { if (_isMouseDown) { - selectedShape?.dragTo(x, y); + activeShape?.dragTo(x, y); repaint(); } } @override void onPointerWheel(double deltaX, double deltaY) { - selectedShape?.changeSize(deltaY / 5); + activeShape?.changeSize(deltaY / 5); } @override diff --git a/patterns/memento/memento_editor/memento_pattern/caretaker.dart b/patterns/memento/memento_editor/memento_pattern/caretaker.dart index 971e3f5..e736f00 100644 --- a/patterns/memento/memento_editor/memento_pattern/caretaker.dart +++ b/patterns/memento/memento_editor/memento_pattern/caretaker.dart @@ -7,7 +7,7 @@ class Caretaker { List get list => List.unmodifiable(_mementoList); void addMemento(Memento memento) { - _mementoList.add(memento); + _mementoList.add(memento); } bool isSnapshotExists(Snapshot snapshot) { diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index 078d2df..8db1b44 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -41,10 +41,10 @@ mixin Originator implements Shapes, ClassicApp { void _writeSelectedIndex(ByteData data) { late final int selectedIndex; - if (selectedShape == null) { + if (activeShape == null) { selectedIndex = -1; } else { - selectedIndex = shapes.indexOf(selectedShape!.shape); + selectedIndex = shapes.indexOf(activeShape!.shape); } final byteOffset = data.lengthInBytes - _selectedIndexByteSize; diff --git a/patterns/memento/memento_editor/shapes/selected_shape.dart b/patterns/memento/memento_editor/shapes/active_shape.dart similarity index 91% rename from patterns/memento/memento_editor/shapes/selected_shape.dart rename to patterns/memento/memento_editor/shapes/active_shape.dart index 941b3da..b13a21d 100644 --- a/patterns/memento/memento_editor/shapes/selected_shape.dart +++ b/patterns/memento/memento_editor/shapes/active_shape.dart @@ -1,10 +1,10 @@ part of 'shape.dart'; -class SelectedShape { +class ActiveShape { final Shape shape; final void Function() repaint; - SelectedShape(this.shape, this._xStart, this._yStart, this.repaint); + ActiveShape(this.shape, this._xStart, this._yStart, this.repaint); void changeSize(double delta) { final currentSize = shape.size; diff --git a/patterns/memento/memento_editor/shapes/shape.dart b/patterns/memento/memento_editor/shapes/shape.dart index 09233c3..fa27539 100644 --- a/patterns/memento/memento_editor/shapes/shape.dart +++ b/patterns/memento/memento_editor/shapes/shape.dart @@ -2,7 +2,7 @@ import 'dart:ui'; -part 'selected_shape.dart'; +part 'active_shape.dart'; class Shape { double _x; diff --git a/patterns/memento/memento_editor/shapes/shapes.dart b/patterns/memento/memento_editor/shapes/shapes.dart index 09e4e58..83370b7 100644 --- a/patterns/memento/memento_editor/shapes/shapes.dart +++ b/patterns/memento/memento_editor/shapes/shapes.dart @@ -2,21 +2,20 @@ import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; import 'shape.dart'; - -mixin Shapes implements ClassicApp{ +mixin Shapes implements ClassicApp { final shapes = []; - SelectedShape? _selectedShape; + ActiveShape? _activeShape; - SelectedShape? get selectedShape => _selectedShape; + ActiveShape? get activeShape => _activeShape; void select(double x, double y) { final shape = findShape(x, y); if (shape != null) { - _selectedShape = SelectedShape(shape, shape.x - x, shape.y - y, repaint); + _activeShape = ActiveShape(shape, shape.x - x, shape.y - y, repaint); } else { - _selectedShape = null; + _activeShape = null; } } @@ -26,16 +25,16 @@ mixin Shapes implements ClassicApp{ } if (index <= shapes.length - 1) { - _selectedShape = SelectedShape(shapes[index], 0, 0, repaint); + _activeShape = ActiveShape(shapes[index], 0, 0, repaint); } } void unSelect() { - _selectedShape = null; + _activeShape = null; } Shape? findShape(double x, double y) { - for (final shape in shapes) { + for (final shape in shapes.reversed) { if (shape.isBounded(x, y)) { return shape; } @@ -45,7 +44,7 @@ mixin Shapes implements ClassicApp{ } void paintShapes(Canvas canvas) { - for (final shape in shapes.reversed) { + for (final shape in shapes) { shape.paint(canvas); } } diff --git a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart index a9c3259..f923727 100644 --- a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart @@ -26,15 +26,15 @@ class ShapePropertiesWidget extends StatelessWidget { children: [ Row( children: [ - _buildNumberField('x:', app.editor.selectedShape?.shape.x), + _buildNumberField('x:', app.editor.activeShape?.shape.x), SizedBox(width: 20), - _buildNumberField('y:', app.editor.selectedShape?.shape.y), + _buildNumberField('y:', app.editor.activeShape?.shape.y), ], ), SizedBox(height: 20), _buildNumberField( 'size:', - app.editor.selectedShape?.shape.size, + app.editor.activeShape?.shape.size, ), SizedBox(height: 20), Row( @@ -43,16 +43,16 @@ class ShapePropertiesWidget extends StatelessWidget { 'color:', style: TextStyle( color: Colors.black.withOpacity( - app.editor.selectedShape == null ? 0.5 : 1.0, + app.editor.activeShape == null ? 0.5 : 1.0, ), ), ), SizedBox(width: 10), ColorsWidget( - currentColor: app.editor.selectedShape?.shape.color, + currentColor: app.editor.activeShape?.shape.color, colors: colors, onColorSelect: (newColor) { - app.editor.selectedShape?.changeColor(newColor); + app.editor.activeShape?.changeColor(newColor); }, ), From 2b74748d1241efd54f747138638ce7b4acb3822b Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 27 Apr 2022 00:21:36 +0300 Subject: [PATCH 262/479] Rename memento event. --- patterns/memento/memento_editor/application.dart | 4 ++-- .../{create_memento_event.dart => memento_create_event.dart} | 2 +- .../memento/memento_editor/widgets/panels/memento_widget.dart | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename patterns/memento/memento_editor/editor/{create_memento_event.dart => memento_create_event.dart} (59%) diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index af31cdb..a98d3cc 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'editor/create_memento_event.dart'; +import 'editor/memento_create_event.dart'; import 'editor/editor.dart'; import 'memento_pattern/caretaker.dart'; import 'memento_pattern/memento.dart'; @@ -32,7 +32,7 @@ class MementoEditorApplication { final memento = Memento(DateTime.now(), snapshot); caretaker.addMemento(memento); - editor.events.notify(CreateMementoEvent()); + editor.events.notify(MementoCreateEvent()); } void restoreState(Memento memento) { diff --git a/patterns/memento/memento_editor/editor/create_memento_event.dart b/patterns/memento/memento_editor/editor/memento_create_event.dart similarity index 59% rename from patterns/memento/memento_editor/editor/create_memento_event.dart rename to patterns/memento/memento_editor/editor/memento_create_event.dart index 51acd4c..67e459a 100644 --- a/patterns/memento/memento_editor/editor/create_memento_event.dart +++ b/patterns/memento/memento_editor/editor/memento_create_event.dart @@ -1,3 +1,3 @@ import '../../../observer/app_observer/observer/event.dart'; -class CreateMementoEvent extends Event {} +class MementoCreateEvent extends Event {} diff --git a/patterns/memento/memento_editor/widgets/panels/memento_widget.dart b/patterns/memento/memento_editor/widgets/panels/memento_widget.dart index 13c5c53..c61dc67 100644 --- a/patterns/memento/memento_editor/widgets/panels/memento_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/memento_widget.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import '../../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; import '../../application.dart'; -import '../../editor/create_memento_event.dart'; +import '../../editor/memento_create_event.dart'; import '../composite/named_panel.dart'; import '../composite/snapshot_list_widget.dart'; @@ -21,7 +21,7 @@ class MementoWidget extends StatelessWidget { _buildSaveStateButton(), SizedBox(height: 5), Expanded( - child: SubscriberWidget( + child: SubscriberWidget( observer: app.editor.events, builder: (buildContext, event) { return SnapshotListWidget( From 3ae4283dcc18f2fbb1e8890cc068a00c3106c1fc Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 27 Apr 2022 01:15:59 +0300 Subject: [PATCH 263/479] Add README.md. --- patterns/memento/memento_editor/README.md | 48 +++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 patterns/memento/memento_editor/README.md diff --git a/patterns/memento/memento_editor/README.md b/patterns/memento/memento_editor/README.md new file mode 100644 index 0000000..622c72f --- /dev/null +++ b/patterns/memento/memento_editor/README.md @@ -0,0 +1,48 @@ +# 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). + +### Online demo: +Click on the picture to see a [demo](https://RefactoringGuru.github.io/design-patterns-dart/#/memento/flutter_memento_editor). + +[![image](https://user-images.githubusercontent.com/8049534/165401175-88bc4593-4624-45b4-8c03-6f1390ed771a.png)](https://refactoringguru.github.io/design-patterns-dart/#/memento/flutter_memento_editor) + + +### Dependency Patterns +This complex example includes these implementations: +- [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer)] +- [[SubscriberWidget](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] + +### Diagram: +![image](https://user-images.githubusercontent.com/8049534/165399085-06835617-8ef1-4e2f-930f-03d730433afb.png) + +### Client code: +```dart +class MementoEditorApplication { + final editor = Editor(); + final caretaker = Caretaker(); + + void createDefaultShapes() {/*...*/} + + void saveState() { + final snapshot = editor.backup(); + + if (caretaker.isSnapshotExists(snapshot)) { + return; + } + + final memento = Memento(DateTime.now(), snapshot); + caretaker.addMemento(memento); + editor.events.notify(MementoCreateEvent()); + } + + void restoreState(Memento memento) { + editor + ..unSelect() + ..restore(memento.snapshot) + ..repaint(); + } +} +``` From 849a2c0da7818319afc6b69e243ba265a9a105e8 Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 27 Apr 2022 01:20:22 +0300 Subject: [PATCH 264/479] Bump version 0.18.0. --- CHANGELOG.md | 3 +++ README.md | 4 ++-- pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b55e030..21d69b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.18.0 +- Add Memento Editor. + ## 0.17.16 - refactoring - Simplifying the ternary construction. - Remove multiline comment from main README. diff --git a/README.md b/README.md index 87b9ef5..84db0c3 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ It contains **Dart** examples for all classic **GoF** design patterns. - [ ] Interpreter - [ ] **Iterator** - [ ] **Mediator** - - [x] **Memento** - [[Conceptual](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/memento/conceptual)] + - [x] **Memento** - [[Conceptual](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/memento/conceptual)] [[Memento Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/memento/memento_editor)] - [x] **Observer** - [[Open-Close Editor Events](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/open_close_editor_events)] [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer)] [[Subscriber Flutter Widget](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] - [ ] **State** - [ ] **Template Method** @@ -33,7 +33,7 @@ It contains **Dart** examples for all classic **GoF** design patterns. ## Requirements The examples were written in **Dart 2.15**. -Some complex examples require **Flutter 2.15.0**. +Some complex examples require **Flutter 2.12**. ## Contributor's Guide diff --git a/pubspec.yaml b/pubspec.yaml index b2c58fc..43d8765 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.17.16 +version: 0.18.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 0f699ed26d009d65ea4e0230ea859f1a74d20375 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 28 Apr 2022 14:51:13 +0300 Subject: [PATCH 265/479] Add implementation AppObserver list. --- patterns/observer/app_observer/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/patterns/observer/app_observer/README.md b/patterns/observer/app_observer/README.md index b1caf8e..b04b276 100644 --- a/patterns/observer/app_observer/README.md +++ b/patterns/observer/app_observer/README.md @@ -5,8 +5,10 @@ multiple objects about any events that happen to the object they’re observing. Tutorial: [here](https://refactoring.guru/design-patterns/observer). ## AppObserver example -This example was created to be used in a more complex example. -A complex example will be implemented later. +This example was created to be used in a more complex examples: +- [[SubscriberWidget](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/subscriber_flutter_widget)] +- [[FlutterAdapter](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/adapter/flutter_adapter)] +- [[MementoEditor](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/memento/memento_editor)] ### Diagram: ![image](https://user-images.githubusercontent.com/8049534/152783537-0c39c119-2e5b-44fb-9be1-a88840c7f7a1.png) From c5117d1b4181c373d4180e2b96a3c7d9c792705c Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 28 Apr 2022 14:57:59 +0300 Subject: [PATCH 266/479] Fix name: isDelete -> isDeleted. --- patterns/observer/app_observer/observer/app_observer.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patterns/observer/app_observer/observer/app_observer.dart b/patterns/observer/app_observer/observer/app_observer.dart index a6fa01a..d78848b 100644 --- a/patterns/observer/app_observer/observer/app_observer.dart +++ b/patterns/observer/app_observer/observer/app_observer.dart @@ -21,9 +21,9 @@ class AppObserver { } void unsubscribe(EventFunction func) { - final isDelete = _subscribers[T.hashCode]?.remove(func) ?? false; + final isDeleted = _subscribers[T.hashCode]?.remove(func) ?? false; - if (isDelete) { + if (isDeleted) { return; } From 2925a3b41f85775fe4fed4228f81552e35ea835b Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 28 Apr 2022 15:02:53 +0300 Subject: [PATCH 267/479] Fix builder pattern & fix user text. --- patterns/builder/color_text_format/README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/patterns/builder/color_text_format/README.md b/patterns/builder/color_text_format/README.md index 4e949c5..c2a5136 100644 --- a/patterns/builder/color_text_format/README.md +++ b/patterns/builder/color_text_format/README.md @@ -8,15 +8,13 @@ https://refactoring.guru/design-patterns/builder Using different text formats. ### Class diagram -![image](https://user-images.githubusercontent.com/8049534/152780645-4eed27fc-a483-432e-99b0-4a77f2adbd5d.png) +![image](https://user-images.githubusercontent.com/8049534/165747411-75d587d4-aa16-4a14-a16d-52f3fa461a11.png) ### Client code: ```dart main() { final reader = ColorTextReader( - text: 'I love looking at the blue sky, ' - 'eating red apples, ' - 'sitting on the green grass.', + text: 'I love looking at the blue sky, eating red apples and sitting on the green grass.', ); final html = reader.convert(HtmlConverter()); From fff04cf9eaa3f61c14726f77538a868920eda634 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 28 Apr 2022 15:15:04 +0300 Subject: [PATCH 268/479] Fix name: shape -> shapes. --- patterns/adapter/text_graphics/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/adapter/text_graphics/README.md b/patterns/adapter/text_graphics/README.md index 25fd1fd..4ee42df 100644 --- a/patterns/adapter/text_graphics/README.md +++ b/patterns/adapter/text_graphics/README.md @@ -8,7 +8,7 @@ https://refactoring.guru/design-patterns/adapter Adding shapes from a third-party library. ### Class Diagram: -![image](https://user-images.githubusercontent.com/8049534/146028651-1262b66d-b0ac-4fc9-9487-76a2d588f97e.png) +![image](https://user-images.githubusercontent.com/8049534/165749605-5361d5e2-e4a1-41ed-9d27-1c4965fa30d2.png) ### Client code: ```dart From a08bd5311ee28569a571340984ad2b5735da0a24 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 28 Apr 2022 15:23:54 +0300 Subject: [PATCH 269/479] Fix: online link 'a' to 'the'. --- patterns/memento/memento_editor/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/patterns/memento/memento_editor/README.md b/patterns/memento/memento_editor/README.md index 622c72f..7ab3feb 100644 --- a/patterns/memento/memento_editor/README.md +++ b/patterns/memento/memento_editor/README.md @@ -5,11 +5,10 @@ object without revealing the details of its implementation. Tutorial: [here](https://refactoring.guru/design-patterns/memento). ### Online demo: -Click on the picture to see a [demo](https://RefactoringGuru.github.io/design-patterns-dart/#/memento/flutter_memento_editor). +Click on the picture to see the [demo](https://RefactoringGuru.github.io/design-patterns-dart/#/memento/flutter_memento_editor). [![image](https://user-images.githubusercontent.com/8049534/165401175-88bc4593-4624-45b4-8c03-6f1390ed771a.png)](https://refactoringguru.github.io/design-patterns-dart/#/memento/flutter_memento_editor) - ### Dependency Patterns This complex example includes these implementations: - [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer)] From 7a518e997bafc0b7335fa3b8114eec9dbe518ff4 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 28 Apr 2022 15:54:48 +0300 Subject: [PATCH 270/479] Only use the repaint event in ActiveShape. --- .../memento_editor/editor/manipulator.dart | 15 +-------------- .../memento_pattern/originator.dart | 3 +-- .../memento_editor/shapes/active_shape.dart | 9 +++++++-- .../memento/memento_editor/shapes/shapes.dart | 7 ++++++- .../widgets/panels/shape_properties_widget.dart | 1 - 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart index c883e97..53cd7ba 100644 --- a/patterns/memento/memento_editor/editor/manipulator.dart +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -1,32 +1,19 @@ import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; import '../shapes/shapes.dart'; -mixin Manipulator implements ClassicApp, Shapes { +mixin Manipulator implements Shapes { var _isMouseDown = false; @override void onMouseDown(double x, double y) { _isMouseDown = true; - final currSelection = activeShape; - select(x, y); - - if (currSelection == activeShape) { - return; - } - - if (activeShape == null) { - unSelect(); - } - - repaint(); } @override void onMouseMove(double x, double y) { if (_isMouseDown) { activeShape?.dragTo(x, y); - repaint(); } } diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index 8db1b44..f46a1ae 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -2,12 +2,11 @@ import 'dart:convert'; import 'dart:typed_data'; import 'dart:ui'; -import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; import '../shapes/shapes.dart'; import '../shapes/shape.dart'; import 'snapshot.dart'; -mixin Originator implements Shapes, ClassicApp { +mixin Originator implements Shapes { Snapshot backup() { final data = _allocateBuffer(); _writeShapes(data); diff --git a/patterns/memento/memento_editor/shapes/active_shape.dart b/patterns/memento/memento_editor/shapes/active_shape.dart index b13a21d..2892e90 100644 --- a/patterns/memento/memento_editor/shapes/active_shape.dart +++ b/patterns/memento/memento_editor/shapes/active_shape.dart @@ -4,7 +4,9 @@ class ActiveShape { final Shape shape; final void Function() repaint; - ActiveShape(this.shape, this._xStart, this._yStart, this.repaint); + ActiveShape(this.shape, this._xStart, this._yStart, this.repaint) { + repaint(); + } void changeSize(double delta) { final currentSize = shape.size; @@ -30,7 +32,6 @@ class ActiveShape { void dragTo(double x, double y) { shape._x = x + _xStart; shape._y = y + _yStart; - repaint(); } @@ -53,4 +54,8 @@ class ActiveShape { ..color = Color(0xff26e6ff), ); } + + void dispose() { + repaint(); + } } diff --git a/patterns/memento/memento_editor/shapes/shapes.dart b/patterns/memento/memento_editor/shapes/shapes.dart index 83370b7..deaa151 100644 --- a/patterns/memento/memento_editor/shapes/shapes.dart +++ b/patterns/memento/memento_editor/shapes/shapes.dart @@ -15,7 +15,7 @@ mixin Shapes implements ClassicApp { if (shape != null) { _activeShape = ActiveShape(shape, shape.x - x, shape.y - y, repaint); } else { - _activeShape = null; + unSelect(); } } @@ -30,6 +30,11 @@ mixin Shapes implements ClassicApp { } void unSelect() { + if (_activeShape == null) { + return; + } + + _activeShape?.dispose(); _activeShape = null; } diff --git a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart index f923727..8671de9 100644 --- a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart @@ -53,7 +53,6 @@ class ShapePropertiesWidget extends StatelessWidget { colors: colors, onColorSelect: (newColor) { app.editor.activeShape?.changeColor(newColor); - }, ), ], From 942cbfe9fcbf2e9844e940015f24b5935c8c800d Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 28 Apr 2022 16:06:15 +0300 Subject: [PATCH 271/479] Add repaint event to diagram. --- patterns/memento/memento_editor/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/memento/memento_editor/README.md b/patterns/memento/memento_editor/README.md index 7ab3feb..bc794f9 100644 --- a/patterns/memento/memento_editor/README.md +++ b/patterns/memento/memento_editor/README.md @@ -15,7 +15,7 @@ This complex example includes these implementations: - [[SubscriberWidget](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] ### Diagram: -![image](https://user-images.githubusercontent.com/8049534/165399085-06835617-8ef1-4e2f-930f-03d730433afb.png) +![image](https://user-images.githubusercontent.com/8049534/165758516-1de543f5-666d-4e07-958d-2d8fceb73af9.png) ### Client code: ```dart From 4df3f8ebdda21392394788e0ad4d63f291dee804 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 28 Apr 2022 16:08:00 +0300 Subject: [PATCH 272/479] Add repaint event to diagram. --- patterns/memento/memento_editor/editor/manipulator.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart index 53cd7ba..4124062 100644 --- a/patterns/memento/memento_editor/editor/manipulator.dart +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -1,4 +1,3 @@ -import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; import '../shapes/shapes.dart'; mixin Manipulator implements Shapes { From a0c853dbb32e5191a602b20d7b3e658e58ec5266 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 28 Apr 2022 16:12:27 +0300 Subject: [PATCH 273/479] Replace 'master' to 'main' in md files. --- README.md | 20 +++++++++---------- patterns/adapter/flutter_adapter/README.md | 4 ++-- .../bridge/devices_remote_control/README.md | 2 +- .../server_middleware/README.md | 2 +- patterns/command/text_editor/README.md | 2 +- patterns/composite/image_editor/README.md | 2 +- patterns/memento/memento_editor/README.md | 4 ++-- .../open_close_editor_events/README.md | 2 +- .../subscriber_flutter_widget/README.md | 2 +- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 84db0c3..b4fe095 100644 --- a/README.md +++ b/README.md @@ -5,27 +5,27 @@ It contains **Dart** examples for all classic **GoF** design patterns. # Implementation checklist: - [ ] **Creation** - [ ] **Abstract Factory** - - [x] **Builder** - [[Color Text Format](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/builder/color_text_format)] + - [x] **Builder** - [[Color Text Format](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/builder/color_text_format)] - [ ] **Factory Method** - - [x] **Prototype**] - [[Shapes](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/prototype/shapes)] + - [x] **Prototype**] - [[Shapes](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/prototype/shapes)] - [ ] **Singleton** - [ ] **Behavioral** - - [x] **Chain of Responsibility** - [[Server Middleware](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/chain_of_responsibility/server_middleware)] - - [x] **Command** - [[Text Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/command/text_editor)] + - [x] **Chain of Responsibility** - [[Server Middleware](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/chain_of_responsibility/server_middleware)] + - [x] **Command** - [[Text Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/command/text_editor)] - [ ] Interpreter - [ ] **Iterator** - [ ] **Mediator** - - [x] **Memento** - [[Conceptual](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/memento/conceptual)] [[Memento Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/memento/memento_editor)] - - [x] **Observer** - [[Open-Close Editor Events](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/open_close_editor_events)] [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer)] [[Subscriber Flutter Widget](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] + - [x] **Memento** - [[Conceptual](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/memento/conceptual)] [[Memento Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/memento/memento_editor)] + - [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** - [ ] **Strategy** - [ ] **Structural** - - [x] **Adapter** - [[Text Graphics](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/text_graphics)] [[Square Round conflict](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/square_round_conflict)] [[Flutter Adapter](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/adapter/flutter_adapter)] - - [x] **Bridge** - [[Remote Device Control](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/bridge/devices_remote_control)] [[Clock](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/bridge/clock)] - - [x] **Composite** - [[Image Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/composite/image_editor)] [[Products and Boxes](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/composite/products_and_boxes)] - - [x] **Decorator** - [[Data Source Decoder](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/decorator/data_source_decoder)] + - [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)] + - [x] **Composite** - [[Image Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/composite/image_editor)] [[Products and Boxes](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/composite/products_and_boxes)] + - [x] **Decorator** - [[Data Source Decoder](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/decorator/data_source_decoder)] - [ ] **Facade** - [ ] **Flyweight** - [ ] **Proxy** diff --git a/patterns/adapter/flutter_adapter/README.md b/patterns/adapter/flutter_adapter/README.md index 41320ff..aff519c 100644 --- a/patterns/adapter/flutter_adapter/README.md +++ b/patterns/adapter/flutter_adapter/README.md @@ -9,8 +9,8 @@ This example shows how to adapt a non-reactive classic type application for Flut ### Dependency This complex example includes these implementations: -- [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer)] -- [[SubscriberWidget](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] +- [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/app_observer)] +- [[SubscriberWidget](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/subscriber_flutter_widget)] ### Online demo: Click on the picture to see a [demo](https://refactoringguru.github.io/design-patterns-dart/#/adapter/flutter_adapter). diff --git a/patterns/bridge/devices_remote_control/README.md b/patterns/bridge/devices_remote_control/README.md index 12bd5ed..c731ed1 100644 --- a/patterns/bridge/devices_remote_control/README.md +++ b/patterns/bridge/devices_remote_control/README.md @@ -1,5 +1,5 @@ # Bridge pattern -This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/master/src/refactoring_guru/bridge/example) +This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/main/src/refactoring_guru/bridge/example) But removed the ability to use null for devices. Instead, the EmptyDevice class is used. ### Remote device example diff --git a/patterns/chain_of_responsibility/server_middleware/README.md b/patterns/chain_of_responsibility/server_middleware/README.md index aa38dc3..69aa509 100644 --- a/patterns/chain_of_responsibility/server_middleware/README.md +++ b/patterns/chain_of_responsibility/server_middleware/README.md @@ -8,7 +8,7 @@ Chain of Responsibility is a behavioral design pattern that lets you pass reques https://refactoring.guru/design-patterns/chain-of-responsibility?#problem ### 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) +This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/main/src/refactoring_guru/chain_of_responsibility/example) ### Diagram: ![image](https://user-images.githubusercontent.com/8049534/149488654-7ff5f659-4086-4d1a-ae44-326c71fb880a.png) diff --git a/patterns/command/text_editor/README.md b/patterns/command/text_editor/README.md index 52c2260..1622f96 100644 --- a/patterns/command/text_editor/README.md +++ b/patterns/command/text_editor/README.md @@ -11,7 +11,7 @@ possible to revert an operation if needed. More detailed explanation on [RefactoringGuru](https://refactoring.guru/design-patterns/command?#pseudocode). ## Origin source code: -This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/master/src/refactoring_guru/command/example). +This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/main/src/refactoring_guru/command/example). But slightly changed, see the class diagram. ## Diagram: diff --git a/patterns/composite/image_editor/README.md b/patterns/composite/image_editor/README.md index 1dc597c..dc61e2b 100644 --- a/patterns/composite/image_editor/README.md +++ b/patterns/composite/image_editor/README.md @@ -5,7 +5,7 @@ Composite is a structural design pattern that lets you compose objects into tree https://refactoring.guru/design-patterns/composite?#pseudocode ## Origin source code: -This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/master/src/refactoring_guru/composite/example) +This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/main/src/refactoring_guru/composite/example) ### Diagram: ![image](https://user-images.githubusercontent.com/8049534/149174388-25ca21b1-d762-40b5-a853-528abe18b66c.png) diff --git a/patterns/memento/memento_editor/README.md b/patterns/memento/memento_editor/README.md index bc794f9..6fc92a7 100644 --- a/patterns/memento/memento_editor/README.md +++ b/patterns/memento/memento_editor/README.md @@ -11,8 +11,8 @@ Click on the picture to see the [demo](https://RefactoringGuru.github.io/design- ### Dependency Patterns This complex example includes these implementations: -- [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer)] -- [[SubscriberWidget](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] +- [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/app_observer)] +- [[SubscriberWidget](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/subscriber_flutter_widget)] ### Diagram: ![image](https://user-images.githubusercontent.com/8049534/165758516-1de543f5-666d-4e07-958d-2d8fceb73af9.png) diff --git a/patterns/observer/open_close_editor_events/README.md b/patterns/observer/open_close_editor_events/README.md index fa69f69..613f1dc 100644 --- a/patterns/observer/open_close_editor_events/README.md +++ b/patterns/observer/open_close_editor_events/README.md @@ -9,7 +9,7 @@ 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). +This example rewrite from [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/main/src/refactoring_guru/observer/example). ### Diagram: ![image](https://user-images.githubusercontent.com/8049534/150955865-7fbb29f3-ed48-4317-a356-cbb9cc79ed11.png) diff --git a/patterns/observer/subscriber_flutter_widget/README.md b/patterns/observer/subscriber_flutter_widget/README.md index f1b586e..06fb727 100644 --- a/patterns/observer/subscriber_flutter_widget/README.md +++ b/patterns/observer/subscriber_flutter_widget/README.md @@ -7,7 +7,7 @@ Tutorial: [here](https://refactoring.guru/design-patterns/observer). ## Subscriber flutter widget example This is a complex example of an Observer pattern, connected to a Flutter application. The example includes, the previously implemented **AppObserver** example pattern, -which you can see [here](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer). +which you can see [here](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/app_observer). ### Online demo: Click on the picture to see a [demo](https://RefactoringGuru.github.io/design-patterns-dart/#/observer/subscriber_flutter_widget). From 1f5925f0096c7d3c49ef4e29d550b5a798f346b5 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 28 Apr 2022 16:16:38 +0300 Subject: [PATCH 274/479] Bump version 0.18.9 - small fix-refactoring. --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 43d8765..bfc44e3 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.18.0 +version: 0.18.9 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 4b6868f19de87ff235c63fc46ac408df29d20c4f Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 29 Apr 2022 15:05:28 +0300 Subject: [PATCH 275/479] Fix local-remote host to web demos logo. --- bin/deploy_flutter_demos.dart | 28 +++++++++++++++++++++++----- web/index.html | 2 +- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/bin/deploy_flutter_demos.dart b/bin/deploy_flutter_demos.dart index 387cd87..9fed5c2 100644 --- a/bin/deploy_flutter_demos.dart +++ b/bin/deploy_flutter_demos.dart @@ -14,11 +14,17 @@ void main() async { await Future.wait([prepareRepository, buildProject]); + prepareIndexHtmlForRemoteHost(); copyFiles(); await pushToOrigin(); clear(); } +late final tmpDir = Directory.systemTemp.createTempSync(); +late final projectDir = thisPath(r'..\'); +late final webBuildDir = Directory(projectDir.uri.toFilePath() + r'build\web'); +late final String originUrl; + Future init() async { print('Use temp: $tmpDir'); originUrl = await repositoryOriginUrl(projectDir); @@ -45,6 +51,23 @@ Future fetchUpstream() async { await cmd('git fetch upstream', workingDirectory: tmpDir); } +void prepareIndexHtmlForRemoteHost() { + print('Prepare "index.html" for remote host.'); + + final indexHtmlFile = File(projectDir.uri.toFilePath() + r'\index.html'); + final indexContent = indexHtmlFile.readAsStringSync(); + final fixedIndexContent = indexContent.replaceFirst( + r'', + r'', + ); + + if (indexContent == fixedIndexContent) { + throw 'Base url not found. It should be '; + } + + indexHtmlFile.writeAsStringSync(fixedIndexContent); +} + void copyFiles() { print('Copy files:'); copyDirectory(webBuildDir, tmpDir); @@ -66,11 +89,6 @@ Future pushToOrigin() async { ); } -late final tmpDir = Directory.systemTemp.createTempSync(); -late final projectDir = thisPath(r'..\'); -late final webBuildDir = Directory(projectDir.uri.toFilePath() + r'build\web'); -late final String originUrl; - void clear() { print('Clear: $tmpDir'); tmpDir.deleteSync(recursive: true); diff --git a/web/index.html b/web/index.html index d2d6493..c8c2113 100644 --- a/web/index.html +++ b/web/index.html @@ -1,7 +1,7 @@ - + From 5d11f07bb67362301b9fb630cb912e221d39d624 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 29 Apr 2022 15:13:01 +0300 Subject: [PATCH 276/479] Fix local-remote host to web demos logo. --- bin/deploy_flutter_demos.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/deploy_flutter_demos.dart b/bin/deploy_flutter_demos.dart index 9fed5c2..aa713f3 100644 --- a/bin/deploy_flutter_demos.dart +++ b/bin/deploy_flutter_demos.dart @@ -54,7 +54,7 @@ Future fetchUpstream() async { void prepareIndexHtmlForRemoteHost() { print('Prepare "index.html" for remote host.'); - final indexHtmlFile = File(projectDir.uri.toFilePath() + r'\index.html'); + final indexHtmlFile = File(webBuildDir.path + r'\index.html'); final indexContent = indexHtmlFile.readAsStringSync(); final fixedIndexContent = indexContent.replaceFirst( r'', From 9b341b20441b4bc4ada51dc835892783b1a4e28a Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 29 Apr 2022 16:00:47 +0300 Subject: [PATCH 277/479] Add step-by-step contribute instruction. --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b4fe095..7cdc7a9 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ It contains **Dart** examples for all classic **GoF** design patterns. - [ ] **Abstract Factory** - [x] **Builder** - [[Color Text Format](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/builder/color_text_format)] - [ ] **Factory Method** - - [x] **Prototype**] - [[Shapes](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/prototype/shapes)] + - [x] **Prototype** - [[Shapes](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/prototype/shapes)] - [ ] **Singleton** - [ ] **Behavioral** - [x] **Chain of Responsibility** - [[Server Middleware](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/chain_of_responsibility/server_middleware)] @@ -39,6 +39,16 @@ Some complex examples require **Flutter 2.12**. 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/). +### Step-by-step instruction +1. Just make a fork. +2. Clone the forked repository to your local machine. +3. Create a new branch and name it, for example: fix-issue-32. +4. Make changes. +5. Create commits and push them to your forked Github repository. +6. Submit a pull request to the master branch. +7. Wait for review. + +### Style guide 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). From d945b5e7a6a7641103b0427c1468f38d89459a3e Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 29 Apr 2022 19:37:34 +0300 Subject: [PATCH 278/479] Impl abstract factory example. --- .../app/application.dart | 18 +++++++++++++++++ .../conceptual_gui_factory/button/button.dart | 3 +++ .../button/mac_os_button.dart | 8 ++++++++ .../button/windows_button.dart | 8 ++++++++ .../checkbox/checkbox.dart | 3 +++ .../checkbox/mac_os_checkbox.dart | 8 ++++++++ .../checkbox/windows_checkbox.dart | 8 ++++++++ .../factories/gui_factory.dart | 20 +++++++++++++++++++ .../factories/mac_os_factory.dart | 13 ++++++++++++ .../factories/window_factory.dart | 13 ++++++++++++ .../conceptual_gui_factory/main.dart | 8 ++++++++ 11 files changed, 110 insertions(+) create mode 100644 patterns/abstract_factory/conceptual_gui_factory/app/application.dart create mode 100644 patterns/abstract_factory/conceptual_gui_factory/button/button.dart create mode 100644 patterns/abstract_factory/conceptual_gui_factory/button/mac_os_button.dart create mode 100644 patterns/abstract_factory/conceptual_gui_factory/button/windows_button.dart create mode 100644 patterns/abstract_factory/conceptual_gui_factory/checkbox/checkbox.dart create mode 100644 patterns/abstract_factory/conceptual_gui_factory/checkbox/mac_os_checkbox.dart create mode 100644 patterns/abstract_factory/conceptual_gui_factory/checkbox/windows_checkbox.dart create mode 100644 patterns/abstract_factory/conceptual_gui_factory/factories/gui_factory.dart create mode 100644 patterns/abstract_factory/conceptual_gui_factory/factories/mac_os_factory.dart create mode 100644 patterns/abstract_factory/conceptual_gui_factory/factories/window_factory.dart create mode 100644 patterns/abstract_factory/conceptual_gui_factory/main.dart diff --git a/patterns/abstract_factory/conceptual_gui_factory/app/application.dart b/patterns/abstract_factory/conceptual_gui_factory/app/application.dart new file mode 100644 index 0000000..a14e494 --- /dev/null +++ b/patterns/abstract_factory/conceptual_gui_factory/app/application.dart @@ -0,0 +1,18 @@ +import '../button/button.dart'; +import '../checkbox/checkbox.dart'; +import '../factories/gui_factory.dart'; + +class Application { + late Button _button; + late Checkbox _checkbox; + + Application(GUIFactory factory) { + _button = factory.createButton(); + _checkbox = factory.createCheckbox(); + } + + void paint() { + _button.paint(); + _checkbox.paint(); + } +} diff --git a/patterns/abstract_factory/conceptual_gui_factory/button/button.dart b/patterns/abstract_factory/conceptual_gui_factory/button/button.dart new file mode 100644 index 0000000..ccf3c96 --- /dev/null +++ b/patterns/abstract_factory/conceptual_gui_factory/button/button.dart @@ -0,0 +1,3 @@ +abstract class Button { + void paint(); +} diff --git a/patterns/abstract_factory/conceptual_gui_factory/button/mac_os_button.dart b/patterns/abstract_factory/conceptual_gui_factory/button/mac_os_button.dart new file mode 100644 index 0000000..d84c7a5 --- /dev/null +++ b/patterns/abstract_factory/conceptual_gui_factory/button/mac_os_button.dart @@ -0,0 +1,8 @@ +import 'button.dart'; + +class MacOSButton implements Button { + @override + void paint() { + print('You have created MacOSButton.'); + } +} diff --git a/patterns/abstract_factory/conceptual_gui_factory/button/windows_button.dart b/patterns/abstract_factory/conceptual_gui_factory/button/windows_button.dart new file mode 100644 index 0000000..790345a --- /dev/null +++ b/patterns/abstract_factory/conceptual_gui_factory/button/windows_button.dart @@ -0,0 +1,8 @@ +import 'button.dart'; + +class WindowsButton implements Button { + @override + void paint() { + print('You have created WindowsButton.'); + } +} diff --git a/patterns/abstract_factory/conceptual_gui_factory/checkbox/checkbox.dart b/patterns/abstract_factory/conceptual_gui_factory/checkbox/checkbox.dart new file mode 100644 index 0000000..caaf517 --- /dev/null +++ b/patterns/abstract_factory/conceptual_gui_factory/checkbox/checkbox.dart @@ -0,0 +1,3 @@ +abstract class Checkbox { + void paint(); +} diff --git a/patterns/abstract_factory/conceptual_gui_factory/checkbox/mac_os_checkbox.dart b/patterns/abstract_factory/conceptual_gui_factory/checkbox/mac_os_checkbox.dart new file mode 100644 index 0000000..26cacfd --- /dev/null +++ b/patterns/abstract_factory/conceptual_gui_factory/checkbox/mac_os_checkbox.dart @@ -0,0 +1,8 @@ +import 'checkbox.dart'; + +class MacOSCheckbox implements Checkbox { + @override + void paint() { + print('You have created MacOSCheckbox.'); + } +} diff --git a/patterns/abstract_factory/conceptual_gui_factory/checkbox/windows_checkbox.dart b/patterns/abstract_factory/conceptual_gui_factory/checkbox/windows_checkbox.dart new file mode 100644 index 0000000..0fa6755 --- /dev/null +++ b/patterns/abstract_factory/conceptual_gui_factory/checkbox/windows_checkbox.dart @@ -0,0 +1,8 @@ +import 'checkbox.dart'; + +class WindowsCheckbox implements Checkbox { + @override + void paint() { + print('You have created WindowsCheckbox.'); + } +} diff --git a/patterns/abstract_factory/conceptual_gui_factory/factories/gui_factory.dart b/patterns/abstract_factory/conceptual_gui_factory/factories/gui_factory.dart new file mode 100644 index 0000000..fca44e2 --- /dev/null +++ b/patterns/abstract_factory/conceptual_gui_factory/factories/gui_factory.dart @@ -0,0 +1,20 @@ +import 'dart:io'; + +import '../button/button.dart'; +import '../checkbox/checkbox.dart'; +import 'mac_os_factory.dart'; +import 'window_factory.dart'; + +abstract class GUIFactory { + factory GUIFactory() { + if (Platform.isMacOS) { + return MacOSFactory(); + } else { + return WindowsFactory(); + } + } + + Button createButton(); + + Checkbox createCheckbox(); +} diff --git a/patterns/abstract_factory/conceptual_gui_factory/factories/mac_os_factory.dart b/patterns/abstract_factory/conceptual_gui_factory/factories/mac_os_factory.dart new file mode 100644 index 0000000..d49f919 --- /dev/null +++ b/patterns/abstract_factory/conceptual_gui_factory/factories/mac_os_factory.dart @@ -0,0 +1,13 @@ +import '../button/button.dart'; +import '../checkbox/checkbox.dart'; +import '../button/mac_os_button.dart'; +import '../checkbox/mac_os_checkbox.dart'; +import 'gui_factory.dart'; + +class MacOSFactory implements GUIFactory { + @override + Button createButton() => MacOSButton(); + + @override + Checkbox createCheckbox() => MacOSCheckbox(); +} diff --git a/patterns/abstract_factory/conceptual_gui_factory/factories/window_factory.dart b/patterns/abstract_factory/conceptual_gui_factory/factories/window_factory.dart new file mode 100644 index 0000000..7592f86 --- /dev/null +++ b/patterns/abstract_factory/conceptual_gui_factory/factories/window_factory.dart @@ -0,0 +1,13 @@ +import '../button/button.dart'; +import '../checkbox/checkbox.dart'; +import '../button/windows_button.dart'; +import '../checkbox/windows_checkbox.dart'; +import 'gui_factory.dart'; + +class WindowsFactory implements GUIFactory { + @override + Button createButton() => WindowsButton(); + + @override + Checkbox createCheckbox() => WindowsCheckbox(); +} diff --git a/patterns/abstract_factory/conceptual_gui_factory/main.dart b/patterns/abstract_factory/conceptual_gui_factory/main.dart new file mode 100644 index 0000000..b946056 --- /dev/null +++ b/patterns/abstract_factory/conceptual_gui_factory/main.dart @@ -0,0 +1,8 @@ +import 'app/application.dart'; +import 'factories/gui_factory.dart'; + +void main() { + final guiFactory = GUIFactory(); + final app = Application(guiFactory); + app.paint(); +} From 2a2b5eb5e5050e255d36b06855525c6da2c2f010 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 29 Apr 2022 19:42:35 +0300 Subject: [PATCH 279/479] Add client code & output to README. --- .../conceptual_gui_factory/README.md | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 patterns/abstract_factory/conceptual_gui_factory/README.md diff --git a/patterns/abstract_factory/conceptual_gui_factory/README.md b/patterns/abstract_factory/conceptual_gui_factory/README.md new file mode 100644 index 0000000..6ea92ca --- /dev/null +++ b/patterns/abstract_factory/conceptual_gui_factory/README.md @@ -0,0 +1,45 @@ +# 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). + +### Online demo: +Click on the picture to see the [demo](https://RefactoringGuru.github.io/design-patterns-dart/#/memento/flutter_memento_editor). + +[![image](https://user-images.githubusercontent.com/8049534/165401175-88bc4593-4624-45b4-8c03-6f1390ed771a.png)](https://refactoringguru.github.io/design-patterns-dart/#/memento/flutter_memento_editor) + +### Dependency Patterns +This complex example includes these implementations: +- [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/app_observer)] +- [[SubscriberWidget](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/subscriber_flutter_widget)] + +### Diagram: +![image](https://user-images.githubusercontent.com/8049534/165758516-1de543f5-666d-4e07-958d-2d8fceb73af9.png) + +### Client code: +```dart +void main() { + final guiFactory = GUIFactory(); + final app = Application(guiFactory); + app.paint(); +} + +abstract class GUIFactory { + factory GUIFactory() { + if (Platform.isMacOS) { + return MacOSFactory(); + } else { + return WindowsFactory(); + } + } + + /*...*/ +} +``` + +### Output: +``` +You have created WindowsButton. +You have created WindowsCheckbox. +``` From f7b1ea6e8e74f722fff35787cee477f5ee2b8297 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 29 Apr 2022 19:50:18 +0300 Subject: [PATCH 280/479] Add diagram & description. --- .../conceptual_gui_factory/README.md | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/patterns/abstract_factory/conceptual_gui_factory/README.md b/patterns/abstract_factory/conceptual_gui_factory/README.md index 6ea92ca..9454266 100644 --- a/patterns/abstract_factory/conceptual_gui_factory/README.md +++ b/patterns/abstract_factory/conceptual_gui_factory/README.md @@ -1,21 +1,14 @@ -# 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. +# Abstract Factory pattern +Abstract Factory is a creational design pattern that lets you produce families of related objects +without specifying their concrete classes. -Tutorial: [here](https://refactoring.guru/design-patterns/memento). +Tutorial: [here](https://refactoring.guru/design-patterns/abstract-factory). -### Online demo: -Click on the picture to see the [demo](https://RefactoringGuru.github.io/design-patterns-dart/#/memento/flutter_memento_editor). - -[![image](https://user-images.githubusercontent.com/8049534/165401175-88bc4593-4624-45b4-8c03-6f1390ed771a.png)](https://refactoringguru.github.io/design-patterns-dart/#/memento/flutter_memento_editor) - -### Dependency Patterns -This complex example includes these implementations: -- [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/app_observer)] -- [[SubscriberWidget](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/subscriber_flutter_widget)] +### About example. +This the very conceptual example rewrite from original source code [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/main/src/refactoring_guru/abstract_factory/example) ### Diagram: -![image](https://user-images.githubusercontent.com/8049534/165758516-1de543f5-666d-4e07-958d-2d8fceb73af9.png) +![image](https://user-images.githubusercontent.com/8049534/165987890-e64db9a3-4865-411c-a5c0-16da21043159.png) ### Client code: ```dart From 6a38aceb819932abaf36c2d955fcde481b79d536 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 29 Apr 2022 19:57:19 +0300 Subject: [PATCH 281/479] Bump version 0.19.0 & update README. --- 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 21d69b6..d340572 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.19.0 +- Add "Conceptual Gui Factory" example. + ## 0.18.0 - Add Memento Editor. diff --git a/README.md b/README.md index b4fe095..c941ef6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ It contains **Dart** examples for all classic **GoF** design patterns. # Implementation checklist: - [ ] **Creation** - - [ ] **Abstract Factory** + - [ ] **Abstract Factory** [[Conceptual Gui Factory](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/abstract_factory/conceptual_gui_factory)] - [x] **Builder** - [[Color Text Format](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/builder/color_text_format)] - [ ] **Factory Method** - [x] **Prototype**] - [[Shapes](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/prototype/shapes)] diff --git a/pubspec.yaml b/pubspec.yaml index bfc44e3..5939bdb 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.18.9 +version: 0.19.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 c6030de6ec83a58f6241cd23f2d081138f280454 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 30 Apr 2022 15:10:25 +0300 Subject: [PATCH 282/479] Impl factory method classes. Rewrite from source origin code. --- .../buttons/button.dart | 10 ++++++ .../buttons/html_button.dart | 14 ++++++++ .../buttons/windows_button.dart | 14 ++++++++ .../dialogs_factory/dialog.dart | 21 +++++++++++ .../dialogs_factory/html_dialog.dart | 11 ++++++ .../dialogs_factory/windows_dialog.dart | 11 ++++++ .../concaptual_platform_dialog/main.dart | 36 +++++++++++++++++++ 7 files changed, 117 insertions(+) create mode 100644 patterns/factory_method/concaptual_platform_dialog/buttons/button.dart create mode 100644 patterns/factory_method/concaptual_platform_dialog/buttons/html_button.dart create mode 100644 patterns/factory_method/concaptual_platform_dialog/buttons/windows_button.dart create mode 100644 patterns/factory_method/concaptual_platform_dialog/dialogs_factory/dialog.dart create mode 100644 patterns/factory_method/concaptual_platform_dialog/dialogs_factory/html_dialog.dart create mode 100644 patterns/factory_method/concaptual_platform_dialog/dialogs_factory/windows_dialog.dart create mode 100644 patterns/factory_method/concaptual_platform_dialog/main.dart diff --git a/patterns/factory_method/concaptual_platform_dialog/buttons/button.dart b/patterns/factory_method/concaptual_platform_dialog/buttons/button.dart new file mode 100644 index 0000000..bdffd72 --- /dev/null +++ b/patterns/factory_method/concaptual_platform_dialog/buttons/button.dart @@ -0,0 +1,10 @@ +/// EN: Common interface for all buttons. +/// +/// RU: Общий интерфейс для всех продуктов. +abstract class Button { + final void Function() onClick; + + Button(this.onClick); + + void render(); +} diff --git a/patterns/factory_method/concaptual_platform_dialog/buttons/html_button.dart b/patterns/factory_method/concaptual_platform_dialog/buttons/html_button.dart new file mode 100644 index 0000000..6619972 --- /dev/null +++ b/patterns/factory_method/concaptual_platform_dialog/buttons/html_button.dart @@ -0,0 +1,14 @@ +import 'button.dart'; + +/// EN: HTML button implementation. +/// +/// RU: Реализация HTML кнопок. +class HtmlButton extends Button { + HtmlButton(void Function() onClick) : super(onClick); + + @override + void render() { + print(''); + onClick(); + } +} diff --git a/patterns/factory_method/concaptual_platform_dialog/buttons/windows_button.dart b/patterns/factory_method/concaptual_platform_dialog/buttons/windows_button.dart new file mode 100644 index 0000000..4a6c4f9 --- /dev/null +++ b/patterns/factory_method/concaptual_platform_dialog/buttons/windows_button.dart @@ -0,0 +1,14 @@ +import 'button.dart'; + +/// EN: Windows button implementation. +/// +/// RU: Реализация нативных кнопок операционной системы. +class WindowsButton extends Button { + WindowsButton(void Function() onClick) : super(onClick); + + @override + void render() { + print('Windows Button'); + onClick(); + } +} diff --git a/patterns/factory_method/concaptual_platform_dialog/dialogs_factory/dialog.dart b/patterns/factory_method/concaptual_platform_dialog/dialogs_factory/dialog.dart new file mode 100644 index 0000000..360a0d2 --- /dev/null +++ b/patterns/factory_method/concaptual_platform_dialog/dialogs_factory/dialog.dart @@ -0,0 +1,21 @@ +import '../buttons/button.dart'; + +abstract class Dialog { + void renderWindow() { + /// EN: ... other code ... + /// + /// RU: ... остальной код диалога ... + + Button okButton = createButton(() { + print('Click! Button says - "Hello World!"'); + }); + okButton.render(); + } + + /// EN: Subclasses will override this method in order to create specific + /// button objects. + /// + /// RU: Подклассы будут переопределять этот метод, чтобы создавать конкретные + /// объекты продуктов, разные для каждой фабрики. + Button createButton(void Function() onClick); +} diff --git a/patterns/factory_method/concaptual_platform_dialog/dialogs_factory/html_dialog.dart b/patterns/factory_method/concaptual_platform_dialog/dialogs_factory/html_dialog.dart new file mode 100644 index 0000000..1af9d7e --- /dev/null +++ b/patterns/factory_method/concaptual_platform_dialog/dialogs_factory/html_dialog.dart @@ -0,0 +1,11 @@ +import '../buttons/button.dart'; +import '../buttons/html_button.dart'; +import 'dialog.dart'; + +/// EN: HTML Dialog will produce HTML buttons. +/// +/// RU: HTML-диалог. +class HtmlDialog extends Dialog { + @override + Button createButton(void Function() onClick) => HtmlButton(onClick); +} diff --git a/patterns/factory_method/concaptual_platform_dialog/dialogs_factory/windows_dialog.dart b/patterns/factory_method/concaptual_platform_dialog/dialogs_factory/windows_dialog.dart new file mode 100644 index 0000000..5445e66 --- /dev/null +++ b/patterns/factory_method/concaptual_platform_dialog/dialogs_factory/windows_dialog.dart @@ -0,0 +1,11 @@ +import '../buttons/button.dart'; +import '../buttons/windows_button.dart'; +import 'dialog.dart'; + +/// EN: Windows Dialog will produce Windows buttons. +/// +/// RU: Диалог на элементах операционной системы. +class WindowsDialog extends Dialog { + @override + Button createButton(void Function() onClick) => WindowsButton(onClick); +} diff --git a/patterns/factory_method/concaptual_platform_dialog/main.dart b/patterns/factory_method/concaptual_platform_dialog/main.dart new file mode 100644 index 0000000..2ea39f6 --- /dev/null +++ b/patterns/factory_method/concaptual_platform_dialog/main.dart @@ -0,0 +1,36 @@ +import 'dart:io'; + +import 'dialogs_factory/dialog.dart'; +import 'dialogs_factory/html_dialog.dart'; +import 'dialogs_factory/windows_dialog.dart'; + +late Dialog dialog; + +void main() { + configure(); + runBusinessLogic(); +} + +/// EN: The concrete factory is usually chosen depending on configuration or +/// environment options. +/// +/// RU: Приложение создаёт определённую фабрику в зависимости от конфигурации +/// или окружения. +void configure() { + if (Platform.isWindows) { + dialog = WindowsDialog(); + } else { + dialog = HtmlDialog(); + } +} + +/// EN: All of the client code should work with factories and products +/// through abstract interfaces. This way it does not care which factory it +/// works with and what kind of product it returns. +/// +/// RU: Весь остальной клиентский код работает с фабрикой и продуктами только +/// через общий интерфейс, поэтому для него неважно какая фабрика была +/// создана. +void runBusinessLogic() { + dialog.renderWindow(); +} From 804f3bff4cfc25236f334fc201bc330b310d3996 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 30 Apr 2022 15:19:07 +0300 Subject: [PATCH 283/479] Add README to example. --- .../concaptual_platform_dialog/README.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 patterns/factory_method/concaptual_platform_dialog/README.md diff --git a/patterns/factory_method/concaptual_platform_dialog/README.md b/patterns/factory_method/concaptual_platform_dialog/README.md new file mode 100644 index 0000000..a67da5c --- /dev/null +++ b/patterns/factory_method/concaptual_platform_dialog/README.md @@ -0,0 +1,39 @@ +# Abstract Factory pattern +Factory Method is a creational design pattern that provides an interface for creating objects in a +superclass, but allows subclasses to alter the type of objects that will be created. + +Tutorial: [here](https://refactoring.guru/design-patterns/factory-method). + +### About example. +This the very conceptual example rewrite from original source code [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/main/src/refactoring_guru/factory_method/example) + +### Diagram: +![image](https://user-images.githubusercontent.com/8049534/166105090-a2b490fe-3e3e-44f1-a781-9777023020fb.png) + +### Client code: +```dart +late Dialog dialog; + +void main() { + configure(); + runBusinessLogic(); +} + +void configure() { + if (Platform.isWindows) { + dialog = WindowsDialog(); + } else { + dialog = HtmlDialog(); + } +} + +void runBusinessLogic() { + dialog.renderWindow(); +} +``` + +### Output: +``` +Windows Button +Click! Button says - "Hello World!" +``` From 0046ad5e8dddfd2987eb5378d617a5f9316893b1 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 30 Apr 2022 15:19:39 +0300 Subject: [PATCH 284/479] Bump version 0.20.0 & update main README. --- CHANGELOG.md | 3 +++ README.md | 4 ++-- pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d340572..364f637 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.20.0 +- Add "Conceptual Dialog Factory" example. + ## 0.19.0 - Add "Conceptual Gui Factory" example. diff --git a/README.md b/README.md index 2aacd54..014c1ea 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ It contains **Dart** examples for all classic **GoF** design patterns. # Implementation checklist: - [ ] **Creation** - - [ ] **Abstract Factory** [[Conceptual Gui Factory](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/abstract_factory/conceptual_gui_factory)] + - [x] **Abstract Factory** [[Conceptual Gui Factory](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/abstract_factory/conceptual_gui_factory)] - [x] **Builder** - [[Color Text Format](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/builder/color_text_format)] - - [ ] **Factory Method** + - [x] **Factory Method** [[Conceptual Platform Dialog](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/factory_method/conceptual_platform_dialog)] - [x] **Prototype** - [[Shapes](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/prototype/shapes)] - [ ] **Singleton** - [ ] **Behavioral** diff --git a/pubspec.yaml b/pubspec.yaml index 5939bdb..83d3c93 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.19.0 +version: 0.20.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 e49672b1ed903b8495e0535ca5d2deaf7ae7a7b9 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sat, 30 Apr 2022 15:23:21 +0300 Subject: [PATCH 285/479] Fix directory name. --- .../README.md | 0 .../buttons/button.dart | 0 .../buttons/html_button.dart | 0 .../buttons/windows_button.dart | 0 .../dialogs_factory/dialog.dart | 0 .../dialogs_factory/html_dialog.dart | 0 .../dialogs_factory/windows_dialog.dart | 0 .../main.dart | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename patterns/factory_method/{concaptual_platform_dialog => conceptual_platform_dialog}/README.md (100%) rename patterns/factory_method/{concaptual_platform_dialog => conceptual_platform_dialog}/buttons/button.dart (100%) rename patterns/factory_method/{concaptual_platform_dialog => conceptual_platform_dialog}/buttons/html_button.dart (100%) rename patterns/factory_method/{concaptual_platform_dialog => conceptual_platform_dialog}/buttons/windows_button.dart (100%) rename patterns/factory_method/{concaptual_platform_dialog => conceptual_platform_dialog}/dialogs_factory/dialog.dart (100%) rename patterns/factory_method/{concaptual_platform_dialog => conceptual_platform_dialog}/dialogs_factory/html_dialog.dart (100%) rename patterns/factory_method/{concaptual_platform_dialog => conceptual_platform_dialog}/dialogs_factory/windows_dialog.dart (100%) rename patterns/factory_method/{concaptual_platform_dialog => conceptual_platform_dialog}/main.dart (100%) diff --git a/patterns/factory_method/concaptual_platform_dialog/README.md b/patterns/factory_method/conceptual_platform_dialog/README.md similarity index 100% rename from patterns/factory_method/concaptual_platform_dialog/README.md rename to patterns/factory_method/conceptual_platform_dialog/README.md diff --git a/patterns/factory_method/concaptual_platform_dialog/buttons/button.dart b/patterns/factory_method/conceptual_platform_dialog/buttons/button.dart similarity index 100% rename from patterns/factory_method/concaptual_platform_dialog/buttons/button.dart rename to patterns/factory_method/conceptual_platform_dialog/buttons/button.dart diff --git a/patterns/factory_method/concaptual_platform_dialog/buttons/html_button.dart b/patterns/factory_method/conceptual_platform_dialog/buttons/html_button.dart similarity index 100% rename from patterns/factory_method/concaptual_platform_dialog/buttons/html_button.dart rename to patterns/factory_method/conceptual_platform_dialog/buttons/html_button.dart diff --git a/patterns/factory_method/concaptual_platform_dialog/buttons/windows_button.dart b/patterns/factory_method/conceptual_platform_dialog/buttons/windows_button.dart similarity index 100% rename from patterns/factory_method/concaptual_platform_dialog/buttons/windows_button.dart rename to patterns/factory_method/conceptual_platform_dialog/buttons/windows_button.dart diff --git a/patterns/factory_method/concaptual_platform_dialog/dialogs_factory/dialog.dart b/patterns/factory_method/conceptual_platform_dialog/dialogs_factory/dialog.dart similarity index 100% rename from patterns/factory_method/concaptual_platform_dialog/dialogs_factory/dialog.dart rename to patterns/factory_method/conceptual_platform_dialog/dialogs_factory/dialog.dart diff --git a/patterns/factory_method/concaptual_platform_dialog/dialogs_factory/html_dialog.dart b/patterns/factory_method/conceptual_platform_dialog/dialogs_factory/html_dialog.dart similarity index 100% rename from patterns/factory_method/concaptual_platform_dialog/dialogs_factory/html_dialog.dart rename to patterns/factory_method/conceptual_platform_dialog/dialogs_factory/html_dialog.dart diff --git a/patterns/factory_method/concaptual_platform_dialog/dialogs_factory/windows_dialog.dart b/patterns/factory_method/conceptual_platform_dialog/dialogs_factory/windows_dialog.dart similarity index 100% rename from patterns/factory_method/concaptual_platform_dialog/dialogs_factory/windows_dialog.dart rename to patterns/factory_method/conceptual_platform_dialog/dialogs_factory/windows_dialog.dart diff --git a/patterns/factory_method/concaptual_platform_dialog/main.dart b/patterns/factory_method/conceptual_platform_dialog/main.dart similarity index 100% rename from patterns/factory_method/concaptual_platform_dialog/main.dart rename to patterns/factory_method/conceptual_platform_dialog/main.dart From 3ef63dce2b2e83038fca38bc6625ffd22bff2633 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 3 May 2022 22:57:24 +0300 Subject: [PATCH 286/479] Add pattern strategy. OverbookingPolicy example. --- .../application/application.dart | 20 +++++++++++++++++++ .../order_confirmation_sequence.dart | 5 +++++ .../reservation_cargo_spaces/main.dart | 18 +++++++++++++++++ .../partners/cargo.dart | 5 +++++ .../partners/voyage.dart | 16 +++++++++++++++ .../policy/overbooking_policy.dart | 13 ++++++++++++ 6 files changed, 77 insertions(+) create mode 100644 patterns/strategy/reservation_cargo_spaces/application/application.dart create mode 100644 patterns/strategy/reservation_cargo_spaces/application/order_confirmation_sequence.dart create mode 100644 patterns/strategy/reservation_cargo_spaces/main.dart create mode 100644 patterns/strategy/reservation_cargo_spaces/partners/cargo.dart create mode 100644 patterns/strategy/reservation_cargo_spaces/partners/voyage.dart create mode 100644 patterns/strategy/reservation_cargo_spaces/policy/overbooking_policy.dart diff --git a/patterns/strategy/reservation_cargo_spaces/application/application.dart b/patterns/strategy/reservation_cargo_spaces/application/application.dart new file mode 100644 index 0000000..d8aaa8f --- /dev/null +++ b/patterns/strategy/reservation_cargo_spaces/application/application.dart @@ -0,0 +1,20 @@ +import '../partners/cargo.dart'; +import '../partners/voyage.dart'; +import '../policy/overbooking_policy.dart'; +import 'order_confirmation_sequence.dart'; + +class Application { + final OverbookingPolicy overbookingPolicy; + final orderConfirmationSequence = OrderConfirmationSequence(); + + Application(this.overbookingPolicy); + + void makeBooking(Cargo cargo, Voyage voyage) { + if (overbookingPolicy.isAllowed(cargo, voyage)) { + final confirmation = orderConfirmationSequence.next(); + voyage.addCargo(cargo, confirmation); + } else { + throw 'The weight of the cargo exceeds the permissible norm.'; + } + } +} diff --git a/patterns/strategy/reservation_cargo_spaces/application/order_confirmation_sequence.dart b/patterns/strategy/reservation_cargo_spaces/application/order_confirmation_sequence.dart new file mode 100644 index 0000000..906793e --- /dev/null +++ b/patterns/strategy/reservation_cargo_spaces/application/order_confirmation_sequence.dart @@ -0,0 +1,5 @@ +class OrderConfirmationSequence { + var _index = 0; + + int next() => _index++; +} diff --git a/patterns/strategy/reservation_cargo_spaces/main.dart b/patterns/strategy/reservation_cargo_spaces/main.dart new file mode 100644 index 0000000..ddced6e --- /dev/null +++ b/patterns/strategy/reservation_cargo_spaces/main.dart @@ -0,0 +1,18 @@ +import 'application/application.dart'; +import 'partners/cargo.dart'; +import 'partners/voyage.dart'; +import 'policy/overbooking_policy.dart'; + +void main() { + final overbookingPolicy = OverbookingPolicy(); + final app = Application(overbookingPolicy); + final voyage = Voyage(); + + try { + app.makeBooking(Cargo(1000), voyage); + app.makeBooking(Cargo(500), voyage); + app.makeBooking(Cargo(800), voyage); // error + } catch (e) { + print(e); + } +} diff --git a/patterns/strategy/reservation_cargo_spaces/partners/cargo.dart b/patterns/strategy/reservation_cargo_spaces/partners/cargo.dart new file mode 100644 index 0000000..b6b4d23 --- /dev/null +++ b/patterns/strategy/reservation_cargo_spaces/partners/cargo.dart @@ -0,0 +1,5 @@ +class Cargo { + final double size; + + Cargo(this.size); +} diff --git a/patterns/strategy/reservation_cargo_spaces/partners/voyage.dart b/patterns/strategy/reservation_cargo_spaces/partners/voyage.dart new file mode 100644 index 0000000..13f42b7 --- /dev/null +++ b/patterns/strategy/reservation_cargo_spaces/partners/voyage.dart @@ -0,0 +1,16 @@ +import 'cargo.dart'; + +class Voyage { + final _cargo = {}; + + double get capacity => 2000.0; + + double bookedCargoSize() { + return _cargo.values.fold(0, (prev, cargo) => prev + cargo.size); + } + + void addCargo(Cargo cargo, int confirmation) { + _cargo.putIfAbsent(confirmation, () => cargo); + print('Add Cargo(${cargo.size}) to voyage.'); + } +} diff --git a/patterns/strategy/reservation_cargo_spaces/policy/overbooking_policy.dart b/patterns/strategy/reservation_cargo_spaces/policy/overbooking_policy.dart new file mode 100644 index 0000000..8061db5 --- /dev/null +++ b/patterns/strategy/reservation_cargo_spaces/policy/overbooking_policy.dart @@ -0,0 +1,13 @@ +import '../partners/cargo.dart'; +import '../partners/voyage.dart'; + +class OverbookingPolicy { + static const allowableRedundancy = 1.1; + + bool isAllowed(Cargo cargo, Voyage voyage) { + final maxBooking = voyage.capacity * allowableRedundancy; + final futureWeight = voyage.bookedCargoSize() + cargo.size; + + return futureWeight < maxBooking; + } +} From 9b62d27bede84f7f661fe6ff89b05e915b733980 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 3 May 2022 23:27:32 +0300 Subject: [PATCH 287/479] Add README. --- .../reservation_cargo_spaces/README.md | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 patterns/strategy/reservation_cargo_spaces/README.md diff --git a/patterns/strategy/reservation_cargo_spaces/README.md b/patterns/strategy/reservation_cargo_spaces/README.md new file mode 100644 index 0000000..9285ea2 --- /dev/null +++ b/patterns/strategy/reservation_cargo_spaces/README.md @@ -0,0 +1,43 @@ +# Memento pattern +Strategy is a behavioral design pattern that lets you define a family of algorithms, put each of +them into a separate class, and make their objects interchangeable. + +Tutorial: [here](https://refactoring.guru/design-patterns/strategy). + +## About example: Reservation cargo spaces. +This example is taken from the "Blue" book **"Domain-Driven Design" - Eric Evans. Chapter Once**. + +### Diagram: +![image](https://user-images.githubusercontent.com/8049534/166560051-6e392b01-6777-4eb1-ae20-fcd643b248ef.png) + +### Client code: +```dart +void main() { + final overbookingPolicy = OverbookingPolicy(); + final app = Application(overbookingPolicy); + final voyage = Voyage(); + + try { + app.makeBooking(Cargo(1000), voyage); + app.makeBooking(Cargo(500), voyage); + app.makeBooking(Cargo(800), voyage); // error + } catch (e) { + print(e); + } +} + +class Application { + void makeBooking(Cargo cargo, Voyage voyage) { + if (overbookingPolicy.isAllowed(cargo, voyage)) { + voyage.addCargo(cargo, confirmation); + } + } +} +``` + +**Output:** +``` +add Cargo(1000.0) to voyage. +add Cargo(500.0) to voyage. +The weight of the cargo exceeds the permissible norm. +``` From 83387b21032aadd6cc32c7cbb4fad34736385044 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 3 May 2022 23:29:39 +0300 Subject: [PATCH 288/479] Update main README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 014c1ea..e89a712 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ It contains **Dart** examples for all classic **GoF** design patterns. - [ ] **State** - [ ] **Template Method** - [ ] **Visitor** - - [ ] **Strategy** + - [ ] **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)] From 59a6e912d381540abc39f2051aa4917125f05312 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 3 May 2022 23:30:46 +0300 Subject: [PATCH 289/479] Bump version 0.21.0. --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 364f637..9bb36d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.21.0 +- Add strategy pattern: "Reservation cargo spaces". + ## 0.20.0 - Add "Conceptual Dialog Factory" example. diff --git a/pubspec.yaml b/pubspec.yaml index 83d3c93..8dc319c 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.20.0 +version: 0.21.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 e98f6182a8e39c806d1aa8c0e3840a9d1e5bc8e1 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 3 May 2022 23:40:42 +0300 Subject: [PATCH 290/479] Fix strategy pattern name & fix checkbox main README. --- README.md | 2 +- patterns/strategy/reservation_cargo_spaces/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e89a712..2bcd1de 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ It contains **Dart** examples for all classic **GoF** design patterns. - [ ] **State** - [ ] **Template Method** - [ ] **Visitor** - - [ ] **Strategy** [[Reservation cargo spaces](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/strategy/reservation_cargo_spaces)] + - [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/strategy/reservation_cargo_spaces/README.md b/patterns/strategy/reservation_cargo_spaces/README.md index 9285ea2..afeae66 100644 --- a/patterns/strategy/reservation_cargo_spaces/README.md +++ b/patterns/strategy/reservation_cargo_spaces/README.md @@ -1,4 +1,4 @@ -# Memento pattern +# Strategy pattern Strategy is a behavioral design pattern that lets you define a family of algorithms, put each of them into a separate class, and make their objects interchangeable. From 3f7f93cb13aca20eba28283771642eb741eef0c7 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 8 May 2022 18:48:18 +0300 Subject: [PATCH 291/479] Impl visitor classes. --- patterns/visitor/shapes_exporter/main.dart | 28 +++++++ .../shapes_exporter/shapes/circle.dart | 24 ++++++ .../shapes/compound_shape.dart | 24 ++++++ .../visitor/shapes_exporter/shapes/dot.dart | 22 ++++++ .../shapes_exporter/shapes/rectangle.dart | 26 +++++++ .../visitor/shapes_exporter/shapes/shape.dart | 7 ++ .../shapes_exporter/visitor/visitor.dart | 14 ++++ .../visitor/xml_export_visitor.dart | 73 +++++++++++++++++++ 8 files changed, 218 insertions(+) create mode 100644 patterns/visitor/shapes_exporter/main.dart create mode 100644 patterns/visitor/shapes_exporter/shapes/circle.dart create mode 100644 patterns/visitor/shapes_exporter/shapes/compound_shape.dart create mode 100644 patterns/visitor/shapes_exporter/shapes/dot.dart create mode 100644 patterns/visitor/shapes_exporter/shapes/rectangle.dart create mode 100644 patterns/visitor/shapes_exporter/shapes/shape.dart create mode 100644 patterns/visitor/shapes_exporter/visitor/visitor.dart create mode 100644 patterns/visitor/shapes_exporter/visitor/xml_export_visitor.dart 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..74b16ef --- /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.visitCompoundGraphic(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..67d8059 --- /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 visitCompoundGraphic(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..cc14b98 --- /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 visitCompoundGraphic(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++; + } + } +} From 88ae32cb64ccaaa44cbd194113eee217eff096fc Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 8 May 2022 18:54:13 +0300 Subject: [PATCH 292/479] Add README. --- patterns/visitor/shapes_exporter/README.md | 75 ++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 patterns/visitor/shapes_exporter/README.md diff --git a/patterns/visitor/shapes_exporter/README.md b/patterns/visitor/shapes_exporter/README.md new file mode 100644 index 0000000..c48dae4 --- /dev/null +++ b/patterns/visitor/shapes_exporter/README.md @@ -0,0 +1,75 @@ +# 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. +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 + + + 30 + 45 + + + 10 + 10 + 100 + 100 + + + 300 + 20 + 35 + + + 60 + 60 + + + 5 + 5 + + + 15 + 15 + + + 20 + 20 + + + + + +``` From 31e2a063ed85436eb1127883d4d1c0143ce5ad05 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 8 May 2022 18:54:41 +0300 Subject: [PATCH 293/479] Fix xml header. --- .../visitor/shapes_exporter/visitor/xml_export_visitor.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/visitor/shapes_exporter/visitor/xml_export_visitor.dart b/patterns/visitor/shapes_exporter/visitor/xml_export_visitor.dart index cc14b98..4205b6d 100644 --- a/patterns/visitor/shapes_exporter/visitor/xml_export_visitor.dart +++ b/patterns/visitor/shapes_exporter/visitor/xml_export_visitor.dart @@ -9,7 +9,7 @@ class XMLExportVisitor implements Visitor { String export(Shape shape) { buff.clear(); - _write(''); + _write('?'); shape.accept(this); return buff.toString().trim(); } From 58656c3253dcf4789e12d13f94a7a4f6790e2b60 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 8 May 2022 18:56:04 +0300 Subject: [PATCH 294/479] Fix "visitCompoundShape()" method name. --- patterns/visitor/shapes_exporter/shapes/compound_shape.dart | 2 +- patterns/visitor/shapes_exporter/visitor/visitor.dart | 2 +- .../visitor/shapes_exporter/visitor/xml_export_visitor.dart | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/patterns/visitor/shapes_exporter/shapes/compound_shape.dart b/patterns/visitor/shapes_exporter/shapes/compound_shape.dart index 74b16ef..d35338e 100644 --- a/patterns/visitor/shapes_exporter/shapes/compound_shape.dart +++ b/patterns/visitor/shapes_exporter/shapes/compound_shape.dart @@ -14,7 +14,7 @@ class CompoundShape implements Shape { @override void accept(Visitor visitor) { - visitor.visitCompoundGraphic(this); + visitor.visitCompoundShape(this); } @override diff --git a/patterns/visitor/shapes_exporter/visitor/visitor.dart b/patterns/visitor/shapes_exporter/visitor/visitor.dart index 67d8059..3d6ba3b 100644 --- a/patterns/visitor/shapes_exporter/visitor/visitor.dart +++ b/patterns/visitor/shapes_exporter/visitor/visitor.dart @@ -4,7 +4,7 @@ import '../shapes/dot.dart'; import '../shapes/rectangle.dart'; abstract class Visitor { - void visitCompoundGraphic(CompoundShape compound); + void visitCompoundShape(CompoundShape compound); void visitDot(Dot dot); diff --git a/patterns/visitor/shapes_exporter/visitor/xml_export_visitor.dart b/patterns/visitor/shapes_exporter/visitor/xml_export_visitor.dart index 4205b6d..be6acd3 100644 --- a/patterns/visitor/shapes_exporter/visitor/xml_export_visitor.dart +++ b/patterns/visitor/shapes_exporter/visitor/xml_export_visitor.dart @@ -15,7 +15,7 @@ class XMLExportVisitor implements Visitor { } @override - void visitCompoundGraphic(CompoundShape compound) { + void visitCompoundShape(CompoundShape compound) { _write('', openTag: true); _write('${compound.x}'); _write('${compound.y}'); From 213cd66ebd6b319c81c4ff873a5b101879adfc0d Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 8 May 2022 19:01:39 +0300 Subject: [PATCH 295/479] Add: about example. --- patterns/visitor/shapes_exporter/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/patterns/visitor/shapes_exporter/README.md b/patterns/visitor/shapes_exporter/README.md index c48dae4..11ebdc4 100644 --- a/patterns/visitor/shapes_exporter/README.md +++ b/patterns/visitor/shapes_exporter/README.md @@ -4,7 +4,10 @@ they operate. Tutorial: [here](https://refactoring.guru/design-patterns/visitor). -### About example. +### 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: From 3fcf963d518837b45b5ab9b3d64b5d49a28f6616 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 8 May 2022 19:02:24 +0300 Subject: [PATCH 296/479] Update main README. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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)] From 5ffb3879ca8d0a62417e273e72a5366d34e74b14 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 8 May 2022 19:02:34 +0300 Subject: [PATCH 297/479] Bump version 0.22.0. --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) 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/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 From 06ccb2c8ed185e68617b790da3596caa3dffd408 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 9 May 2022 19:45:51 +0300 Subject: [PATCH 298/479] Divide Originator into Recovery-Backup classes. --- .../memento/memento_editor/editor/editor.dart | 3 +- .../memento_pattern/originator.dart | 64 ++++++++++--------- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index 8ab97ac..6455cb7 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -5,7 +5,8 @@ import '../memento_pattern/originator.dart'; import '../shapes/shapes.dart'; import 'manipulator.dart'; -class Editor extends ClassicApp with Manipulator, Shapes, Originator { +class Editor extends ClassicApp + with Manipulator, Shapes, BackupOriginator, RecoveryOriginator { @override void onPaint(Canvas canvas, Size canvasSize) { _paintBackground(canvas, canvasSize); diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index f46a1ae..36ed585 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -6,37 +6,22 @@ import '../shapes/shapes.dart'; import '../shapes/shape.dart'; import 'snapshot.dart'; -mixin Originator implements Shapes { - Snapshot backup() { - final data = _allocateBuffer(); - _writeShapes(data); - _writeSelectedIndex(data); - return _toSnapshot(data); - } +const _shapeByteSize = 16; +const _selectedIndexByteSize = 4; - void restore(Snapshot snapshot) { - final byteData = _fromSnapshotToByteData(snapshot); - final newShapes = _readShapes(byteData); - final selectedIndex = _readSelectedIndex(byteData); - shapes.clear(); - shapes.addAll(newShapes); - selectByIndex(selectedIndex); +mixin BackupOriginator implements Shapes { + Snapshot backup() { + final buffer = _allocateBuffer(); + _writeShapes(buffer); + _writeSelectedIndex(buffer); + return _toSnapshot(buffer); } - static const _shapeByteSize = 16; - static const _selectedIndexByteSize = 4; - ByteData _allocateBuffer() { final byteSize = shapes.length * _shapeByteSize + _selectedIndexByteSize; return ByteData(byteSize); } - ByteData _fromSnapshotToByteData(Snapshot snapshot) { - final unBase = Base64Decoder().convert(snapshot); - final byteData = ByteData.sublistView(unBase); - return byteData; - } - void _writeSelectedIndex(ByteData data) { late final int selectedIndex; @@ -59,12 +44,35 @@ mixin Originator implements Shapes { ..setFloat32(byteOffset + 4, shape.y) ..setInt32(byteOffset + 8, shape.color.value) ..setFloat32(byteOffset + 12, shape.size); - byteOffset += 16; + byteOffset += _shapeByteSize; } return byteOffset; } + Snapshot _toSnapshot(ByteData data) { + return Base64Encoder().convert( + data.buffer.asUint8List(), + ); + } +} + +mixin RecoveryOriginator implements Shapes { + void restore(Snapshot snapshot) { + final byteData = _fromSnapshotToByteData(snapshot); + final newShapes = _readShapes(byteData); + final selectedIndex = _readSelectedIndex(byteData); + shapes.clear(); + shapes.addAll(newShapes); + selectByIndex(selectedIndex); + } + + ByteData _fromSnapshotToByteData(Snapshot snapshot) { + final unBase = Base64Decoder().convert(snapshot); + final byteData = ByteData.sublistView(unBase); + return byteData; + } + int _getNumberOfShapes(ByteData byteData) { return (byteData.lengthInBytes - _selectedIndexByteSize) ~/ _shapeByteSize; } @@ -82,7 +90,7 @@ mixin Originator implements Shapes { byteData.getFloat32(byteOffset + 12), ); shapes.add(shape); - byteOffset += 16; + byteOffset += _shapeByteSize; } return shapes; @@ -91,10 +99,4 @@ mixin Originator implements Shapes { int _readSelectedIndex(ByteData byteData) { return byteData.getInt32(byteData.lengthInBytes - _selectedIndexByteSize); } - - Snapshot _toSnapshot(ByteData data) { - return Base64Encoder().convert( - data.buffer.asUint8List(), - ); - } } From d5c2cfcb56b85eecb21e96392c5081197ed52a05 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 16 May 2022 22:08:17 +0300 Subject: [PATCH 299/479] Impl project "Tool Panel". --- bin/main.dart | 4 +- .../tool_panel_factory/app/app.dart | 21 ++++++ .../tool_panel_factory/app/shapes.dart | 25 +++++++ .../tool_panel_factory/app/tools.dart | 40 +++++++++++ .../factories/circle_factory.dart | 18 +++++ .../factories/line_factory.dart | 18 +++++ .../factories/star_factory.dart | 18 +++++ .../factories/triangle_factory.dart | 18 +++++ .../factories/txt_factory.dart | 19 +++++ .../tool_panel_factory/main.dart | 55 ++++++++++++++ .../mixin/icon_box_mixin.dart | 71 +++++++++++++++++++ .../tool_panel_factory/pattern/shape.dart | 30 ++++++++ .../pattern/tool_factory.dart | 9 +++ .../shapes/circle_shape.dart | 36 ++++++++++ .../tool_panel_factory/shapes/line_shape.dart | 37 ++++++++++ .../tool_panel_factory/shapes/star_shape.dart | 52 ++++++++++++++ .../shapes/triangle_shape.dart | 49 +++++++++++++ .../tool_panel_factory/shapes/txt_shape.dart | 51 +++++++++++++ .../widgets/colors_tool_bar.dart | 35 +++++++++ .../widgets/drawing_board.dart | 39 ++++++++++ .../widgets/factories_tool_bar.dart | 46 ++++++++++++ .../independent/event_listenable_builder.dart | 44 ++++++++++++ .../widgets/independent/hove.dart | 47 ++++++++++++ .../widgets/independent/tool_bar.dart | 62 ++++++++++++++++ .../widgets/independent/tool_button.dart | 40 +++++++++++ .../widgets/shape_widget.dart | 39 ++++++++++ .../widgets/tool_panel.dart | 25 +++++++ 27 files changed, 947 insertions(+), 1 deletion(-) create mode 100644 patterns/abstract_factory/tool_panel_factory/app/app.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/app/shapes.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/app/tools.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/factories/txt_factory.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/main.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/mixin/icon_box_mixin.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/pattern/shape.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/pattern/tool_factory.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/shapes/circle_shape.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/shapes/line_shape.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/shapes/star_shape.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/shapes/triangle_shape.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/shapes/txt_shape.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/colors_tool_bar.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/drawing_board.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/factories_tool_bar.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/independent/event_listenable_builder.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/independent/hove.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/independent/tool_bar.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/independent/tool_button.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/shape_widget.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/tool_panel.dart diff --git a/bin/main.dart b/bin/main.dart index 935d592..1f3c009 100644 --- a/bin/main.dart +++ b/bin/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../patterns/abstract_factory/tool_panel_factory/main.dart'; import '../patterns/observer/subscriber_flutter_widget/main.dart'; import '../patterns/adapter/flutter_adapter/main.dart'; import '../patterns/memento/memento_editor/main.dart'; @@ -13,11 +14,12 @@ class MyApp extends StatelessWidget { return MaterialApp( title: 'Refactoring Guru: Flutter launcher', theme: ThemeData(primarySwatch: Colors.pink), - initialRoute: '/memento/flutter_memento_editor', + initialRoute: '/abstract_factory/tool_panel', routes: { '/observer/subscriber_flutter_widget': (_) => SubscriberFlutterApp(), '/adapter/flutter_adapter': (_) => FlutterAdapterApp(), '/memento/flutter_memento_editor': (_) => FlutterMementoEditorApp(), + '/abstract_factory/tool_panel': (_) => WToolPanelApp(), }, ); } diff --git a/patterns/abstract_factory/tool_panel_factory/app/app.dart b/patterns/abstract_factory/tool_panel_factory/app/app.dart new file mode 100644 index 0000000..5613017 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/app/app.dart @@ -0,0 +1,21 @@ +import '../app/tools.dart'; +import 'shapes.dart'; + +class App { + final Tools tools; + final Shapes shapes; + + App({ + required this.tools, + required this.shapes, + }); + + void addShape(double x, double y) { + final activeColor = tools.activeColor.value; + final activeFactory = tools.activeFactory.value!; + + final newShape = activeFactory.createShape(x, y, activeColor); + newShape.centerToFit(); + shapes.add(newShape); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/app/shapes.dart b/patterns/abstract_factory/tool_panel_factory/app/shapes.dart new file mode 100644 index 0000000..da28a47 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/app/shapes.dart @@ -0,0 +1,25 @@ +import 'dart:collection'; + +import 'package:flutter/foundation.dart'; + +import '../pattern/shape.dart'; + +class Shapes with IterableMixin { + final List _shapes; + + Shapes(this._shapes); + + void add(Shape shape) { + _shapes.add(shape); + onAddShapeEvent._emit(); + } + + @override + Iterator get iterator => _shapes.iterator; + + final onAddShapeEvent = Event(); +} + +class Event extends ChangeNotifier { + void _emit() => notifyListeners(); +} diff --git a/patterns/abstract_factory/tool_panel_factory/app/tools.dart b/patterns/abstract_factory/tool_panel_factory/app/tools.dart new file mode 100644 index 0000000..c0cf9c7 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/app/tools.dart @@ -0,0 +1,40 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; + +import '../mixin/icon_box_mixin.dart'; +import '../pattern/tool_factory.dart'; + +class Tools { + final List factories; + final List colors; + + final activeFactory = ValueNotifier(null); + + final activeColor = ValueNotifier(Color(0x0FFFFFFFF)); + + Future get iconsReady => _iconsInitCompleter.future; + + Tools({required this.factories, required this.colors}) { + _initIconsFromShapes(); + + if (factories.isNotEmpty) { + activeFactory.value = factories.first; + } + + if (colors.isNotEmpty) { + activeColor.value = colors.first; + } + } + + final _iconsInitCompleter = Completer(); + + void _initIconsFromShapes() async { + await Future.wait([ + for (final factory in factories) + (factory as IconBoxMixin).updateIcon(activeColor.value), + ]); + _iconsInitCompleter.complete(Future.value(true)); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart new file mode 100644 index 0000000..529e311 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart @@ -0,0 +1,18 @@ +import 'dart:ui'; + +import '../mixin/icon_box_mixin.dart'; +import '../pattern/tool_factory.dart'; +import '../shapes/circle_shape.dart'; +import '../pattern/shape.dart'; + +class CircleFactory extends FactoryTool with IconBoxMixin { + @override + Shape createShape(double x, double y, Color color) { + return CircleShape( + radius: 50, + x: x, + y: y, + color: color, + ); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart new file mode 100644 index 0000000..aa7e01f --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart @@ -0,0 +1,18 @@ +import 'dart:ui'; + +import '../mixin/icon_box_mixin.dart'; +import '../pattern/tool_factory.dart'; +import '../pattern/shape.dart'; +import '../shapes/line_shape.dart'; + +class LineFactory extends FactoryTool with IconBoxMixin { + @override + Shape createShape(double x, double y, Color color) { + return LineShape( + length: 100, + x: x, + y: y, + color: color, + ); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart new file mode 100644 index 0000000..794bcdd --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart @@ -0,0 +1,18 @@ +import 'dart:ui'; + +import '../mixin/icon_box_mixin.dart'; +import '../pattern/tool_factory.dart'; +import '../pattern/shape.dart'; +import '../shapes/star_shape.dart'; + +class StarFactory extends FactoryTool with IconBoxMixin { + @override + Shape createShape(double x, double y, Color color) { + return StarShape( + radius: 80, + x: x, + y: y, + color: color, + ); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart new file mode 100644 index 0000000..1cac49c --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart @@ -0,0 +1,18 @@ +import 'dart:ui'; + +import '../mixin/icon_box_mixin.dart'; +import '../pattern/tool_factory.dart'; +import '../pattern/shape.dart'; +import '../shapes/triangle_shape.dart'; + +class TriangleFactory extends FactoryTool with IconBoxMixin { + @override + Shape createShape(double x, double y, Color color) { + return TriangleShape( + sideLength: 120, + x: x, + y: y, + color: color, + ); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/factories/txt_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/txt_factory.dart new file mode 100644 index 0000000..011b093 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/factories/txt_factory.dart @@ -0,0 +1,19 @@ +import 'dart:ui'; + +import '../mixin/icon_box_mixin.dart'; +import '../pattern/tool_factory.dart'; +import '../shapes/txt_shape.dart'; +import '../pattern/shape.dart'; + +class TxtFactory extends FactoryTool with IconBoxMixin { + @override + Shape createShape(double x, double y, Color color) { + return Txt( + text: 'Text', + fontSize: 100, + x: x, + y: y, + color: color, + ); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/main.dart b/patterns/abstract_factory/tool_panel_factory/main.dart new file mode 100644 index 0000000..f20c48b --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/main.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; + +import 'app/app.dart'; +import 'app/shapes.dart'; +import 'app/tools.dart'; +import 'factories/circle_factory.dart'; +import 'factories/line_factory.dart'; +import 'factories/star_factory.dart'; +import 'factories/triangle_factory.dart'; +import 'factories/txt_factory.dart'; +import 'widgets/drawing_board.dart'; +import 'widgets/tool_panel.dart'; + +class WToolPanelApp extends StatefulWidget { + const WToolPanelApp({Key? key}) : super(key: key); + + @override + _WToolPanelAppState createState() => _WToolPanelAppState(); +} + +class _WToolPanelAppState extends State { + final app = App( + shapes: Shapes([]), + tools: Tools( + factories: [ + TxtFactory(), + LineFactory(), + CircleFactory(), + TriangleFactory(), + StarFactory(), + ], + colors: [ + Colors.white, + Colors.green, + Colors.blue, + Colors.yellow, + ], + ), + ); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + DrawingBoard( + shapes: app.shapes, + onClick: app.addShape, + ), + ToolPanel( + tools: app.tools, + ), + ], + ); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/mixin/icon_box_mixin.dart b/patterns/abstract_factory/tool_panel_factory/mixin/icon_box_mixin.dart new file mode 100644 index 0000000..31eaa00 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/mixin/icon_box_mixin.dart @@ -0,0 +1,71 @@ +import 'dart:async'; +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +import '../pattern/tool_factory.dart'; +import '../pattern/shape.dart'; + +mixin IconBoxMixin implements FactoryTool { + Image? _icon; + + @override + Image get icon => _icon!; + + Future updateIcon(Color color) async { + final shape = createShape(0, 0, color); + final pngBytes = await _pngImageFromShape(shape); + _icon = Image.memory( + pngBytes, + fit: BoxFit.none, + ); + } + + Future _pngImageFromShape(Shape shape) async { + final iconSize = 32.0; + + final rec = PictureRecorder(); + final can = Canvas( + rec, + Rect.fromLTWH(0, 0, iconSize, iconSize), + ); + + _scaleTo(can, shape, iconSize); + shape.paint(can); + + final image = await rec.endRecording().toImage( + iconSize.toInt(), + iconSize.toInt(), + ); + final bytes = await image.toByteData(format: ImageByteFormat.png); + + if (bytes == null) { + throw 'Bytes is empty.'; + } + + return bytes.buffer.asUint8List(); + } + + void _scaleTo(Canvas can, Shape shape, double iconSize) { + var xMove = 0.0; + var yMove = 0.0; + late double w; + late double h; + + if (shape.width >= shape.height) { + yMove = (shape.width - shape.height); + w = iconSize / shape.width; + h = iconSize / (shape.height + yMove); + yMove /= 2; + } else { + xMove = (shape.height - shape.width); + w = iconSize / (shape.width + xMove); + h = iconSize / shape.height; + xMove /= 2; + } + + can.scale(w, h); + can.translate(xMove, yMove); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/pattern/shape.dart b/patterns/abstract_factory/tool_panel_factory/pattern/shape.dart new file mode 100644 index 0000000..37330c9 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/pattern/shape.dart @@ -0,0 +1,30 @@ +import 'dart:ui'; + +abstract class Shape { + double get x => _x; + + double get y => _y; + + final Color color; + + Shape({ + required double x, + required double y, + required this.color, + }) : _x = x, + _y = y; + + void paint(Canvas can); + + double get width; + + double get height; + + void centerToFit() { + _x -= width / 2; + _y -= height / 2; + } + + double _x; + double _y; +} diff --git a/patterns/abstract_factory/tool_panel_factory/pattern/tool_factory.dart b/patterns/abstract_factory/tool_panel_factory/pattern/tool_factory.dart new file mode 100644 index 0000000..0a02001 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/pattern/tool_factory.dart @@ -0,0 +1,9 @@ +import 'package:flutter/widgets.dart'; + +import 'shape.dart'; + +abstract class FactoryTool { + Shape createShape(double x, double y, Color color); + + Image get icon; +} diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/circle_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/circle_shape.dart new file mode 100644 index 0000000..f6c21b2 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/shapes/circle_shape.dart @@ -0,0 +1,36 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; + +import '../pattern/shape.dart'; + +class CircleShape extends Shape { + final double radius; + + CircleShape({ + required this.radius, + required double x, + required double y, + required Color color, + }) : super( + x: x, + y: y, + color: color, + ); + + @override + void paint(Canvas can) { + final pos = width / 2; + can.drawCircle( + Offset(pos, pos), + radius, + Paint()..color = color, + ); + } + + @override + double get width => radius * 2; + + @override + double get height => width; +} diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/line_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/line_shape.dart new file mode 100644 index 0000000..0e08df5 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/shapes/line_shape.dart @@ -0,0 +1,37 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; + +import '../pattern/shape.dart'; + +class LineShape extends Shape { + final double length; + + LineShape({ + required this.length, + required double x, + required double y, + required Color color, + }) : super( + x: x, + y: y, + color: color, + ); + + @override + void paint(Canvas can) { + can.drawLine( + Offset(0, length), + Offset(length, 0), + Paint() + ..color = color + ..strokeWidth = 3, + ); + } + + @override + double get width => length; + + @override + double get height => length; +} diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/star_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/star_shape.dart new file mode 100644 index 0000000..6c68798 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/shapes/star_shape.dart @@ -0,0 +1,52 @@ +import 'dart:math'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +import '../pattern/shape.dart'; + +class StarShape extends Shape { + final double radius; + + StarShape({ + required this.radius, + required double x, + required double y, + required Color color, + }) : super( + x: x, + y: y, + color: color, + ); + + @override + void paint(Canvas can) { + final path = Path()..addPolygon(_createStar(), true); + can.drawPath(path, Paint()..color = color); + } + + @override + double get width => radius * 2; + + @override + double get height => radius * 2; + + List _createStar() { + const alpha = (2 * pi) / 10; + + final starXY = radius; + + final points = []; + + for (var i = 11; i != 0; i--) { + var r = radius * (i % 2 + 1) / 2; + var omega = alpha * i; + points.add(Offset( + (r * sin(omega)) + starXY, + (r * cos(omega)) + starXY, + )); + } + + return points; + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/triangle_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/triangle_shape.dart new file mode 100644 index 0000000..1d7e373 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/shapes/triangle_shape.dart @@ -0,0 +1,49 @@ +import 'dart:math'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; + +import '../pattern/shape.dart'; + +class TriangleShape extends Shape { + final double sideLength; + + TriangleShape({ + required this.sideLength, + required double x, + required double y, + required Color color, + }) : super( + x: x, + y: y, + color: color, + ); + + @override + void paint(Canvas can) { + final path = Path() + ..addPolygon( + [ + Offset(0, height), + Offset(width / 2, 0), + Offset(width, height), + ], + true, + ); + + can.drawPath( + path, + Paint() + ..strokeJoin = StrokeJoin.round + ..style = PaintingStyle.stroke + ..color = color + ..strokeWidth = 5, + ); + } + + @override + double get width => sideLength; + + @override + double get height => sideLength * sqrt(3) / 2; +} diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/txt_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/txt_shape.dart new file mode 100644 index 0000000..3eaafbc --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/shapes/txt_shape.dart @@ -0,0 +1,51 @@ +import 'dart:ui'; + +import '../pattern/shape.dart'; + +class Txt extends Shape { + final String text; + final double fontSize; + + Txt({ + required this.text, + required this.fontSize, + required double x, + required double y, + required Color color, + }) : super( + x: x, + y: y, + color: color, + ) { + _initTextParagraph(); + } + + @override + void paint(Canvas can) { + can.drawParagraph(_paragraph, Offset.zero); + } + + @override + double get width => _paragraph.maxIntrinsicWidth; + + @override + double get height => _paragraph.height; + + late final Paragraph _paragraph; + + void _initTextParagraph() { + final style = ParagraphStyle( + textDirection: TextDirection.ltr, + ); + final tStyle = TextStyle( + fontFamily: 'Arial', + color: color, + fontSize: fontSize, + ); + _paragraph = (ParagraphBuilder(style) + ..pushStyle(tStyle) + ..addText(text)) + .build(); + _paragraph.layout(ParagraphConstraints(width: double.infinity)); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/colors_tool_bar.dart b/patterns/abstract_factory/tool_panel_factory/widgets/colors_tool_bar.dart new file mode 100644 index 0000000..9b30eac --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/colors_tool_bar.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; + +import '../app/tools.dart'; +import 'independent/tool_bar.dart'; +import 'independent/tool_button.dart'; + +class ColorsToolBar extends StatelessWidget { + final Tools tools; + + const ColorsToolBar({Key? key, required this.tools}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ToolBar( + title: 'colors', + child: ValueListenableBuilder( + valueListenable: tools.activeColor, + builder: (_, activeColor, __) { + return Column( + children: [ + for (final color in tools.colors) + ToolButton( + icon: Icon(Icons.circle, color: color), + active: color == activeColor, + onTap: () { + tools.activeColor.value = color; + }, + ), + ], + ); + }, + ), + ); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/drawing_board.dart b/patterns/abstract_factory/tool_panel_factory/widgets/drawing_board.dart new file mode 100644 index 0000000..3735982 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/drawing_board.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import '../app/shapes.dart'; +import 'independent/event_listenable_builder.dart'; +import 'shape_widget.dart'; + +class DrawingBoard extends StatelessWidget { + final Shapes shapes; + final Function(double x, double y) onClick; + + const DrawingBoard({ + Key? key, + required this.shapes, + required this.onClick, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTapDown: (e) => addShape(e.localPosition), + child: Container( + color: Color(0xff1f1f1f), + child: EventListenableBuilder( + event: shapes.onAddShapeEvent, + builder: (_) { + return Stack( + children: [ + for (final shape in shapes) ShapeWidget(shape: shape), + ], + ); + }, + ), + ), + ); + } + + void addShape(Offset position) { + onClick(position.dx, position.dy); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/factories_tool_bar.dart b/patterns/abstract_factory/tool_panel_factory/widgets/factories_tool_bar.dart new file mode 100644 index 0000000..51514f0 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/factories_tool_bar.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +import '../app/tools.dart'; +import 'independent/tool_bar.dart'; +import 'independent/tool_button.dart'; + +class FactoriesToolBar extends StatelessWidget { + final Tools tools; + + const FactoriesToolBar({Key? key, required this.tools}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ToolBar( + title: 'factories', + child: FutureBuilder( + future: tools.iconsReady, + builder: (_, snapshot) { + return snapshot.hasData + ? _buildToolButtons() + : Padding( + padding: EdgeInsets.all(10), + child: CircularProgressIndicator()); + }, + ), + ); + } + + Widget _buildToolButtons() { + return ValueListenableBuilder( + valueListenable: tools.activeFactory, + builder: (_, activeFactory, __) { + return Column( + children: [ + for (final factory in tools.factories) + ToolButton( + icon: factory.icon, + active: factory == activeFactory, + onTap: () => tools.activeFactory.value = factory, + ), + ], + ); + }, + ); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/independent/event_listenable_builder.dart b/patterns/abstract_factory/tool_panel_factory/widgets/independent/event_listenable_builder.dart new file mode 100644 index 0000000..c1d02d2 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/independent/event_listenable_builder.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; + +class EventListenableBuilder extends StatefulWidget { + final T event; + final Widget Function(BuildContext context) builder; + + const EventListenableBuilder({ + Key? key, + required this.event, + required this.builder, + }) : super(key: key); + + @override + _EventListenableBuilderState createState() => + _EventListenableBuilderState(event); +} + +class _EventListenableBuilderState + extends State> { + final T event; + + _EventListenableBuilderState(this.event); + + @override + void initState() { + event.addListener(_update); + super.initState(); + } + + @override + void dispose() { + event.removeListener(_update); + super.dispose(); + } + + void _update() { + setState(() {}); + } + + @override + Widget build(BuildContext context) { + return widget.builder(context); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/independent/hove.dart b/patterns/abstract_factory/tool_panel_factory/widgets/independent/hove.dart new file mode 100644 index 0000000..b3bb973 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/independent/hove.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; + +class Hover extends StatefulWidget { + final Function(double hoverForce) builder; + + const Hover({Key? key, required this.builder}) : super(key: key); + + @override + _HoverState createState() => _HoverState(); +} + +class _HoverState extends State with SingleTickerProviderStateMixin { + late final AnimationController _animation; + + @override + void initState() { + _animation = AnimationController( + duration: Duration(milliseconds: 200), + value: 0.0, + vsync: this, + )..addListener( + () { + setState(() {}); + }, + ); + super.initState(); + } + + @override + void dispose() { + _animation.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return MouseRegion( + onEnter: (_) { + _animation.forward(from: _animation.value); + }, + onExit: (_) { + _animation.reverse(); + }, + child: widget.builder(_animation.value), + ); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/independent/tool_bar.dart b/patterns/abstract_factory/tool_panel_factory/widgets/independent/tool_bar.dart new file mode 100644 index 0000000..b0aa11a --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/independent/tool_bar.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; + +class ToolBar extends StatelessWidget { + final String title; + final Widget child; + + const ToolBar({ + Key? key, + required this.title, + required this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return _buildNamedPanel( + title: title, + child: child, + ); + } + + Widget _buildNamedPanel({required String title, required Widget child}) { + return Container( + width: 64, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: Color(0xFF3B3B3B), + borderRadius: BorderRadius.all(Radius.circular(3)), + boxShadow: [ + BoxShadow( + color: Color(0x7C000000), + blurRadius: 6, + offset: Offset(2, 2), + ), + ]), + child: Column( + children: [ + _buildTitle(title), + child, + ], + ), + ); + } + + Widget _buildTitle(String title) { + return Container( + height: 20, + color: Colors.white10, + width: double.infinity, + alignment: Alignment.center, + child: Text( + title, + style: TextStyle( + color: Colors.white70, + fontSize: 13, + fontFamily: 'Arial', + decoration: TextDecoration.none, + fontWeight: FontWeight.normal, + ), + ), + ); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/independent/tool_button.dart b/patterns/abstract_factory/tool_panel_factory/widgets/independent/tool_button.dart new file mode 100644 index 0000000..c0a633b --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/independent/tool_button.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; + +import 'hove.dart'; + +class ToolButton extends StatelessWidget { + final Function() onTap; + final bool active; + final Widget icon; + + const ToolButton({ + Key? key, + required this.onTap, + required this.active, + required this.icon, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Hover( + builder: (hoverForce) { + return Container( + width: 64, + height: 64, + color: color(hoverForce), + child: Padding( + padding: EdgeInsets.all(16), + child: icon, + ), + ); + }, + ), + ); + } + + Color color(double hoverForce) => active + ? Color.lerp(Colors.white10, Colors.white12, hoverForce)! + : Color.lerp(Colors.transparent, Colors.white10, hoverForce)!; +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/shape_widget.dart b/patterns/abstract_factory/tool_panel_factory/widgets/shape_widget.dart new file mode 100644 index 0000000..aec0755 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/shape_widget.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import '../pattern/shape.dart'; + +class ShapeWidget extends StatelessWidget { + final Shape shape; + + const ShapeWidget({ + Key? key, + required this.shape, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Positioned( + left: shape.x, + top: shape.y, + child: CustomPaint( + size: Size(shape.width, shape.height), + painter: PaintShape(shape), + ), + ); + } +} + +class PaintShape extends CustomPainter { + final Shape shape; + + PaintShape(this.shape); + + @override + void paint(Canvas canvas, Size _) { + shape.paint(canvas); + } + + @override + bool shouldRepaint(covariant CustomPainter _) { + return false; + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/tool_panel.dart b/patterns/abstract_factory/tool_panel_factory/widgets/tool_panel.dart new file mode 100644 index 0000000..6e0729d --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/tool_panel.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +import '../app/tools.dart'; +import 'colors_tool_bar.dart'; +import 'factories_tool_bar.dart'; + +class ToolPanel extends StatelessWidget { + final Tools tools; + const ToolPanel({Key? key, required this.tools}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Positioned( + left: 12, + top: 12, + child: Column( + children: [ + FactoriesToolBar(tools: tools), + SizedBox(height: 24), + ColorsToolBar(tools: tools), + ], + ), + ); + } +} From 8c334184d73ee01940d4f97779779eed2a351489 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 16 May 2022 22:58:25 +0300 Subject: [PATCH 300/479] Impl project "Tool Panel". --- bin/main.dart | 2 +- .../abstract_factory/tool_panel_factory/app/tools.dart | 4 ++-- .../tool_panel_factory/factories/circle_factory.dart | 2 +- .../tool_panel_factory/factories/line_factory.dart | 2 +- .../tool_panel_factory/factories/star_factory.dart | 2 +- .../tool_panel_factory/factories/triangle_factory.dart | 2 +- .../tool_panel_factory/factories/txt_factory.dart | 4 ++-- patterns/abstract_factory/tool_panel_factory/main.dart | 8 ++++---- .../tool_panel_factory/mixin/icon_box_mixin.dart | 2 +- .../tool_panel_factory/pattern/tool_factory.dart | 2 +- 10 files changed, 15 insertions(+), 15 deletions(-) diff --git a/bin/main.dart b/bin/main.dart index 1f3c009..5c35ea6 100644 --- a/bin/main.dart +++ b/bin/main.dart @@ -19,7 +19,7 @@ class MyApp extends StatelessWidget { '/observer/subscriber_flutter_widget': (_) => SubscriberFlutterApp(), '/adapter/flutter_adapter': (_) => FlutterAdapterApp(), '/memento/flutter_memento_editor': (_) => FlutterMementoEditorApp(), - '/abstract_factory/tool_panel': (_) => WToolPanelApp(), + '/abstract_factory/tool_panel_factory': (_) => ToolPanelFactoryApp(), }, ); } diff --git a/patterns/abstract_factory/tool_panel_factory/app/tools.dart b/patterns/abstract_factory/tool_panel_factory/app/tools.dart index c0cf9c7..2e7c5bc 100644 --- a/patterns/abstract_factory/tool_panel_factory/app/tools.dart +++ b/patterns/abstract_factory/tool_panel_factory/app/tools.dart @@ -7,10 +7,10 @@ import '../mixin/icon_box_mixin.dart'; import '../pattern/tool_factory.dart'; class Tools { - final List factories; + final List factories; final List colors; - final activeFactory = ValueNotifier(null); + final activeFactory = ValueNotifier(null); final activeColor = ValueNotifier(Color(0x0FFFFFFFF)); diff --git a/patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart index 529e311..b924f83 100644 --- a/patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart @@ -5,7 +5,7 @@ import '../pattern/tool_factory.dart'; import '../shapes/circle_shape.dart'; import '../pattern/shape.dart'; -class CircleFactory extends FactoryTool with IconBoxMixin { +class CircleFactory extends ToolFactory with IconBoxMixin { @override Shape createShape(double x, double y, Color color) { return CircleShape( diff --git a/patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart index aa7e01f..6ed1bf1 100644 --- a/patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart @@ -5,7 +5,7 @@ import '../pattern/tool_factory.dart'; import '../pattern/shape.dart'; import '../shapes/line_shape.dart'; -class LineFactory extends FactoryTool with IconBoxMixin { +class LineFactory extends ToolFactory with IconBoxMixin { @override Shape createShape(double x, double y, Color color) { return LineShape( diff --git a/patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart index 794bcdd..a7d2737 100644 --- a/patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart @@ -5,7 +5,7 @@ import '../pattern/tool_factory.dart'; import '../pattern/shape.dart'; import '../shapes/star_shape.dart'; -class StarFactory extends FactoryTool with IconBoxMixin { +class StarFactory extends ToolFactory with IconBoxMixin { @override Shape createShape(double x, double y, Color color) { return StarShape( diff --git a/patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart index 1cac49c..08951a8 100644 --- a/patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart @@ -5,7 +5,7 @@ import '../pattern/tool_factory.dart'; import '../pattern/shape.dart'; import '../shapes/triangle_shape.dart'; -class TriangleFactory extends FactoryTool with IconBoxMixin { +class TriangleFactory extends ToolFactory with IconBoxMixin { @override Shape createShape(double x, double y, Color color) { return TriangleShape( diff --git a/patterns/abstract_factory/tool_panel_factory/factories/txt_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/txt_factory.dart index 011b093..fac6ad8 100644 --- a/patterns/abstract_factory/tool_panel_factory/factories/txt_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/factories/txt_factory.dart @@ -5,12 +5,12 @@ import '../pattern/tool_factory.dart'; import '../shapes/txt_shape.dart'; import '../pattern/shape.dart'; -class TxtFactory extends FactoryTool with IconBoxMixin { +class TxtFactory extends ToolFactory with IconBoxMixin { @override Shape createShape(double x, double y, Color color) { return Txt( text: 'Text', - fontSize: 100, + fontSize: 50, x: x, y: y, color: color, diff --git a/patterns/abstract_factory/tool_panel_factory/main.dart b/patterns/abstract_factory/tool_panel_factory/main.dart index f20c48b..fef07c5 100644 --- a/patterns/abstract_factory/tool_panel_factory/main.dart +++ b/patterns/abstract_factory/tool_panel_factory/main.dart @@ -11,14 +11,14 @@ import 'factories/txt_factory.dart'; import 'widgets/drawing_board.dart'; import 'widgets/tool_panel.dart'; -class WToolPanelApp extends StatefulWidget { - const WToolPanelApp({Key? key}) : super(key: key); +class ToolPanelFactoryApp extends StatefulWidget { + const ToolPanelFactoryApp({Key? key}) : super(key: key); @override - _WToolPanelAppState createState() => _WToolPanelAppState(); + _ToolPanelFactoryAppState createState() => _ToolPanelFactoryAppState(); } -class _WToolPanelAppState extends State { +class _ToolPanelFactoryAppState extends State { final app = App( shapes: Shapes([]), tools: Tools( diff --git a/patterns/abstract_factory/tool_panel_factory/mixin/icon_box_mixin.dart b/patterns/abstract_factory/tool_panel_factory/mixin/icon_box_mixin.dart index 31eaa00..11016a1 100644 --- a/patterns/abstract_factory/tool_panel_factory/mixin/icon_box_mixin.dart +++ b/patterns/abstract_factory/tool_panel_factory/mixin/icon_box_mixin.dart @@ -7,7 +7,7 @@ import 'package:flutter/material.dart'; import '../pattern/tool_factory.dart'; import '../pattern/shape.dart'; -mixin IconBoxMixin implements FactoryTool { +mixin IconBoxMixin implements ToolFactory { Image? _icon; @override diff --git a/patterns/abstract_factory/tool_panel_factory/pattern/tool_factory.dart b/patterns/abstract_factory/tool_panel_factory/pattern/tool_factory.dart index 0a02001..a556a98 100644 --- a/patterns/abstract_factory/tool_panel_factory/pattern/tool_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/pattern/tool_factory.dart @@ -2,7 +2,7 @@ import 'package:flutter/widgets.dart'; import 'shape.dart'; -abstract class FactoryTool { +abstract class ToolFactory { Shape createShape(double x, double y, Color color); Image get icon; From 09a1aa084a0324de260570d2e374473aacf98fc1 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 16 May 2022 22:58:41 +0300 Subject: [PATCH 301/479] Add README. --- .../tool_panel_factory/README.md | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 patterns/abstract_factory/tool_panel_factory/README.md diff --git a/patterns/abstract_factory/tool_panel_factory/README.md b/patterns/abstract_factory/tool_panel_factory/README.md new file mode 100644 index 0000000..36e82d0 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/README.md @@ -0,0 +1,49 @@ +# Abstract Factory Pattern +Abstract Factory is a creational design pattern that lets you produce families of related objects +without specifying their concrete classes. + +Tutorial: [here](https://refactoring.guru/design-patterns/abstract-factory). + +### Online demo: +Click on the picture to see the [demo](https://RefactoringGuru.github.io/design-patterns-dart/#/abstract_factory/tool_panel_factory). + +[![image](https://user-images.githubusercontent.com/8049534/168668992-369a1bab-9f97-4333-a20e-ffd06bf91b54.png)](https://refactoringguru.github.io/design-patterns-dart/#/abstract_factory/tool_panel_factory) + +### Diagram: +![image](https://user-images.githubusercontent.com/8049534/168672053-73ae1c9c-8fad-45ae-9247-429f7b5da565.png) + +### Client code (using the "createShape" method): +```dart +final app = App( + tools: Tools( + factories: [ + TxtFactory(), + LineFactory(), + CircleFactory(), + TriangleFactory(), + StarFactory(), + ], + ), +); + +class App { + void addShape(double x, double y) { + final newShape = activeFactory.createShape(x, y, activeColor); + shapes.add(newShape); + } +} + +mixin IconBoxMixin implements FactoryTool { + Image? _icon; + + @override + Image get icon => _icon!; + + Future updateIcon(Color color) async { + final shape = createShape(0, 0, color); + final pngBytes = await _pngImageFromShape(shape); + _icon = Image.memory(pngBytes); + } +} +``` + From 18b1898b09576fc34f688384100d9206cc473b93 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 16 May 2022 23:01:28 +0300 Subject: [PATCH 302/479] Update main README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 69a5674..5c6a719 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ It contains **Dart** examples for all classic **GoF** design patterns. # Implementation checklist: - [ ] **Creation** - - [x] **Abstract Factory** [[Conceptual Gui Factory](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/abstract_factory/conceptual_gui_factory)] + - [x] **Abstract Factory** [[Conceptual Gui Factory](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/abstract_factory/conceptual_gui_factory)] [[Tool Panel Factory](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/abstract_factory/tool_panel_factory)] - [x] **Builder** - [[Color Text Format](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/builder/color_text_format)] - [x] **Factory Method** [[Conceptual Platform Dialog](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/factory_method/conceptual_platform_dialog)] - [x] **Prototype** - [[Shapes](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/prototype/shapes)] From 8859d33e91cf9ce60823946e0ce8b5994296ace0 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 16 May 2022 23:01:51 +0300 Subject: [PATCH 303/479] Bump version 0.23.0. --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16286c9..cacd0cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.23.0 +- Add "Tool Panel Factory" flutter example + ## 0.22.0 - Add visitor pattern: "Shape Xml Export". diff --git a/pubspec.yaml b/pubspec.yaml index 6ac14f2..95bed6c 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.22.0 +version: 0.23.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 2b13923fd1bf20d0e9163f6c876effade8b4ed4c Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 18 May 2022 17:27:16 +0300 Subject: [PATCH 304/479] Add empty property bar. --- .../factories/circle_factory.dart | 4 ++ .../factories/line_factory.dart | 4 ++ .../factories/star_factory.dart | 4 ++ .../factories/text_factory.dart | 43 +++++++++++++++++++ .../factories/triangle_factory.dart | 4 ++ .../factories/txt_factory.dart | 19 -------- .../tool_panel_factory/main.dart | 4 +- .../tool_panel_factory/pattern/property.dart | 11 +++++ .../pattern/tool_factory.dart | 5 ++- .../{txt_shape.dart => text_shape.dart} | 4 +- .../widgets/independent/panel.dart | 35 +++++++++++++++ .../widgets/independent/tool_bar.dart | 24 ++--------- .../widgets/property_bar.dart | 23 ++++++++++ 13 files changed, 140 insertions(+), 44 deletions(-) create mode 100644 patterns/abstract_factory/tool_panel_factory/factories/text_factory.dart delete mode 100644 patterns/abstract_factory/tool_panel_factory/factories/txt_factory.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/pattern/property.dart rename patterns/abstract_factory/tool_panel_factory/shapes/{txt_shape.dart => text_shape.dart} (95%) create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/independent/panel.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart diff --git a/patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart index b924f83..b22ee80 100644 --- a/patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart @@ -1,6 +1,7 @@ import 'dart:ui'; import '../mixin/icon_box_mixin.dart'; +import '../pattern/property.dart'; import '../pattern/tool_factory.dart'; import '../shapes/circle_shape.dart'; import '../pattern/shape.dart'; @@ -15,4 +16,7 @@ class CircleFactory extends ToolFactory with IconBoxMixin { color: color, ); } + + @override + Iterable get properties => []; } diff --git a/patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart index 6ed1bf1..815834a 100644 --- a/patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart @@ -1,6 +1,7 @@ import 'dart:ui'; import '../mixin/icon_box_mixin.dart'; +import '../pattern/property.dart'; import '../pattern/tool_factory.dart'; import '../pattern/shape.dart'; import '../shapes/line_shape.dart'; @@ -15,4 +16,7 @@ class LineFactory extends ToolFactory with IconBoxMixin { color: color, ); } + + @override + Iterable get properties => []; } diff --git a/patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart index a7d2737..d1f4e96 100644 --- a/patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart @@ -1,6 +1,7 @@ import 'dart:ui'; import '../mixin/icon_box_mixin.dart'; +import '../pattern/property.dart'; import '../pattern/tool_factory.dart'; import '../pattern/shape.dart'; import '../shapes/star_shape.dart'; @@ -15,4 +16,7 @@ class StarFactory extends ToolFactory with IconBoxMixin { color: color, ); } + + @override + Iterable get properties => []; } diff --git a/patterns/abstract_factory/tool_panel_factory/factories/text_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/text_factory.dart new file mode 100644 index 0000000..4831554 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/factories/text_factory.dart @@ -0,0 +1,43 @@ +import 'dart:ui'; + +import '../mixin/icon_box_mixin.dart'; +import '../pattern/property.dart'; +import '../pattern/tool_factory.dart'; +import '../shapes/text_shape.dart'; +import '../pattern/shape.dart'; + +class TextFactory extends ToolFactory with IconBoxMixin { + var _text = 'Text'; + var _fontSize = 50.0; + + @override + Shape createShape(double x, double y, Color color) { + return TextShape( + text: _text, + fontSize: _fontSize, + x: x, + y: y, + color: color, + ); + } + + @override + Iterable get properties { + return [ + Property( + name: 'text', + value: () => _text, + onChange: (value) { + _text = value; + }, + ), + Property( + name: 'fontSize', + value: () => _fontSize, + onChange: (value) { + _fontSize = value; + }, + ), + ]; + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart index 08951a8..84bc62e 100644 --- a/patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart @@ -1,6 +1,7 @@ import 'dart:ui'; import '../mixin/icon_box_mixin.dart'; +import '../pattern/property.dart'; import '../pattern/tool_factory.dart'; import '../pattern/shape.dart'; import '../shapes/triangle_shape.dart'; @@ -15,4 +16,7 @@ class TriangleFactory extends ToolFactory with IconBoxMixin { color: color, ); } + + @override + Iterable get properties => []; } diff --git a/patterns/abstract_factory/tool_panel_factory/factories/txt_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/txt_factory.dart deleted file mode 100644 index fac6ad8..0000000 --- a/patterns/abstract_factory/tool_panel_factory/factories/txt_factory.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'dart:ui'; - -import '../mixin/icon_box_mixin.dart'; -import '../pattern/tool_factory.dart'; -import '../shapes/txt_shape.dart'; -import '../pattern/shape.dart'; - -class TxtFactory extends ToolFactory with IconBoxMixin { - @override - Shape createShape(double x, double y, Color color) { - return Txt( - text: 'Text', - fontSize: 50, - x: x, - y: y, - color: color, - ); - } -} diff --git a/patterns/abstract_factory/tool_panel_factory/main.dart b/patterns/abstract_factory/tool_panel_factory/main.dart index fef07c5..8b42d7a 100644 --- a/patterns/abstract_factory/tool_panel_factory/main.dart +++ b/patterns/abstract_factory/tool_panel_factory/main.dart @@ -7,7 +7,7 @@ import 'factories/circle_factory.dart'; import 'factories/line_factory.dart'; import 'factories/star_factory.dart'; import 'factories/triangle_factory.dart'; -import 'factories/txt_factory.dart'; +import 'factories/text_factory.dart'; import 'widgets/drawing_board.dart'; import 'widgets/tool_panel.dart'; @@ -23,7 +23,7 @@ class _ToolPanelFactoryAppState extends State { shapes: Shapes([]), tools: Tools( factories: [ - TxtFactory(), + TextFactory(), LineFactory(), CircleFactory(), TriangleFactory(), diff --git a/patterns/abstract_factory/tool_panel_factory/pattern/property.dart b/patterns/abstract_factory/tool_panel_factory/pattern/property.dart new file mode 100644 index 0000000..640f5b9 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/pattern/property.dart @@ -0,0 +1,11 @@ +class Property { + final String name; + final T Function() value; + final void Function(T) onChange; + + Property({ + required this.name, + required this.value, + required this.onChange, + }); +} diff --git a/patterns/abstract_factory/tool_panel_factory/pattern/tool_factory.dart b/patterns/abstract_factory/tool_panel_factory/pattern/tool_factory.dart index a556a98..60323b6 100644 --- a/patterns/abstract_factory/tool_panel_factory/pattern/tool_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/pattern/tool_factory.dart @@ -1,9 +1,12 @@ import 'package:flutter/widgets.dart'; +import 'property.dart'; import 'shape.dart'; abstract class ToolFactory { + Image get icon; + Shape createShape(double x, double y, Color color); - Image get icon; + Iterable get properties; } diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/txt_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/text_shape.dart similarity index 95% rename from patterns/abstract_factory/tool_panel_factory/shapes/txt_shape.dart rename to patterns/abstract_factory/tool_panel_factory/shapes/text_shape.dart index 3eaafbc..00bcbd0 100644 --- a/patterns/abstract_factory/tool_panel_factory/shapes/txt_shape.dart +++ b/patterns/abstract_factory/tool_panel_factory/shapes/text_shape.dart @@ -2,11 +2,11 @@ import 'dart:ui'; import '../pattern/shape.dart'; -class Txt extends Shape { +class TextShape extends Shape { final String text; final double fontSize; - Txt({ + TextShape({ required this.text, required this.fontSize, required double x, diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/independent/panel.dart b/patterns/abstract_factory/tool_panel_factory/widgets/independent/panel.dart new file mode 100644 index 0000000..289ef8c --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/independent/panel.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; + +class Panel extends StatelessWidget { + static const thickness = 64.0; + + final Axis direction; + final Widget child; + + const Panel({ + Key? key, + required this.direction, + required this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + height: direction == Axis.horizontal ? thickness : null, + width: direction == Axis.vertical ? thickness : null, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: Color(0xFF3B3B3B), + borderRadius: BorderRadius.all(Radius.circular(3)), + boxShadow: [ + BoxShadow( + color: Color(0x7C000000), + blurRadius: 6, + offset: Offset(2, 2), + ), + ], + ), + child: child, + ); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/independent/tool_bar.dart b/patterns/abstract_factory/tool_panel_factory/widgets/independent/tool_bar.dart index b0aa11a..ec554b2 100644 --- a/patterns/abstract_factory/tool_panel_factory/widgets/independent/tool_bar.dart +++ b/patterns/abstract_factory/tool_panel_factory/widgets/independent/tool_bar.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'panel.dart'; + class ToolBar extends StatelessWidget { final String title; final Widget child; @@ -12,26 +14,8 @@ class ToolBar extends StatelessWidget { @override Widget build(BuildContext context) { - return _buildNamedPanel( - title: title, - child: child, - ); - } - - Widget _buildNamedPanel({required String title, required Widget child}) { - return Container( - width: 64, - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration( - color: Color(0xFF3B3B3B), - borderRadius: BorderRadius.all(Radius.circular(3)), - boxShadow: [ - BoxShadow( - color: Color(0x7C000000), - blurRadius: 6, - offset: Offset(2, 2), - ), - ]), + return Panel( + direction: Axis.vertical, child: Column( children: [ _buildTitle(title), diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart new file mode 100644 index 0000000..d8fa82a --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +import 'independent/panel.dart'; + +class PropertyBar extends StatelessWidget { + const PropertyBar({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Positioned( + top: 12, + left: 200, + child: Panel( + direction: Axis.horizontal, + child: Row( + children: [ + Text('Hello'), + ], + ), + ), + ); + } +} From 1922eb6fd3bb549f43c55bd807ca542152a8e337 Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 18 May 2022 17:48:26 +0300 Subject: [PATCH 305/479] Fix default root project. --- bin/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/main.dart b/bin/main.dart index 5c35ea6..1180c2d 100644 --- a/bin/main.dart +++ b/bin/main.dart @@ -14,7 +14,7 @@ class MyApp extends StatelessWidget { return MaterialApp( title: 'Refactoring Guru: Flutter launcher', theme: ThemeData(primarySwatch: Colors.pink), - initialRoute: '/abstract_factory/tool_panel', + initialRoute: '/abstract_factory/tool_panel_factory', routes: { '/observer/subscriber_flutter_widget': (_) => SubscriberFlutterApp(), '/adapter/flutter_adapter': (_) => FlutterAdapterApp(), From ebdb8c3237f1805ca5f620dea221f3280a508e6c Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 18 May 2022 17:49:28 +0300 Subject: [PATCH 306/479] Add property bar listenable. --- .../tool_panel_factory/app/app.dart | 2 +- .../tool_panel_factory/app/tools.dart | 18 +-- .../tool_panel_factory/main.dart | 4 + .../widgets/independent/panel.dart | 7 +- .../widgets/property_bar.dart | 148 ++++++++++++++++-- 5 files changed, 155 insertions(+), 24 deletions(-) diff --git a/patterns/abstract_factory/tool_panel_factory/app/app.dart b/patterns/abstract_factory/tool_panel_factory/app/app.dart index 5613017..062ecb7 100644 --- a/patterns/abstract_factory/tool_panel_factory/app/app.dart +++ b/patterns/abstract_factory/tool_panel_factory/app/app.dart @@ -12,7 +12,7 @@ class App { void addShape(double x, double y) { final activeColor = tools.activeColor.value; - final activeFactory = tools.activeFactory.value!; + final activeFactory = tools.activeFactory.value; final newShape = activeFactory.createShape(x, y, activeColor); newShape.centerToFit(); diff --git a/patterns/abstract_factory/tool_panel_factory/app/tools.dart b/patterns/abstract_factory/tool_panel_factory/app/tools.dart index 2e7c5bc..60708d2 100644 --- a/patterns/abstract_factory/tool_panel_factory/app/tools.dart +++ b/patterns/abstract_factory/tool_panel_factory/app/tools.dart @@ -10,22 +10,18 @@ class Tools { final List factories; final List colors; - final activeFactory = ValueNotifier(null); + late final ValueNotifier activeFactory; - final activeColor = ValueNotifier(Color(0x0FFFFFFFF)); + late final ValueNotifier activeColor; Future get iconsReady => _iconsInitCompleter.future; - Tools({required this.factories, required this.colors}) { + Tools({required this.factories, required this.colors}) + : assert(factories.isNotEmpty), + assert(colors.isNotEmpty) { + activeFactory = ValueNotifier(factories.first); + activeColor = ValueNotifier(colors.first); _initIconsFromShapes(); - - if (factories.isNotEmpty) { - activeFactory.value = factories.first; - } - - if (colors.isNotEmpty) { - activeColor.value = colors.first; - } } final _iconsInitCompleter = Completer(); diff --git a/patterns/abstract_factory/tool_panel_factory/main.dart b/patterns/abstract_factory/tool_panel_factory/main.dart index 8b42d7a..dcab2e7 100644 --- a/patterns/abstract_factory/tool_panel_factory/main.dart +++ b/patterns/abstract_factory/tool_panel_factory/main.dart @@ -9,6 +9,7 @@ import 'factories/star_factory.dart'; import 'factories/triangle_factory.dart'; import 'factories/text_factory.dart'; import 'widgets/drawing_board.dart'; +import 'widgets/property_bar.dart'; import 'widgets/tool_panel.dart'; class ToolPanelFactoryApp extends StatefulWidget { @@ -49,6 +50,9 @@ class _ToolPanelFactoryAppState extends State { ToolPanel( tools: app.tools, ), + PropertyPanel( + tools: app.tools, + ), ], ); } diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/independent/panel.dart b/patterns/abstract_factory/tool_panel_factory/widgets/independent/panel.dart index 289ef8c..06343f5 100644 --- a/patterns/abstract_factory/tool_panel_factory/widgets/independent/panel.dart +++ b/patterns/abstract_factory/tool_panel_factory/widgets/independent/panel.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; class Panel extends StatelessWidget { - static const thickness = 64.0; + static const thicknessWidth = 64.0; + static const thicknessHeight = 48.0; final Axis direction; final Widget child; @@ -15,8 +16,8 @@ class Panel extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - height: direction == Axis.horizontal ? thickness : null, - width: direction == Axis.vertical ? thickness : null, + height: direction == Axis.horizontal ? thicknessHeight : null, + width: direction == Axis.vertical ? thicknessWidth : null, clipBehavior: Clip.antiAlias, decoration: BoxDecoration( color: Color(0xFF3B3B3B), diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart index d8fa82a..51d68f3 100644 --- a/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart @@ -1,23 +1,153 @@ import 'package:flutter/material.dart'; +import '../app/tools.dart'; +import '../pattern/property.dart'; +import '../pattern/tool_factory.dart'; import 'independent/panel.dart'; -class PropertyBar extends StatelessWidget { - const PropertyBar({Key? key}) : super(key: key); +class PropertyPanel extends StatelessWidget { + final Tools tools; + + const PropertyPanel({ + Key? key, + required this.tools, + }) : super(key: key); @override Widget build(BuildContext context) { return Positioned( top: 12, - left: 200, - child: Panel( - direction: Axis.horizontal, - child: Row( - children: [ - Text('Hello'), - ], + left: 12 + 64 + 8 + 12, + child: _buildTheme( + child: Panel( + direction: Axis.horizontal, + child: ValueListenableBuilder( + valueListenable: tools.activeFactory, + builder: (_, activeFactory, __) { + return Row( + children: [ + for (final prop in activeFactory.properties) + _buildProperty(prop), + ], + ); + }, + ), + ), + ), + ); + } + + Widget _buildTheme({required Widget child}) { + return Material( + color: Colors.transparent, + textStyle: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w600, + ), + child: child); + } + + Widget _buildProperty(Property prop) { + switch (prop.value().runtimeType) { + case String: + return StringPropertyWidget(property: prop); + case double: + return DoublePropertyWidget(property: prop); + } + + throw 'Type(${prop.value().runtimeType}) of property is not support'; + } +} + +class _FieldLabel extends StatelessWidget { + final String text; + final Widget child; + + const _FieldLabel({ + Key? key, + required this.text, + required this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + SizedBox(width: 10), + Text(text + ':'), + SizedBox(width: 10), + child, + SizedBox(width: 20), + ], + ); + } +} + +class StringPropertyWidget extends StatelessWidget { + final Property property; + + const StringPropertyWidget({Key? key, required this.property}) : super(key: key); + + @override + Widget build(BuildContext context) { + return _FieldLabel( + text: property.name, + child: Container( + color: Colors.white12, + width: 120, + height: 32, + child: TextFormField( + style: TextStyle(fontSize: 18, color: Colors.white70), + decoration: InputDecoration( + contentPadding: const EdgeInsets.only(left: 5.0, bottom: 14.0), + border: UnderlineInputBorder(), + ), + initialValue: property.value(), + onChanged: property.onChange, ), ), ); } } + + +class DoublePropertyWidget extends StatefulWidget { + final Property property; + + const DoublePropertyWidget({Key? key, required this.property}) + : super(key: key); + + @override + _DoublePropertyWidgetState createState() => _DoublePropertyWidgetState(); +} + +class _DoublePropertyWidgetState extends State { + @override + Widget build(BuildContext context) { + return _FieldLabel( + text: widget.property.name, + child: Row( + children: [ + Slider( + min: 4, + max: 150, + value: widget.property.value() as double, + onChanged: (val) { + setState(() { + widget.property.onChange(val); + }); + }, + ), + SizedBox( + width: 32, + child: Text( + (widget.property.value() as double).toStringAsFixed(0), + textAlign: TextAlign.right, + ), + ) + ], + ), + ); + } +} From 709f58820f5c6739ea1f5a75d2dedaca57eed6d2 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 19 May 2022 13:51:39 +0300 Subject: [PATCH 307/479] Add new properties. --- .../factories/circle_factory.dart | 25 +++- .../factories/line_factory.dart | 25 +++- .../factories/star_factory.dart | 25 +++- .../factories/triangle_factory.dart | 23 ++- .../tool_panel_factory/main.dart | 2 +- .../tool_panel_factory/pattern/shape.dart | 5 + .../shapes/circle_shape.dart | 8 +- .../tool_panel_factory/shapes/line_shape.dart | 22 ++- .../tool_panel_factory/shapes/star_shape.dart | 16 +- .../shapes/triangle_shape.dart | 29 ++-- .../widgets/property_bar.dart | 137 ++++++++++++------ 11 files changed, 232 insertions(+), 85 deletions(-) diff --git a/patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart index b22ee80..18f7b0c 100644 --- a/patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart @@ -7,10 +7,14 @@ import '../shapes/circle_shape.dart'; import '../pattern/shape.dart'; class CircleFactory extends ToolFactory with IconBoxMixin { + var _radius = 50.0; + var _isFilled = false; + @override Shape createShape(double x, double y, Color color) { return CircleShape( - radius: 50, + radius: _radius, + isFilled: _isFilled, x: x, y: y, color: color, @@ -18,5 +22,22 @@ class CircleFactory extends ToolFactory with IconBoxMixin { } @override - Iterable get properties => []; + Iterable get properties { + return [ + Property( + name: 'radius', + value: () => _radius, + onChange: (val) { + _radius = val; + }, + ), + Property( + name: 'filled', + value: () => _isFilled, + onChange: (val) { + _isFilled = val; + }, + ), + ]; + } } diff --git a/patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart index 815834a..5b60be1 100644 --- a/patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart @@ -7,10 +7,14 @@ import '../pattern/shape.dart'; import '../shapes/line_shape.dart'; class LineFactory extends ToolFactory with IconBoxMixin { + var _isMirror = true; + var _length = 100.0; + @override Shape createShape(double x, double y, Color color) { return LineShape( - length: 100, + length: _length, + isMirror: _isMirror, x: x, y: y, color: color, @@ -18,5 +22,22 @@ class LineFactory extends ToolFactory with IconBoxMixin { } @override - Iterable get properties => []; + Iterable get properties { + return [ + Property( + name: 'mirror', + value: () => _isMirror, + onChange: (val) { + _isMirror = val; + }, + ), + Property( + name: 'length', + value: () => _length, + onChange: (val) { + _length = val; + }, + ), + ]; + } } diff --git a/patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart index d1f4e96..cd77849 100644 --- a/patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart @@ -7,10 +7,14 @@ import '../pattern/shape.dart'; import '../shapes/star_shape.dart'; class StarFactory extends ToolFactory with IconBoxMixin { + var _radius = 80.0; + var _isFilled = false; + @override Shape createShape(double x, double y, Color color) { return StarShape( - radius: 80, + radius: _radius, + isFilled: _isFilled, x: x, y: y, color: color, @@ -18,5 +22,22 @@ class StarFactory extends ToolFactory with IconBoxMixin { } @override - Iterable get properties => []; + Iterable get properties { + return [ + Property( + name: 'radius', + value: () => _radius, + onChange: (val) { + _radius = val; + }, + ), + Property( + name: 'filled', + value: () => _isFilled, + onChange: (val) { + _isFilled = val; + }, + ), + ]; + } } diff --git a/patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart b/patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart index 84bc62e..9e8b477 100644 --- a/patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart @@ -7,10 +7,14 @@ import '../pattern/shape.dart'; import '../shapes/triangle_shape.dart'; class TriangleFactory extends ToolFactory with IconBoxMixin { + var _isFilled = false; + var _sideLength = 120.0; + @override Shape createShape(double x, double y, Color color) { return TriangleShape( - sideLength: 120, + sideLength: _sideLength, + isFilled: _isFilled, x: x, y: y, color: color, @@ -18,5 +22,20 @@ class TriangleFactory extends ToolFactory with IconBoxMixin { } @override - Iterable get properties => []; + Iterable get properties => [ + Property( + name: 'sideLength', + value: () => _sideLength, + onChange: (val) { + _sideLength = val; + }, + ), + Property( + name: 'filled', + value: () => _isFilled, + onChange: (val) { + _isFilled = val; + }, + ), + ]; } diff --git a/patterns/abstract_factory/tool_panel_factory/main.dart b/patterns/abstract_factory/tool_panel_factory/main.dart index dcab2e7..15496e0 100644 --- a/patterns/abstract_factory/tool_panel_factory/main.dart +++ b/patterns/abstract_factory/tool_panel_factory/main.dart @@ -24,11 +24,11 @@ class _ToolPanelFactoryAppState extends State { shapes: Shapes([]), tools: Tools( factories: [ - TextFactory(), LineFactory(), CircleFactory(), TriangleFactory(), StarFactory(), + TextFactory(), ], colors: [ Colors.white, diff --git a/patterns/abstract_factory/tool_panel_factory/pattern/shape.dart b/patterns/abstract_factory/tool_panel_factory/pattern/shape.dart index 37330c9..ee5c4b2 100644 --- a/patterns/abstract_factory/tool_panel_factory/pattern/shape.dart +++ b/patterns/abstract_factory/tool_panel_factory/pattern/shape.dart @@ -25,6 +25,11 @@ abstract class Shape { _y -= height / 2; } + void translate(double x, double y) { + _x += x; + _y += y; + } + double _x; double _y; } diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/circle_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/circle_shape.dart index f6c21b2..a4be61e 100644 --- a/patterns/abstract_factory/tool_panel_factory/shapes/circle_shape.dart +++ b/patterns/abstract_factory/tool_panel_factory/shapes/circle_shape.dart @@ -6,9 +6,11 @@ import '../pattern/shape.dart'; class CircleShape extends Shape { final double radius; + final bool isFilled; CircleShape({ required this.radius, + required this.isFilled, required double x, required double y, required Color color, @@ -23,8 +25,10 @@ class CircleShape extends Shape { final pos = width / 2; can.drawCircle( Offset(pos, pos), - radius, - Paint()..color = color, + radius - 2, + Paint() + ..style = isFilled ? PaintingStyle.fill : PaintingStyle.stroke + ..color = color, ); } diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/line_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/line_shape.dart index 0e08df5..7b0acd1 100644 --- a/patterns/abstract_factory/tool_panel_factory/shapes/line_shape.dart +++ b/patterns/abstract_factory/tool_panel_factory/shapes/line_shape.dart @@ -8,6 +8,7 @@ class LineShape extends Shape { final double length; LineShape({ + required bool isMirror, required this.length, required double x, required double y, @@ -16,17 +17,22 @@ class LineShape extends Shape { x: x, y: y, color: color, - ); + ) { + if (isMirror) { + point1 = Offset(0, length); + point2 = Offset(length, 0); + } else { + point1 = Offset(0, 0); + point2 = Offset(length, length); + } + } + + late final Offset point1; + late final Offset point2; @override void paint(Canvas can) { - can.drawLine( - Offset(0, length), - Offset(length, 0), - Paint() - ..color = color - ..strokeWidth = 3, - ); + can.drawLine(point1, point2, Paint()..color = color); } @override diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/star_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/star_shape.dart index 6c68798..2d27299 100644 --- a/patterns/abstract_factory/tool_panel_factory/shapes/star_shape.dart +++ b/patterns/abstract_factory/tool_panel_factory/shapes/star_shape.dart @@ -7,9 +7,11 @@ import '../pattern/shape.dart'; class StarShape extends Shape { final double radius; + final bool isFilled; StarShape({ required this.radius, + required this.isFilled, required double x, required double y, required Color color, @@ -17,12 +19,20 @@ class StarShape extends Shape { x: x, y: y, color: color, - ); + ) { + _starPath = Path()..addPolygon(_createStar(), true); + } + + late final Path _starPath; @override void paint(Canvas can) { - final path = Path()..addPolygon(_createStar(), true); - can.drawPath(path, Paint()..color = color); + can.drawPath( + _starPath, + Paint() + ..style = isFilled ? PaintingStyle.fill : PaintingStyle.stroke + ..color = color, + ); } @override diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/triangle_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/triangle_shape.dart index 1d7e373..4c0fe17 100644 --- a/patterns/abstract_factory/tool_panel_factory/shapes/triangle_shape.dart +++ b/patterns/abstract_factory/tool_panel_factory/shapes/triangle_shape.dart @@ -7,21 +7,16 @@ import '../pattern/shape.dart'; class TriangleShape extends Shape { final double sideLength; + final bool isFilled; TriangleShape({ required this.sideLength, + required this.isFilled, required double x, required double y, required Color color, - }) : super( - x: x, - y: y, - color: color, - ); - - @override - void paint(Canvas can) { - final path = Path() + }) : super(x: x, y: y, color: color) { + _trianglePath = Path() ..addPolygon( [ Offset(0, height), @@ -30,15 +25,17 @@ class TriangleShape extends Shape { ], true, ); + } + late final Path _trianglePath; + + @override + void paint(Canvas can) { can.drawPath( - path, - Paint() - ..strokeJoin = StrokeJoin.round - ..style = PaintingStyle.stroke - ..color = color - ..strokeWidth = 5, - ); + _trianglePath, + Paint() + ..style = isFilled ? PaintingStyle.fill : PaintingStyle.stroke + ..color = color); } @override diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart index 51d68f3..bd53b5f 100644 --- a/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart @@ -39,55 +39,41 @@ class PropertyPanel extends StatelessWidget { Widget _buildTheme({required Widget child}) { return Material( - color: Colors.transparent, - textStyle: TextStyle( - color: Colors.white, - fontSize: 18, - fontWeight: FontWeight.w600, - ), - child: child); + color: Colors.transparent, + textStyle: TextStyle( + color: Colors.white, + fontSize: 16, + fontFamily: 'Arial', + ), + child: Theme( + data: ThemeData( + primarySwatch: Colors.pink, + unselectedWidgetColor: Colors.grey, // Your color + ), + child: child), + ); } Widget _buildProperty(Property prop) { - switch (prop.value().runtimeType) { - case String: - return StringPropertyWidget(property: prop); - case double: - return DoublePropertyWidget(property: prop); + final val = prop.value(); + + if (val is String) { + return StringPropertyWidget(property: prop); + } else if (val is double) { + return DoublePropertyWidget(property: prop); + } else if (val is bool) { + return BoolPropertyWidget(property: prop); } throw 'Type(${prop.value().runtimeType}) of property is not support'; } } -class _FieldLabel extends StatelessWidget { - final String text; - final Widget child; - - const _FieldLabel({ - Key? key, - required this.text, - required this.child, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - SizedBox(width: 10), - Text(text + ':'), - SizedBox(width: 10), - child, - SizedBox(width: 20), - ], - ); - } -} - class StringPropertyWidget extends StatelessWidget { final Property property; - const StringPropertyWidget({Key? key, required this.property}) : super(key: key); + const StringPropertyWidget({Key? key, required this.property}) + : super(key: key); @override Widget build(BuildContext context) { @@ -111,7 +97,6 @@ class StringPropertyWidget extends StatelessWidget { } } - class DoublePropertyWidget extends StatefulWidget { final Property property; @@ -129,15 +114,18 @@ class _DoublePropertyWidgetState extends State { text: widget.property.name, child: Row( children: [ - Slider( - min: 4, - max: 150, - value: widget.property.value() as double, - onChanged: (val) { - setState(() { - widget.property.onChange(val); - }); - }, + SizedBox( + width: 200, + child: Slider.adaptive( + min: 0.0, + max: 300, + value: widget.property.value() as double, + onChanged: (val) { + setState(() { + widget.property.onChange(val); + }); + }, + ), ), SizedBox( width: 32, @@ -151,3 +139,58 @@ class _DoublePropertyWidgetState extends State { ); } } + +class BoolPropertyWidget extends StatefulWidget { + final Property property; + + const BoolPropertyWidget({Key? key, required this.property}) + : super(key: key); + + @override + _BoolPropertyWidgetState createState() => _BoolPropertyWidgetState(); +} + +class _BoolPropertyWidgetState extends State { + @override + Widget build(BuildContext context) { + return _FieldLabel( + text: widget.property.name, + child: Row( + children: [ + Checkbox( + value: widget.property.value() as bool, + onChanged: (val) { + setState(() { + widget.property.onChange(val); + }); + }, + ) + ], + ), + ); + } +} + +class _FieldLabel extends StatelessWidget { + final String text; + final Widget child; + + const _FieldLabel({ + Key? key, + required this.text, + required this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + SizedBox(width: 10), + Text(text + ':'), + SizedBox(width: 10), + child, + SizedBox(width: 20), + ], + ); + } +} From 506b40c070aef15e88734170b545cd2816d729cd Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 19 May 2022 14:28:30 +0300 Subject: [PATCH 308/479] Create BaseShape. --- .../tool_panel_factory/pattern/shape.dart | 28 ++++------------- .../tool_panel_factory/shapes/base_shape.dart | 30 +++++++++++++++++++ .../shapes/circle_shape.dart | 8 ++--- .../tool_panel_factory/shapes/line_shape.dart | 6 ++-- .../tool_panel_factory/shapes/star_shape.dart | 4 +-- .../tool_panel_factory/shapes/text_shape.dart | 4 +-- .../shapes/triangle_shape.dart | 6 ++-- 7 files changed, 48 insertions(+), 38 deletions(-) create mode 100644 patterns/abstract_factory/tool_panel_factory/shapes/base_shape.dart diff --git a/patterns/abstract_factory/tool_panel_factory/pattern/shape.dart b/patterns/abstract_factory/tool_panel_factory/pattern/shape.dart index ee5c4b2..6f68e63 100644 --- a/patterns/abstract_factory/tool_panel_factory/pattern/shape.dart +++ b/patterns/abstract_factory/tool_panel_factory/pattern/shape.dart @@ -1,35 +1,17 @@ import 'dart:ui'; abstract class Shape { - double get x => _x; + double get x; - double get y => _y; - - final Color color; - - Shape({ - required double x, - required double y, - required this.color, - }) : _x = x, - _y = y; - - void paint(Canvas can); + double get y; double get width; double get height; - void centerToFit() { - _x -= width / 2; - _y -= height / 2; - } + Color get color; - void translate(double x, double y) { - _x += x; - _y += y; - } + void paint(Canvas can); - double _x; - double _y; + void centerToFit(); } diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/base_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/base_shape.dart new file mode 100644 index 0000000..50c04bd --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/shapes/base_shape.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +import '../pattern/shape.dart'; + +abstract class BaseShape implements Shape { + @override + double get x => _x; + + @override + double get y => _y; + + @override + final Color color; + + BaseShape({ + required double x, + required double y, + required this.color, + }) : _x = x, + _y = y; + + @override + void centerToFit() { + _x -= width / 2; + _y -= height / 2; + } + + double _x; + double _y; +} diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/circle_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/circle_shape.dart index a4be61e..1bffdec 100644 --- a/patterns/abstract_factory/tool_panel_factory/shapes/circle_shape.dart +++ b/patterns/abstract_factory/tool_panel_factory/shapes/circle_shape.dart @@ -1,10 +1,8 @@ -import 'dart:ui'; +import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; +import 'base_shape.dart'; -import '../pattern/shape.dart'; - -class CircleShape extends Shape { +class CircleShape extends BaseShape { final double radius; final bool isFilled; diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/line_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/line_shape.dart index 7b0acd1..4191230 100644 --- a/patterns/abstract_factory/tool_panel_factory/shapes/line_shape.dart +++ b/patterns/abstract_factory/tool_panel_factory/shapes/line_shape.dart @@ -1,10 +1,10 @@ import 'dart:ui'; -import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; -import '../pattern/shape.dart'; +import 'base_shape.dart'; -class LineShape extends Shape { +class LineShape extends BaseShape { final double length; LineShape({ diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/star_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/star_shape.dart index 2d27299..f0d41b1 100644 --- a/patterns/abstract_factory/tool_panel_factory/shapes/star_shape.dart +++ b/patterns/abstract_factory/tool_panel_factory/shapes/star_shape.dart @@ -3,9 +3,9 @@ import 'dart:ui'; import 'package:flutter/material.dart'; -import '../pattern/shape.dart'; +import 'base_shape.dart'; -class StarShape extends Shape { +class StarShape extends BaseShape { final double radius; final bool isFilled; diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/text_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/text_shape.dart index 00bcbd0..5b35823 100644 --- a/patterns/abstract_factory/tool_panel_factory/shapes/text_shape.dart +++ b/patterns/abstract_factory/tool_panel_factory/shapes/text_shape.dart @@ -1,8 +1,8 @@ import 'dart:ui'; -import '../pattern/shape.dart'; +import 'base_shape.dart'; -class TextShape extends Shape { +class TextShape extends BaseShape { final String text; final double fontSize; diff --git a/patterns/abstract_factory/tool_panel_factory/shapes/triangle_shape.dart b/patterns/abstract_factory/tool_panel_factory/shapes/triangle_shape.dart index 4c0fe17..e94073f 100644 --- a/patterns/abstract_factory/tool_panel_factory/shapes/triangle_shape.dart +++ b/patterns/abstract_factory/tool_panel_factory/shapes/triangle_shape.dart @@ -1,11 +1,11 @@ import 'dart:math'; import 'dart:ui'; -import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; -import '../pattern/shape.dart'; +import 'base_shape.dart'; -class TriangleShape extends Shape { +class TriangleShape extends BaseShape { final double sideLength; final bool isFilled; From 3baaa80305e3d3cab09f1bf08a99cb041e55b09f Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 19 May 2022 17:33:37 +0300 Subject: [PATCH 309/479] =?UTF-8?q?=D0=A1reate=20property=20widget=20facto?= =?UTF-8?q?ries=20to=20apply=20pattern=20to=20flutter=20fidgets.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tool_panel_factory/main.dart | 13 ++ .../widgets/factories_tool_bar.dart | 3 +- .../widgets/property_bar.dart | 173 +----------------- .../bool_property_widget.dart | 46 +++++ .../double_property_widget.dart | 58 ++++++ .../factories/property_widget_factories.dart | 31 ++++ .../factories/property_widget_factory.dart | 9 + .../primitive/filed_label.dart | 25 +++ .../primitive/theme_property.dart | 29 +++ .../string_property_widget.dart | 43 +++++ 10 files changed, 265 insertions(+), 165 deletions(-) create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/bool_property_widget.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/double_property_widget.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factories.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factory.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/primitive/filed_label.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/primitive/theme_property.dart create mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/string_property_widget.dart diff --git a/patterns/abstract_factory/tool_panel_factory/main.dart b/patterns/abstract_factory/tool_panel_factory/main.dart index 15496e0..43d7caa 100644 --- a/patterns/abstract_factory/tool_panel_factory/main.dart +++ b/patterns/abstract_factory/tool_panel_factory/main.dart @@ -10,6 +10,10 @@ import 'factories/triangle_factory.dart'; import 'factories/text_factory.dart'; import 'widgets/drawing_board.dart'; import 'widgets/property_bar.dart'; +import 'widgets/property_widgets/bool_property_widget.dart'; +import 'widgets/property_widgets/double_property_widget.dart'; +import 'widgets/property_widgets/factories/property_widget_factories.dart'; +import 'widgets/property_widgets/string_property_widget.dart'; import 'widgets/tool_panel.dart'; class ToolPanelFactoryApp extends StatefulWidget { @@ -39,6 +43,14 @@ class _ToolPanelFactoryAppState extends State { ), ); + final propertyWidgetFactories = PropertyWidgetFactories( + factories: [ + StringPropertyWidgetFactory(), + DoublePropertyWidgetFactory(), + BoolPropertyWidgetFactory(), + ], + ); + @override Widget build(BuildContext context) { return Stack( @@ -52,6 +64,7 @@ class _ToolPanelFactoryAppState extends State { ), PropertyPanel( tools: app.tools, + propertyWidgetFactories: propertyWidgetFactories, ), ], ); diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/factories_tool_bar.dart b/patterns/abstract_factory/tool_panel_factory/widgets/factories_tool_bar.dart index 51514f0..02ad8c4 100644 --- a/patterns/abstract_factory/tool_panel_factory/widgets/factories_tool_bar.dart +++ b/patterns/abstract_factory/tool_panel_factory/widgets/factories_tool_bar.dart @@ -20,7 +20,8 @@ class FactoriesToolBar extends StatelessWidget { ? _buildToolButtons() : Padding( padding: EdgeInsets.all(10), - child: CircularProgressIndicator()); + child: CircularProgressIndicator(), + ); }, ), ); diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart index bd53b5f..7783dc0 100644 --- a/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart @@ -1,16 +1,19 @@ import 'package:flutter/material.dart'; import '../app/tools.dart'; -import '../pattern/property.dart'; import '../pattern/tool_factory.dart'; import 'independent/panel.dart'; +import 'property_widgets/factories/property_widget_factories.dart'; +import 'property_widgets/primitive/theme_property.dart'; class PropertyPanel extends StatelessWidget { final Tools tools; + final PropertyWidgetFactories propertyWidgetFactories; const PropertyPanel({ Key? key, required this.tools, + required this.propertyWidgetFactories, }) : super(key: key); @override @@ -18,17 +21,16 @@ class PropertyPanel extends StatelessWidget { return Positioned( top: 12, left: 12 + 64 + 8 + 12, - child: _buildTheme( + child: ThemeProperty( child: Panel( direction: Axis.horizontal, child: ValueListenableBuilder( valueListenable: tools.activeFactory, - builder: (_, activeFactory, __) { + builder: (_, activeToolFactory, __) { return Row( - children: [ - for (final prop in activeFactory.properties) - _buildProperty(prop), - ], + children: propertyWidgetFactories + .createListPropertyWidgetsFrom(activeToolFactory.properties) + .toList(), ); }, ), @@ -36,161 +38,4 @@ class PropertyPanel extends StatelessWidget { ), ); } - - Widget _buildTheme({required Widget child}) { - return Material( - color: Colors.transparent, - textStyle: TextStyle( - color: Colors.white, - fontSize: 16, - fontFamily: 'Arial', - ), - child: Theme( - data: ThemeData( - primarySwatch: Colors.pink, - unselectedWidgetColor: Colors.grey, // Your color - ), - child: child), - ); - } - - Widget _buildProperty(Property prop) { - final val = prop.value(); - - if (val is String) { - return StringPropertyWidget(property: prop); - } else if (val is double) { - return DoublePropertyWidget(property: prop); - } else if (val is bool) { - return BoolPropertyWidget(property: prop); - } - - throw 'Type(${prop.value().runtimeType}) of property is not support'; - } -} - -class StringPropertyWidget extends StatelessWidget { - final Property property; - - const StringPropertyWidget({Key? key, required this.property}) - : super(key: key); - - @override - Widget build(BuildContext context) { - return _FieldLabel( - text: property.name, - child: Container( - color: Colors.white12, - width: 120, - height: 32, - child: TextFormField( - style: TextStyle(fontSize: 18, color: Colors.white70), - decoration: InputDecoration( - contentPadding: const EdgeInsets.only(left: 5.0, bottom: 14.0), - border: UnderlineInputBorder(), - ), - initialValue: property.value(), - onChanged: property.onChange, - ), - ), - ); - } -} - -class DoublePropertyWidget extends StatefulWidget { - final Property property; - - const DoublePropertyWidget({Key? key, required this.property}) - : super(key: key); - - @override - _DoublePropertyWidgetState createState() => _DoublePropertyWidgetState(); -} - -class _DoublePropertyWidgetState extends State { - @override - Widget build(BuildContext context) { - return _FieldLabel( - text: widget.property.name, - child: Row( - children: [ - SizedBox( - width: 200, - child: Slider.adaptive( - min: 0.0, - max: 300, - value: widget.property.value() as double, - onChanged: (val) { - setState(() { - widget.property.onChange(val); - }); - }, - ), - ), - SizedBox( - width: 32, - child: Text( - (widget.property.value() as double).toStringAsFixed(0), - textAlign: TextAlign.right, - ), - ) - ], - ), - ); - } -} - -class BoolPropertyWidget extends StatefulWidget { - final Property property; - - const BoolPropertyWidget({Key? key, required this.property}) - : super(key: key); - - @override - _BoolPropertyWidgetState createState() => _BoolPropertyWidgetState(); -} - -class _BoolPropertyWidgetState extends State { - @override - Widget build(BuildContext context) { - return _FieldLabel( - text: widget.property.name, - child: Row( - children: [ - Checkbox( - value: widget.property.value() as bool, - onChanged: (val) { - setState(() { - widget.property.onChange(val); - }); - }, - ) - ], - ), - ); - } -} - -class _FieldLabel extends StatelessWidget { - final String text; - final Widget child; - - const _FieldLabel({ - Key? key, - required this.text, - required this.child, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - SizedBox(width: 10), - Text(text + ':'), - SizedBox(width: 10), - child, - SizedBox(width: 20), - ], - ); - } } diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/bool_property_widget.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/bool_property_widget.dart new file mode 100644 index 0000000..0462aad --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/bool_property_widget.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +import '../../pattern/property.dart'; +import 'factories/property_widget_factory.dart'; +import 'primitive/filed_label.dart'; + +class BoolPropertyWidgetFactory implements PropertyWidgetFactory { + @override + Widget createPropertyWidget(Property property) { + return BoolPropertyWidget(property: property); + } + + @override + bool isPropertyContain(Property property) => property.value() is bool; +} + +class BoolPropertyWidget extends StatefulWidget { + final Property property; + + const BoolPropertyWidget({Key? key, required this.property}) + : super(key: key); + + @override + _BoolPropertyWidgetState createState() => _BoolPropertyWidgetState(); +} + +class _BoolPropertyWidgetState extends State { + @override + Widget build(BuildContext context) { + return FieldLabel( + text: widget.property.name, + child: Row( + children: [ + Checkbox( + value: widget.property.value() as bool, + onChanged: (val) { + setState(() { + widget.property.onChange(val); + }); + }, + ) + ], + ), + ); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/double_property_widget.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/double_property_widget.dart new file mode 100644 index 0000000..4cf95e1 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/double_property_widget.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; + +import '../../pattern/property.dart'; +import 'factories/property_widget_factory.dart'; +import 'primitive/filed_label.dart'; + +class DoublePropertyWidgetFactory implements PropertyWidgetFactory { + @override + Widget createPropertyWidget(Property property) { + return DoublePropertyWidget(property: property); + } + + @override + bool isPropertyContain(Property property) => property.value() is double; +} + +class DoublePropertyWidget extends StatefulWidget { + final Property property; + + const DoublePropertyWidget({Key? key, required this.property}) + : super(key: key); + + @override + _DoublePropertyWidgetState createState() => _DoublePropertyWidgetState(); +} + +class _DoublePropertyWidgetState extends State { + @override + Widget build(BuildContext context) { + return FieldLabel( + text: widget.property.name, + child: Row( + children: [ + SizedBox( + width: 200, + child: Slider.adaptive( + min: 0.0, + max: 300, + value: widget.property.value() as double, + onChanged: (val) { + setState(() { + widget.property.onChange(val); + }); + }, + ), + ), + SizedBox( + width: 32, + child: Text( + (widget.property.value() as double).toStringAsFixed(0), + textAlign: TextAlign.right, + ), + ) + ], + ), + ); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factories.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factories.dart new file mode 100644 index 0000000..340cf74 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factories.dart @@ -0,0 +1,31 @@ +import 'package:flutter/widgets.dart'; + +import '../../../pattern/property.dart'; + +import 'property_widget_factory.dart'; + +class PropertyWidgetFactories { + final List _factories; + + PropertyWidgetFactories({ + required List factories, + }) : _factories = factories; + + Widget createPropertyWidgetFrom(Property property) { + for (final factory in _factories) { + if (factory.isPropertyContain(property)) { + return factory.createPropertyWidget(property); + } + } + + throw 'Value(${property.value()}) property is not support.'; + } + + Iterable createListPropertyWidgetsFrom( + Iterable properties, + ) sync* { + for (final property in properties) { + yield createPropertyWidgetFrom(property); + } + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factory.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factory.dart new file mode 100644 index 0000000..ab210d6 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factory.dart @@ -0,0 +1,9 @@ +import 'package:flutter/widgets.dart'; + +import '../../../pattern/property.dart'; + +abstract class PropertyWidgetFactory { + Widget createPropertyWidget(Property property); + + bool isPropertyContain(Property value); +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/primitive/filed_label.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/primitive/filed_label.dart new file mode 100644 index 0000000..3371e09 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/primitive/filed_label.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +class FieldLabel extends StatelessWidget { + final String text; + final Widget child; + + const FieldLabel({ + Key? key, + required this.text, + required this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + SizedBox(width: 10), + Text(text + ':'), + SizedBox(width: 10), + child, + SizedBox(width: 20), + ], + ); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/primitive/theme_property.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/primitive/theme_property.dart new file mode 100644 index 0000000..af62c06 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/primitive/theme_property.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +class ThemeProperty extends StatelessWidget { + final Widget child; + + const ThemeProperty({ + Key? key, + required this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Material( + color: Colors.transparent, + textStyle: TextStyle( + color: Colors.white, + fontSize: 16, + fontFamily: 'Arial', + ), + child: Theme( + data: ThemeData( + primarySwatch: Colors.pink, + unselectedWidgetColor: Colors.grey, // Your color + ), + child: child, + ), + ); + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/string_property_widget.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/string_property_widget.dart new file mode 100644 index 0000000..f1dff00 --- /dev/null +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/string_property_widget.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +import '../../pattern/property.dart'; +import 'factories/property_widget_factory.dart'; +import 'primitive/filed_label.dart'; + +class StringPropertyWidgetFactory implements PropertyWidgetFactory { + @override + Widget createPropertyWidget(Property property) { + return StringPropertyWidget(property: property); + } + + @override + bool isPropertyContain(Property property) => property.value() is String; +} + +class StringPropertyWidget extends StatelessWidget { + final Property property; + + const StringPropertyWidget({Key? key, required this.property}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return FieldLabel( + text: property.name, + child: Container( + color: Colors.white12, + width: 120, + height: 32, + child: TextFormField( + style: TextStyle(fontSize: 18, color: Colors.white70), + decoration: InputDecoration( + contentPadding: const EdgeInsets.only(left: 5.0, bottom: 14.0), + border: UnderlineInputBorder(), + ), + initialValue: property.value(), + onChanged: property.onChange, + ), + ), + ); + } +} From b8b420fd183984fd4d4a7565e72bdf4bbf03e304 Mon Sep 17 00:00:00 2001 From: ilopX Date: Thu, 19 May 2022 20:54:24 +0300 Subject: [PATCH 310/479] Drawing all shapes in one place without additional widgets. --- .../widgets/drawing_board.dart | 54 ++++++++++++++----- .../widgets/shape_widget.dart | 39 -------------- 2 files changed, 40 insertions(+), 53 deletions(-) delete mode 100644 patterns/abstract_factory/tool_panel_factory/widgets/shape_widget.dart diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/drawing_board.dart b/patterns/abstract_factory/tool_panel_factory/widgets/drawing_board.dart index 3735982..9dba58b 100644 --- a/patterns/abstract_factory/tool_panel_factory/widgets/drawing_board.dart +++ b/patterns/abstract_factory/tool_panel_factory/widgets/drawing_board.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import '../app/shapes.dart'; import 'independent/event_listenable_builder.dart'; -import 'shape_widget.dart'; class DrawingBoard extends StatelessWidget { final Shapes shapes; @@ -15,19 +14,25 @@ class DrawingBoard extends StatelessWidget { @override Widget build(BuildContext context) { - return GestureDetector( - onTapDown: (e) => addShape(e.localPosition), - child: Container( - color: Color(0xff1f1f1f), - child: EventListenableBuilder( - event: shapes.onAddShapeEvent, - builder: (_) { - return Stack( - children: [ - for (final shape in shapes) ShapeWidget(shape: shape), - ], - ); - }, + return Listener( + onPointerMove: (e) => addShape(e.localPosition), + child: GestureDetector( + onTapDown: (e) => addShape(e.localPosition), + child: Container( + color: Color(0xff1f1f1f), + child: EventListenableBuilder( + event: shapes.onAddShapeEvent, + builder: (_) { + return LayoutBuilder( + builder: (_, constraints) { + return CustomPaint( + size: Size(constraints.maxWidth, constraints.maxHeight), + painter: ShapesPainter(shapes), + ); + }, + ); + }, + ), ), ), ); @@ -37,3 +42,24 @@ class DrawingBoard extends StatelessWidget { onClick(position.dx, position.dy); } } + +class ShapesPainter extends CustomPainter { + final Shapes shapes; + + ShapesPainter(this.shapes); + + @override + void paint(Canvas canvas, Size _) { + for (final shape in shapes) { + canvas.save(); + canvas.translate(shape.x, shape.y); + shape.paint(canvas); + canvas.restore(); + } + } + + @override + bool shouldRepaint(covariant CustomPainter _) { + return false; + } +} diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/shape_widget.dart b/patterns/abstract_factory/tool_panel_factory/widgets/shape_widget.dart deleted file mode 100644 index aec0755..0000000 --- a/patterns/abstract_factory/tool_panel_factory/widgets/shape_widget.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; -import '../pattern/shape.dart'; - -class ShapeWidget extends StatelessWidget { - final Shape shape; - - const ShapeWidget({ - Key? key, - required this.shape, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Positioned( - left: shape.x, - top: shape.y, - child: CustomPaint( - size: Size(shape.width, shape.height), - painter: PaintShape(shape), - ), - ); - } -} - -class PaintShape extends CustomPainter { - final Shape shape; - - PaintShape(this.shape); - - @override - void paint(Canvas canvas, Size _) { - shape.paint(canvas); - } - - @override - bool shouldRepaint(covariant CustomPainter _) { - return false; - } -} From ec6daf328f8831c7b0d71b7cb95649279ec67cba Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 20 May 2022 13:35:15 +0300 Subject: [PATCH 311/479] Remove name "property: from creational factory methods. --- .../tool_panel_factory/widgets/property_bar.dart | 2 +- .../widgets/property_widgets/bool_property_widget.dart | 4 ++-- .../widgets/property_widgets/double_property_widget.dart | 4 ++-- .../factories/property_widget_factories.dart | 6 +++--- .../property_widgets/factories/property_widget_factory.dart | 4 ++-- .../widgets/property_widgets/string_property_widget.dart | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart index 7783dc0..5b2dd94 100644 --- a/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart @@ -29,7 +29,7 @@ class PropertyPanel extends StatelessWidget { builder: (_, activeToolFactory, __) { return Row( children: propertyWidgetFactories - .createListPropertyWidgetsFrom(activeToolFactory.properties) + .createListWidgetsFrom(activeToolFactory.properties) .toList(), ); }, diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/bool_property_widget.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/bool_property_widget.dart index 0462aad..b3e1976 100644 --- a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/bool_property_widget.dart +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/bool_property_widget.dart @@ -6,12 +6,12 @@ import 'primitive/filed_label.dart'; class BoolPropertyWidgetFactory implements PropertyWidgetFactory { @override - Widget createPropertyWidget(Property property) { + Widget createWidget(Property property) { return BoolPropertyWidget(property: property); } @override - bool isPropertyContain(Property property) => property.value() is bool; + bool isPropertyCompatible(Property property) => property.value() is bool; } class BoolPropertyWidget extends StatefulWidget { diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/double_property_widget.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/double_property_widget.dart index 4cf95e1..741f54e 100644 --- a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/double_property_widget.dart +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/double_property_widget.dart @@ -6,12 +6,12 @@ import 'primitive/filed_label.dart'; class DoublePropertyWidgetFactory implements PropertyWidgetFactory { @override - Widget createPropertyWidget(Property property) { + Widget createWidget(Property property) { return DoublePropertyWidget(property: property); } @override - bool isPropertyContain(Property property) => property.value() is double; + bool isPropertyCompatible(Property property) => property.value() is double; } class DoublePropertyWidget extends StatefulWidget { diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factories.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factories.dart index 340cf74..760e4b2 100644 --- a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factories.dart +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factories.dart @@ -13,15 +13,15 @@ class PropertyWidgetFactories { Widget createPropertyWidgetFrom(Property property) { for (final factory in _factories) { - if (factory.isPropertyContain(property)) { - return factory.createPropertyWidget(property); + if (factory.isPropertyCompatible(property)) { + return factory.createWidget(property); } } throw 'Value(${property.value()}) property is not support.'; } - Iterable createListPropertyWidgetsFrom( + Iterable createListWidgetsFrom( Iterable properties, ) sync* { for (final property in properties) { diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factory.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factory.dart index ab210d6..66c4820 100644 --- a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factory.dart +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factory.dart @@ -3,7 +3,7 @@ import 'package:flutter/widgets.dart'; import '../../../pattern/property.dart'; abstract class PropertyWidgetFactory { - Widget createPropertyWidget(Property property); + Widget createWidget(Property property); - bool isPropertyContain(Property value); + bool isPropertyCompatible(Property value); } diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/string_property_widget.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/string_property_widget.dart index f1dff00..e64fefb 100644 --- a/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/string_property_widget.dart +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/string_property_widget.dart @@ -6,12 +6,12 @@ import 'primitive/filed_label.dart'; class StringPropertyWidgetFactory implements PropertyWidgetFactory { @override - Widget createPropertyWidget(Property property) { + Widget createWidget(Property property) { return StringPropertyWidget(property: property); } @override - bool isPropertyContain(Property property) => property.value() is String; + bool isPropertyCompatible(Property property) => property.value() is String; } class StringPropertyWidget extends StatelessWidget { From 2084f2a72afe8a4abff4605ebbb87ffc72d176cf Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 20 May 2022 15:31:52 +0300 Subject: [PATCH 312/479] Rename variable: propertyWidgetFactorise to factories. --- patterns/abstract_factory/tool_panel_factory/main.dart | 2 +- .../tool_panel_factory/widgets/property_bar.dart | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/patterns/abstract_factory/tool_panel_factory/main.dart b/patterns/abstract_factory/tool_panel_factory/main.dart index 43d7caa..61d45f9 100644 --- a/patterns/abstract_factory/tool_panel_factory/main.dart +++ b/patterns/abstract_factory/tool_panel_factory/main.dart @@ -64,7 +64,7 @@ class _ToolPanelFactoryAppState extends State { ), PropertyPanel( tools: app.tools, - propertyWidgetFactories: propertyWidgetFactories, + factories: propertyWidgetFactories, ), ], ); diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart b/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart index 5b2dd94..ac1483f 100644 --- a/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart +++ b/patterns/abstract_factory/tool_panel_factory/widgets/property_bar.dart @@ -8,12 +8,12 @@ import 'property_widgets/primitive/theme_property.dart'; class PropertyPanel extends StatelessWidget { final Tools tools; - final PropertyWidgetFactories propertyWidgetFactories; + final PropertyWidgetFactories factories; const PropertyPanel({ Key? key, required this.tools, - required this.propertyWidgetFactories, + required this.factories, }) : super(key: key); @override @@ -28,7 +28,7 @@ class PropertyPanel extends StatelessWidget { valueListenable: tools.activeFactory, builder: (_, activeToolFactory, __) { return Row( - children: propertyWidgetFactories + children: factories .createListWidgetsFrom(activeToolFactory.properties) .toList(), ); From c32d2649e04b8c3c9fb1c7ec5adf5c39032a2726 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 20 May 2022 15:32:57 +0300 Subject: [PATCH 313/479] Add an "about" and update the diagram in the README. --- .../tool_panel_factory/README.md | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/patterns/abstract_factory/tool_panel_factory/README.md b/patterns/abstract_factory/tool_panel_factory/README.md index 36e82d0..ac32a00 100644 --- a/patterns/abstract_factory/tool_panel_factory/README.md +++ b/patterns/abstract_factory/tool_panel_factory/README.md @@ -9,40 +9,32 @@ Click on the picture to see the [demo](https://RefactoringGuru.github.io/design- [![image](https://user-images.githubusercontent.com/8049534/168668992-369a1bab-9f97-4333-a20e-ffd06bf91b54.png)](https://refactoringguru.github.io/design-patterns-dart/#/abstract_factory/tool_panel_factory) +### About +![image](https://user-images.githubusercontent.com/8049534/169521422-052cc59a-7b3d-4889-8d76-5664a75b271a.png) + ### Diagram: -![image](https://user-images.githubusercontent.com/8049534/168672053-73ae1c9c-8fad-45ae-9247-429f7b5da565.png) +![image](https://user-images.githubusercontent.com/8049534/169528461-d7c5279d-7e7f-4ce7-b8d2-87388ec2d32f.png) -### Client code (using the "createShape" method): +### Client code: ```dart -final app = App( - tools: Tools( - factories: [ - TxtFactory(), - LineFactory(), - CircleFactory(), - TriangleFactory(), - StarFactory(), - ], - ), -); - class App { void addShape(double x, double y) { - final newShape = activeFactory.createShape(x, y, activeColor); + final newShape = activeToolFactory.createShape(x, y, activeColor); shapes.add(newShape); } } -mixin IconBoxMixin implements FactoryTool { - Image? _icon; - @override - Image get icon => _icon!; +class PropertyPanel extends StatelessWidget { + final PropertyWidgetFactories factories; - Future updateIcon(Color color) async { - final shape = createShape(0, 0, color); - final pngBytes = await _pngImageFromShape(shape); - _icon = Image.memory(pngBytes); + @override + Widget build(BuildContext context) { + return Row( + children: propertyWidgetFactories + .createListWidgetsFrom(activeToolFactory.properties) + .toList(), + ); } } ``` From 6556e2ed0891cf92f35b543f4908e596964d3d21 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 20 May 2022 15:43:49 +0300 Subject: [PATCH 314/479] Update screenshot. --- patterns/abstract_factory/tool_panel_factory/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/patterns/abstract_factory/tool_panel_factory/README.md b/patterns/abstract_factory/tool_panel_factory/README.md index ac32a00..8dc0de4 100644 --- a/patterns/abstract_factory/tool_panel_factory/README.md +++ b/patterns/abstract_factory/tool_panel_factory/README.md @@ -7,7 +7,7 @@ Tutorial: [here](https://refactoring.guru/design-patterns/abstract-factory). ### Online demo: Click on the picture to see the [demo](https://RefactoringGuru.github.io/design-patterns-dart/#/abstract_factory/tool_panel_factory). -[![image](https://user-images.githubusercontent.com/8049534/168668992-369a1bab-9f97-4333-a20e-ffd06bf91b54.png)](https://refactoringguru.github.io/design-patterns-dart/#/abstract_factory/tool_panel_factory) +[![image](https://user-images.githubusercontent.com/8049534/169530318-0ce7ee6a-3538-4398-a2ab-e6e85f2132b5.png)](https://refactoringguru.github.io/design-patterns-dart/#/abstract_factory/tool_panel_factory) ### About ![image](https://user-images.githubusercontent.com/8049534/169521422-052cc59a-7b3d-4889-8d76-5664a75b271a.png) @@ -38,4 +38,3 @@ class PropertyPanel extends StatelessWidget { } } ``` - From ea8b4880e83ff26b6fdfe75faee2be76223d54a4 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 20 May 2022 15:57:17 +0300 Subject: [PATCH 315/479] Remove moving painter. --- .../widgets/drawing_board.dart | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/patterns/abstract_factory/tool_panel_factory/widgets/drawing_board.dart b/patterns/abstract_factory/tool_panel_factory/widgets/drawing_board.dart index 9dba58b..590f94e 100644 --- a/patterns/abstract_factory/tool_panel_factory/widgets/drawing_board.dart +++ b/patterns/abstract_factory/tool_panel_factory/widgets/drawing_board.dart @@ -14,25 +14,22 @@ class DrawingBoard extends StatelessWidget { @override Widget build(BuildContext context) { - return Listener( - onPointerMove: (e) => addShape(e.localPosition), - child: GestureDetector( - onTapDown: (e) => addShape(e.localPosition), - child: Container( - color: Color(0xff1f1f1f), - child: EventListenableBuilder( - event: shapes.onAddShapeEvent, - builder: (_) { - return LayoutBuilder( - builder: (_, constraints) { - return CustomPaint( - size: Size(constraints.maxWidth, constraints.maxHeight), - painter: ShapesPainter(shapes), - ); - }, - ); - }, - ), + return GestureDetector( + onTapDown: (e) => addShape(e.localPosition), + child: Container( + color: Color(0xff1f1f1f), + child: EventListenableBuilder( + event: shapes.onAddShapeEvent, + builder: (_) { + return LayoutBuilder( + builder: (_, constraints) { + return CustomPaint( + size: Size(constraints.maxWidth, constraints.maxHeight), + painter: ShapesPainter(shapes), + ); + }, + ); + }, ), ), ); From a5f871243d58267b6a233c1f0ef154f9c3f1a4a3 Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 20 May 2022 15:59:08 +0300 Subject: [PATCH 316/479] Bump version 0.23.12. --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cacd0cc..43f2ab2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.23.12 +- Add properties bar to "Tool Panel Factory". + ## 0.23.0 - Add "Tool Panel Factory" flutter example diff --git a/pubspec.yaml b/pubspec.yaml index 95bed6c..a892ffd 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.23.0 +version: 0.23.12 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 ad847d2e16f9e6aa19a9e4aeac4cacd1b7bc46ce Mon Sep 17 00:00:00 2001 From: ilopX Date: Fri, 20 May 2022 16:08:51 +0300 Subject: [PATCH 317/479] Update demos splash screen logo. --- web/index.html | 2 +- web/logo-screen-ukraine.png | Bin 0 -> 62056 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 web/logo-screen-ukraine.png diff --git a/web/index.html b/web/index.html index c8c2113..a8339d0 100644 --- a/web/index.html +++ b/web/index.html @@ -62,7 +62,7 @@