├── README.md ├── _config.yml ├── assets ├── author.png ├── book.png ├── cpp.png ├── csharp.png ├── design-patterns.png ├── go.png ├── java.png ├── php.png ├── python.png ├── ruby.png ├── swift.png └── typescript.png ├── behavioral-pattern ├── README.md ├── assets │ ├── chain-of-responsibility-mini.png │ ├── command-mini.png │ ├── iterator-mini.png │ ├── mediator-mini.png │ ├── memento-mini.png │ ├── observer-mini.png │ ├── state-mini.png │ ├── strategy-mini.png │ ├── template-method-mini.png │ └── visitor-mini.png ├── chain-of-responsibility │ ├── README.md │ └── assets │ │ ├── analogy.png │ │ ├── intent.png │ │ ├── problem1.png │ │ ├── problem2.png │ │ ├── pseudocode.png │ │ ├── pseudocode2.png │ │ ├── solution1.png │ │ ├── solution2.png │ │ └── structure.png ├── command │ ├── README.md │ └── assets │ │ ├── analogy.png │ │ ├── intent.png │ │ ├── problem1.png │ │ ├── problem2.png │ │ ├── problem3.png │ │ ├── pseudocode.png │ │ ├── solution1.png │ │ ├── solution2.png │ │ ├── solution3.png │ │ └── structure.png ├── iterator │ ├── README.md │ └── assets │ │ ├── analogy.png │ │ ├── intent.png │ │ ├── problem1.png │ │ ├── problem2.png │ │ ├── pseudocode.png │ │ ├── solution1.png │ │ └── structure.png ├── mediator │ ├── README.md │ └── assets │ │ ├── analogy.png │ │ ├── intent.png │ │ ├── problem1.png │ │ ├── problem2.png │ │ ├── pseudocode.png │ │ ├── solution1.png │ │ └── structure.png ├── memento │ ├── README.md │ └── assets │ │ ├── intent.png │ │ ├── problem1.png │ │ ├── problem2.png │ │ ├── pseudocode.png │ │ ├── solution.png │ │ ├── structure1.png │ │ ├── structure2.png │ │ └── structure3.png ├── observer │ ├── README.md │ └── assets │ │ ├── analogy.png │ │ ├── intent.png │ │ ├── problem.png │ │ ├── pseudocode.png │ │ ├── solution1.png │ │ ├── solution2.png │ │ └── structure.png ├── state │ ├── README.md │ └── assets │ │ ├── intent.png │ │ ├── problem1.png │ │ ├── problem2.png │ │ ├── pseudocode.png │ │ ├── solution.png │ │ └── structure.png ├── strategy │ ├── README.md │ └── assets │ │ ├── analogy.png │ │ ├── intent.png │ │ ├── problem.png │ │ ├── solution.png │ │ └── structure.png ├── template-method │ ├── README.md │ └── assets │ │ ├── analogy.png │ │ ├── intent.png │ │ ├── problem.png │ │ ├── pseudocode.png │ │ ├── solution.png │ │ └── structure.png └── visitor │ ├── README.md │ └── assets │ ├── analogy.png │ ├── intent.png │ ├── problem1.png │ ├── problem2.png │ ├── pseudocode.png │ └── structure.png ├── creational-pattern ├── README.md ├── abstract-factory │ ├── README.md │ └── assets │ │ ├── intent.png │ │ ├── problem.png │ │ ├── problem1.png │ │ ├── pseudocode.png │ │ ├── solution1.png │ │ ├── solution2.png │ │ ├── solution3.png │ │ └── structure.png ├── assets │ ├── abstract-factory-mini.png │ ├── builder-mini.png │ ├── factory-method-mini.png │ ├── prototype-mini.png │ └── singleton-mini.png ├── builder │ ├── README.md │ └── assets │ │ ├── directory.png │ │ ├── intent.png │ │ ├── problem1.png │ │ ├── problem2.png │ │ ├── pseudocode.png │ │ ├── solution1.png │ │ ├── solution2.png │ │ └── structure.png ├── factory-method │ ├── README.md │ └── assets │ │ ├── intent.png │ │ ├── problem.png │ │ ├── pseudocode.png │ │ ├── solution1.png │ │ ├── solution2.png │ │ ├── solution3.png │ │ └── structure.png ├── prototype │ ├── README.md │ └── assets │ │ ├── analogy.png │ │ ├── intent.png │ │ ├── problem.png │ │ ├── pseudocode.png │ │ ├── solution.png │ │ ├── structure1.png │ │ └── structure2.png └── singleton │ ├── README.md │ └── assets │ ├── intent.png │ ├── problem.png │ └── structure.png └── structural-pattern ├── README.md ├── adapter ├── README.md └── assets │ ├── analogy.png │ ├── intent.png │ ├── problem.png │ ├── pseudocode.png │ ├── solution.png │ ├── structure1.png │ └── structure2.png ├── assets ├── adapter-mini.png ├── bridge-mini.png ├── composite-mini.png ├── decorator-mini.png ├── facade-mini.png ├── flyweight-mini.png └── proxy-mini.png ├── bridge ├── README.md └── assets │ ├── abstraction.png │ ├── implement.png │ ├── intent.png │ ├── problem.png │ ├── pseudocode.png │ ├── solution.png │ └── structure.png ├── composite ├── README.md └── assets │ ├── analogy.png │ ├── intent.png │ ├── problem.png │ ├── pseudocode.png │ ├── solution.png │ └── structure.png ├── decorator ├── README.md └── assets │ ├── analogy.png │ ├── intent.png │ ├── problem1.png │ ├── problem2.png │ ├── problem3.png │ ├── pseudocode.png │ ├── solution1.png │ ├── solution2.png │ ├── solution3.png │ └── structure.png ├── facade ├── README.md └── assets │ ├── analogy.png │ ├── intent.png │ ├── pseudocode.png │ └── structure.png ├── flyweight ├── README.md └── assets │ ├── intent.png │ ├── problem.png │ ├── pseudocode.png │ ├── solution1.png │ ├── solution2.png │ ├── solution3.png │ └── structure.png └── proxy ├── README.md └── assets ├── analogy.png ├── intent.png ├── problem.png ├── pseudocode.png ├── solution.png └── structure.png /README.md: -------------------------------------------------------------------------------- 1 | # DESIGN PATTERN LÀ GÌ ? 2 | 3 | ![design-pattern](./assets/design-patterns.png) 4 | 5 | Design Pattern là một giải pháp tổng thể cho các vấn đề chung trong thiết kế phần mềm. Nó cũng tương tự các bản thiết kế cho xây dựng nhà cửa, chúng được dùng để giải quyết các vấn đề lặp đi lặp lại trong thiết kế của bạn. 6 | 7 | Các design pattern không thể copy rồi paste như cách bạn làm với các function có sẵn hay thư viện, vì chúng không phải là những đoạn code cụ thể. Design pattern ở đây là những khái niệm tổng quát để giải quyết các vấn đề riêng biệt. Bạn có thể tìm hiểu các design pattern và triển khai chúng lên ứng dụng của bạn. Các pattern thường bị nhầm lẫn với thuật toán, vì chúng đều là những khái niệm mô tả giải pháp cho một vấn đề nào đó. 8 | 9 | Trong khi thuật toán là định nghĩa những hành động cụ thể để giải quyết vấn đề thì design pattern lại là một mô tả cao hơn cho các giải pháp. Code cho cùng một pattern có thể được triển khai trên hai ứng dụng khác nhau. 10 | 11 | ## Tài liệu design pattern bao gồm những gì ? 12 | 13 | Hầu hết các tài liệu mô tả rất chính thống, để cho mọi người có thể tái sử dụng cho nhiều trường hợp. Dưới đây là các thành phần thường có trong các document mô tả pattern: 14 | 15 | - **Invention**: mục đích pattern, mô tả ngắn gọn cả vấn đề và giải pháp. 16 | - **Motivation**: giải thích thêm vấn đề và giải pháp mà mô hình khả thi. 17 | - **Structure**: cấu trúc của các lớp cho thấy từng phần của pattern và chúng có liên quan như thế nào. 18 | - **Example**: ví dụ bằng một trong những ngôn ngữ lập trình phổ biến giúp bạn dễ dàng nắm bắt ý tưởng đằng sau pattern. 19 | 20 | # Tại sao nên học design pattern 21 | 22 | Sự thật là các lập trình viên có thể xoay xở làm việc trong nhiều năm mà không cần biết đến bất kỳ pattern nào. Rất nhiều người làm như vậy. Tuy nhiên, ngay cả trong trường hợp đó, bạn có thể đang triển khai một số pattern mà không hề hay biết. Vậy tại sao bạn lại dành thời gian tìm hiểu chúng? 23 | 24 | - Giúp sản phẩm của chúng ta linh hoạt, dễ dàng thay đổi và bảo trì hơn. 25 | - Có một điều luôn xảy ra trong phát triển phần mềm, đó là sự thay đổi về yêu cầu. Lúc này hệ thống phình to, các tính năng mới được thêm vào trong khi performance cần được tối ưu hơn. 26 | - Design pattern cung cấp những giải pháp đã được tối ưu hóa, đã được kiểm chứng để giải quyết các vấn đề trong software engineering. Các giải pháp ở dạng tổng quát, giúp tăng tốc độ phát triển phần mềm bằng cách đưa ra các mô hình test, mô hình phát triển đã qua kiểm nghiệm. 27 | - Những khi bạn gặp bất kỳ khó khăn đối với những vấn đề đã được giải quyết rồi, design patterns là hướng đi giúp bạn giải quyết vấn đề thay vì tự tìm kiếm giải pháp tốn kém thời gian. 28 | - Giúp cho các lập trình viên có thể hiểu code của người khác một cách nhanh chóng (có thể hiểu là các mối quan hệ giữa các module chẳng hạn). Mọi thành viên trong team có thể dễ dàng trao đổi với nhau để cùng xây dựng dự án mà không tốn nhiều thời gian. 29 | 30 | # Khi nào nên sử dụng design pattern 31 | 32 | Việc sử dụng các design pattern sẽ giúp chúng ta giảm được thời gian và công sức suy nghĩ ra các cách giải quyết cho những vấn đề đã có lời giải. Lợi ích của việc sử dụng các mô hình Design Pattern vào phần mềm đó chính là giúp chương trình chạy uyển chuyển hơn, dễ dàng quản lý tiến trình hoạt động, dễ nâng cấp bảo trì, … 33 | 34 | Tuy nhiên điểm bất cập của design pattern là nó luôn là một lĩnh vực khá khó nhằn và hơi trừu tượng. Khi bạn viết code mới từ đầu, khá dễ dàng để nhận ra sự cần thiết phải có design pattern. Tuy nhiên, việc áp dụng design pattern cho code cũ thì khó khăn hơn. 35 | 36 | Khi sử dụng những design pattern có sẵn thì chúng ta sẽ đối mặt với một vấn đề nữa là perfomance của product (code sẽ chạy chậm chẳng hạn). Cần phải chắc chắn là bạn đã hiểu toàn bộ mã nguồn làm việc như thế nào trước khi đụng vào nó. Việc này có thể là dễ dàng hoặc là đau đầu, phụ thuộc vào độ phức tạp của code. 37 | 38 | Hiện nay chúng ta đang áp dụng rất nhiều design pattern vào công việc lập trình của mình. Nếu bạn thường tải và cài đặt các thư viện, packages hoặc module nào đó thì đó là lúc bạn thực thi một design pattern vào hệ thống. 39 | 40 | Tất cả các framework cho ứng dụng web như Laravel, Codeigniter… đều có sử dụng những kiến trúc design pattern có sẵn và mỗi framework sẽ có những kiểu design pattern riêng. 41 | 42 | # Phân loại design pattern 43 | 44 | Hệ thống các design pattern được chia thành 3 nhóm: nhóm Creational, nhóm Structural và nhóm Behavioral. 45 | 46 | - [**Creational Pattern**](./creational-pattern) cung cấp các cơ chế tạo đối tượng để tăng tính linh hoạt và tái sử dụng mã hiện có. 47 | + [**Factory Method**](./creational-pattern/factory-method) 48 | + [**Abstract Factory**](./creational-pattern/abstract-factory) 49 | + [**Builder**](./creational-pattern/builder) 50 | + [**Prototype**](./creational-pattern/prototype) 51 | + [**Singleton**](./creational-pattern/singleton) 52 | - [**Structural Pattern**](./structural-pattern) giải thích cách tập hợp các đối tượng và lớp thành các cấu trúc lớn hơn, trong khi vẫn giữ cho cấu trúc linh hoạt và hiệu quả. 53 | + [**Adapter**](./structural-pattern/adapter) 54 | + [**Bridge**](./structural-pattern/bridge) 55 | + [**Composite**](./structural-pattern/composite) 56 | + [**Decorator**](./structural-pattern/decorator) 57 | + [**Facade**](./structural-pattern/facade) 58 | + [**Flyweight**](./structural-pattern/flyweight) 59 | + [**Proxy**](./structural-pattern/proxy) 60 | - [**Behavioral Pattern**](./behavioral-pattern) quan tâm đến việc giao tiếp hiệu quả và phân công nhiệm vụ giữa các đối tượng. 61 | + [**Chain Of Responsibility**](./behavioral-pattern/chain-of-responsibility) 62 | + [**Command**](./behavioral-pattern/command) 63 | + [**Iterator**](./behavioral-pattern/iterator) 64 | + [**Mediator**](./behavioral-pattern/mediator) 65 | + [**Memento**](./behavioral-pattern/memento) 66 | + [**Observer**](./behavioral-pattern/observer) 67 | + [**State**](./behavioral-pattern/state) 68 | + [**Strategy**](./behavioral-pattern/strategy) 69 | + [**Template Method**](./behavioral-pattern/template-method) 70 | + [**Visitor**](./behavioral-pattern/visitor) 71 | 72 | # Code Example 73 | 74 | Code ví dụ của RefactoringGuru 75 | 76 | | | Ngôn ngữ | Source Code | 77 | |-|----------|-------------| 78 | |![csharp](./assets/csharp.png)| C# | https://github.com/RefactoringGuru/design-patterns-csharp | 79 | |![cpp](./assets/cpp.png)| C++ | https://github.com/RefactoringGuru/design-patterns-cpp | 80 | |![go](./assets/go.png)| GO | https://github.com/RefactoringGuru/design-patterns-go | 81 | |![java](./assets/java.png)| JAVA | https://github.com/RefactoringGuru/design-patterns-java | 82 | |![php](./assets/php.png)| PHP | https://github.com/RefactoringGuru/design-patterns-php | 83 | |![python](./assets/python.png)| PYTHON | https://github.com/RefactoringGuru/design-patterns-python | 84 | |![ruby](./assets/ruby.png)| RUBY | https://github.com/RefactoringGuru/design-patterns-ruby | 85 | |![swift](./assets/swift.png)| SWIFT | https://github.com/RefactoringGuru/design-patterns-swift | 86 | |![typescript](./assets/typescript.png)| TYPESCRIPT | https://github.com/RefactoringGuru/design-patterns-typescript | 87 | 88 | 89 | # Nguồn 90 | 91 | ## Sách 92 | 93 | ![book](./assets/book.png) 94 | 95 | ## Tác giả: [Refactoring.Guru](https://refactoring.guru) 96 | 97 | ![author](./assets/author.png) -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-tactile -------------------------------------------------------------------------------- /assets/author.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/assets/author.png -------------------------------------------------------------------------------- /assets/book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/assets/book.png -------------------------------------------------------------------------------- /assets/cpp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/assets/cpp.png -------------------------------------------------------------------------------- /assets/csharp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/assets/csharp.png -------------------------------------------------------------------------------- /assets/design-patterns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/assets/design-patterns.png -------------------------------------------------------------------------------- /assets/go.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/assets/go.png -------------------------------------------------------------------------------- /assets/java.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/assets/java.png -------------------------------------------------------------------------------- /assets/php.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/assets/php.png -------------------------------------------------------------------------------- /assets/python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/assets/python.png -------------------------------------------------------------------------------- /assets/ruby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/assets/ruby.png -------------------------------------------------------------------------------- /assets/swift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/assets/swift.png -------------------------------------------------------------------------------- /assets/typescript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/assets/typescript.png -------------------------------------------------------------------------------- /behavioral-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Behavioral Design Pattern 2 | 3 | Behavioral Pattern quan tâm đến việc giao tiếp hiệu quả và phân công nhiệm vụ giữa các đối tượng. 4 | 5 | ## Chain of Responsibility 6 | 7 | [![chain-of-responsibility](./assets/chain-of-responsibility-mini.png)](./chain-of-responsibility) 8 | 9 | Cho phép bạn truyền các yêu cầu dọc theo một chuỗi xử lý. Khi nhận được yêu cầu, mỗi trình xử lý sẽ quyết định xử lý yêu cầu hoặc truyền nó cho trình xử lý tiếp theo trong chuỗi. 10 | 11 | ## Command 12 | 13 | [![command](./assets/command-mini.png)](./command) 14 | 15 | Biến một yêu cầu thành một đối tượng độc lập bao gồm tất cả thông tin của yêu cầu đó. Chuyển đổi này giúp bạn truyền các yêu cầu dưới dạng tham số của phương thức, trì hoãn hoặc chờ đợi việc thực thi một yêu cầu hay hỗ trợ các hoạt động hoàn tác. 16 | 17 | ## Iterator 18 | 19 | [![iterator](./assets/iterator-mini.png)](./iterator) 20 | 21 | Duyệt phần tử của một tập hợp mà không để lộ dạng cơ bản của nó (danh sách, ngăn xếp, cây, ...) 22 | 23 | ## Mediator 24 | 25 | [![mediator](./assets/mediator-mini.png)](./mediator) 26 | 27 | Giúp bạn giảm các phụ thuộc hỗn tạp giữa các đối tượng. Pattern hạn chế các giao tiếp trực tiếp giữa các đối tượng và buộc nó giao tiếp thông qua đối tượng mediator. 28 | 29 | ## Memento 30 | 31 | [![memento](./assets/memento-mini.png)](./memento) 32 | 33 | Lưu và phục hồi trạng thái trước đó của một đối tượng mà không để lộ chi tiết triển khai của nó 34 | 35 | ## Observer 36 | 37 | [![observer](./assets/observer-mini.png)](./observer) 38 | 39 | Giúp bạn định nghĩa một cơ chế đăng ký để thông báo cho nhiều đối tượng về bất kỳ sự kiện nào diễn ra với đối tượng mà chúng đang quan sát. 40 | 41 | ## State 42 | 43 | [![state](./assets/state-mini.png)](./state) 44 | 45 | Chỉnh sửa hành vi của một đối tượng khi trạng thái bên trong nó thay đổi. Nó xảy ra nếu như một đối tượng thay đổi lớp của nó. 46 | 47 | ## Strategy 48 | 49 | [![strategy](./assets/strategy-mini.png)](./strategy) 50 | 51 | Giúp bạn xác định một nhóm thuật toán, đặt chúng vào một lớp riêng biệt và làm cho các đối tượng của chúng có thể hoán đổi lẫn nhau. 52 | 53 | ## Template Method 54 | 55 | [![template-method](./assets/template-method-mini.png)](./template-method) 56 | 57 | Định nghĩa bộ khung của thuật toán ở lớp cha (superclass) nhưng các lớp con (subsclasses) có thể ghi đè lên các bước cụ thể của thuật toán mà không làm thay đổi cấu trúc của nó. 58 | 59 | ## Visitor 60 | 61 | [![visitor](./assets/visitor-mini.png)](./visitor) 62 | 63 | Tách các thuật toán khỏi đối tượng mà chúng đang hoạt động trên đó. 64 | -------------------------------------------------------------------------------- /behavioral-pattern/assets/chain-of-responsibility-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/assets/chain-of-responsibility-mini.png -------------------------------------------------------------------------------- /behavioral-pattern/assets/command-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/assets/command-mini.png -------------------------------------------------------------------------------- /behavioral-pattern/assets/iterator-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/assets/iterator-mini.png -------------------------------------------------------------------------------- /behavioral-pattern/assets/mediator-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/assets/mediator-mini.png -------------------------------------------------------------------------------- /behavioral-pattern/assets/memento-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/assets/memento-mini.png -------------------------------------------------------------------------------- /behavioral-pattern/assets/observer-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/assets/observer-mini.png -------------------------------------------------------------------------------- /behavioral-pattern/assets/state-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/assets/state-mini.png -------------------------------------------------------------------------------- /behavioral-pattern/assets/strategy-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/assets/strategy-mini.png -------------------------------------------------------------------------------- /behavioral-pattern/assets/template-method-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/assets/template-method-mini.png -------------------------------------------------------------------------------- /behavioral-pattern/assets/visitor-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/assets/visitor-mini.png -------------------------------------------------------------------------------- /behavioral-pattern/chain-of-responsibility/assets/analogy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/chain-of-responsibility/assets/analogy.png -------------------------------------------------------------------------------- /behavioral-pattern/chain-of-responsibility/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/chain-of-responsibility/assets/intent.png -------------------------------------------------------------------------------- /behavioral-pattern/chain-of-responsibility/assets/problem1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/chain-of-responsibility/assets/problem1.png -------------------------------------------------------------------------------- /behavioral-pattern/chain-of-responsibility/assets/problem2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/chain-of-responsibility/assets/problem2.png -------------------------------------------------------------------------------- /behavioral-pattern/chain-of-responsibility/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/chain-of-responsibility/assets/pseudocode.png -------------------------------------------------------------------------------- /behavioral-pattern/chain-of-responsibility/assets/pseudocode2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/chain-of-responsibility/assets/pseudocode2.png -------------------------------------------------------------------------------- /behavioral-pattern/chain-of-responsibility/assets/solution1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/chain-of-responsibility/assets/solution1.png -------------------------------------------------------------------------------- /behavioral-pattern/chain-of-responsibility/assets/solution2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/chain-of-responsibility/assets/solution2.png -------------------------------------------------------------------------------- /behavioral-pattern/chain-of-responsibility/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/chain-of-responsibility/assets/structure.png -------------------------------------------------------------------------------- /behavioral-pattern/command/assets/analogy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/command/assets/analogy.png -------------------------------------------------------------------------------- /behavioral-pattern/command/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/command/assets/intent.png -------------------------------------------------------------------------------- /behavioral-pattern/command/assets/problem1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/command/assets/problem1.png -------------------------------------------------------------------------------- /behavioral-pattern/command/assets/problem2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/command/assets/problem2.png -------------------------------------------------------------------------------- /behavioral-pattern/command/assets/problem3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/command/assets/problem3.png -------------------------------------------------------------------------------- /behavioral-pattern/command/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/command/assets/pseudocode.png -------------------------------------------------------------------------------- /behavioral-pattern/command/assets/solution1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/command/assets/solution1.png -------------------------------------------------------------------------------- /behavioral-pattern/command/assets/solution2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/command/assets/solution2.png -------------------------------------------------------------------------------- /behavioral-pattern/command/assets/solution3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/command/assets/solution3.png -------------------------------------------------------------------------------- /behavioral-pattern/command/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/command/assets/structure.png -------------------------------------------------------------------------------- /behavioral-pattern/iterator/assets/analogy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/iterator/assets/analogy.png -------------------------------------------------------------------------------- /behavioral-pattern/iterator/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/iterator/assets/intent.png -------------------------------------------------------------------------------- /behavioral-pattern/iterator/assets/problem1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/iterator/assets/problem1.png -------------------------------------------------------------------------------- /behavioral-pattern/iterator/assets/problem2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/iterator/assets/problem2.png -------------------------------------------------------------------------------- /behavioral-pattern/iterator/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/iterator/assets/pseudocode.png -------------------------------------------------------------------------------- /behavioral-pattern/iterator/assets/solution1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/iterator/assets/solution1.png -------------------------------------------------------------------------------- /behavioral-pattern/iterator/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/iterator/assets/structure.png -------------------------------------------------------------------------------- /behavioral-pattern/mediator/assets/analogy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/mediator/assets/analogy.png -------------------------------------------------------------------------------- /behavioral-pattern/mediator/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/mediator/assets/intent.png -------------------------------------------------------------------------------- /behavioral-pattern/mediator/assets/problem1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/mediator/assets/problem1.png -------------------------------------------------------------------------------- /behavioral-pattern/mediator/assets/problem2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/mediator/assets/problem2.png -------------------------------------------------------------------------------- /behavioral-pattern/mediator/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/mediator/assets/pseudocode.png -------------------------------------------------------------------------------- /behavioral-pattern/mediator/assets/solution1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/mediator/assets/solution1.png -------------------------------------------------------------------------------- /behavioral-pattern/mediator/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/mediator/assets/structure.png -------------------------------------------------------------------------------- /behavioral-pattern/memento/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/memento/assets/intent.png -------------------------------------------------------------------------------- /behavioral-pattern/memento/assets/problem1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/memento/assets/problem1.png -------------------------------------------------------------------------------- /behavioral-pattern/memento/assets/problem2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/memento/assets/problem2.png -------------------------------------------------------------------------------- /behavioral-pattern/memento/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/memento/assets/pseudocode.png -------------------------------------------------------------------------------- /behavioral-pattern/memento/assets/solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/memento/assets/solution.png -------------------------------------------------------------------------------- /behavioral-pattern/memento/assets/structure1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/memento/assets/structure1.png -------------------------------------------------------------------------------- /behavioral-pattern/memento/assets/structure2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/memento/assets/structure2.png -------------------------------------------------------------------------------- /behavioral-pattern/memento/assets/structure3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/memento/assets/structure3.png -------------------------------------------------------------------------------- /behavioral-pattern/observer/README.md: -------------------------------------------------------------------------------- 1 | # Observer 2 | 3 | ## 📜 Mục đích 4 | 5 | Observer là một design pattern thuộc nhóm behavioral giúp bạn định nghĩa một cơ chế đăng ký để thông báo cho nhiều đối tượng về bất kỳ sự kiện nào diễn ra với đối tượng mà chúng đang quan sát. 6 | 7 | ![intent](./assets/intent.png) 8 | 9 | ## 😟 Vấn đề 10 | 11 | Tưởng tượng bạn có hai kiểu đối tượng: `Customer` và `Store`. 12 | 13 | Khách hàng thường sẽ bị hấp dẫn với một vài thương hiệu sản phẩm nổi bật, ví dụ như mẫu iPhone mới sẽ sớm được bán ở của hàng. Thế nên họ sẽ đến cửa hàng mỗi ngày để kiểm tra sản phẩm đã có bán chưa. Nhưng nếu sản phẩm vẫn chưa nhập về, thì phần lớn công sức của họ cho mỗi chuyến đi sẽ vô nghĩa. 14 | 15 | ![problem](./assets/problem.png) 16 | 17 | Thế nên, cửa hàng sẽ gửi hàng tấn mail (có thể là spam) cho tất cả khách hàng mỗi lần có sản phẩm mới. Điều này giúp khách hàng tránh những chuyến đi vô nghĩa đến cửa hàng. Nhưng bù lại, nó sẽ làm cho các khách hàng khác (những người không có hứng thú với sản phẩm mới) khó chịu. 18 | 19 | Có vẻ như chúng ta đã xảy ra xung đột. Hoặc khách hàng lãng phí thời gian kiểm tra sản phẩm đã bán chưa hoặc cửa hàng lãng phí nguồn lực để thông báo thừa cho khách hàng. 20 | 21 | ## 😊 Giải pháp 22 | 23 | Đối tượng có trạng thái hấp dẫn thường được gọi là subject(chủ thể), nhưng vì nó cũng sẽ thông báo cho các đối tượng khác về những thay đổi đối với trạng thái của nó, nên ta sẽ gọi nó là **publisher**. Tất cả đối tượng khác muốn theo dõi trạng thái của publisher được gọi là **subscriber**. 24 | 25 | Pattern Observer đề nghị giải pháp là bạn thêm một cơ chế đăng ký đến lớp publisher để các đối tượng riêng biệt có thể đăng ký hoặc huỷ đăng ký một dòng các sự kiện xảy đến từ publisher. Thực tế, cơ chế này bao gồm một trường mảng cho lưu trữ danh sách tham chiếu đến đối tượng subscriber và nhiều phương thức công khai cho phép thêm hay xoá subscriber khỏi danh sách. 26 | 27 | ![solution1](./assets/solution1.png) 28 | 29 | Bây giờ, bất cứ khi nào có sự kiện quan trọng diễn ra với publisher, nó đi qua tất cả subscriber và gọi phương thức thông báo cụ thể trên đối tượng của chúng. 30 | 31 | Ứng dụng thực có thể có hàng tá lớp subscriber khác nhau quan tâm đến việc theo dõi các sự kiện của cùng một lớp publisher. Bạn sẽ không muốn ghép publiser với tất cả các lớp đó. Bên cạnh đó, bạn thậm chí có thể không biết về một số trong số chúng trước nếu lớp publisher của bạn được người khác sử dụng. 32 | 33 | Đó là lý do tại sao cho việc tất cả subscriber phải triển khai cùng một interface và publisher chỉ giao tiếp với chúng qua interface đó. Interface này phải khai báo phương thức thông báo cùng với một tập hợp các tham số mà publisher có thể sử dụng để chuyển một số dữ liệu ngữ cảnh cùng với thông báo. 34 | 35 | ![solution2](./assets/solution2.png) 36 | 37 | Nếu ứng dụng của bạn có nhiều kiểu publisher khác nhau và bạn muốn làm cho subscriber của mình tương thích với tất cả chúng, bạn có thể tiến xa hơn nữa và khiến tất cả các publisher tuân theo cùng một interface. Interface này sẽ chỉ cần mô tả một số phương pháp đăng ký. Interface sẽ cho phép subscriber quan sát trạng thái của publisher mà không cần kết nối đến các lớp cụ thể của chúng. 38 | 39 | ## 🚗 Thế Giới Thực 40 | 41 | ![analogy](./assets/analogy.png) 42 | 43 | Nếu bạn đăng ký một tờ báo hoặc tạp chí, bạn không cần phải đến cửa hàng để kiểm tra xem số tiếp theo có sẵn hay không. Thay vào đó, nhà xuất bản gửi các số báo mới trực tiếp đến hộp thư của bạn ngay sau khi xuất bản hoặc thậm chí trước. 44 | 45 | Nhà xuất bản duy trì danh sách người đăng ký và biết họ quan tâm đến tạp chí nào. Người đăng ký có thể rời khỏi danh sách bất kỳ lúc nào khi họ muốn ngăn nhà xuất bản gửi các số tạp chí mới cho họ. 46 | 47 | ## 🏢 Cấu trúc 48 | 49 | ![structure](./assets/structure.png) 50 | 51 | 1. **Publisher** phát hành các sự kiện mà các đối tượng khác quan tâm. Các sự kiện này diễn ra khi publisher thay đổi trạng thái của nó hoặc thực thi một vài hành vi. Publisher bao gồm một kết cấu đăng ký cho phép subscriber mới tham gia hay subscriber hiện tại rời khỏi danh sách. 52 | 2. Khi một sự kiện mới diễn ra, publisher sẽ đi qua danh sách đăng ký và gọi phương thức thông báo được khai báo ở interface subscriber cho từng đối tượng subscriber. 53 | 3. **Subscriber** là interface khai báo interface thông báo. Trong hầu hết trường hợp, nó bao gồm một phương thức cập nhật duy nhất. Phương thức này có nhiều tham số giúp publisher truyền một vài chi tiết sự kiện cùng với cập nhật. 54 | 4. **Concrete Subscriber** thực hiện một vài hành động phản hồi lại thông báo được phát hành bởi publisher. Tất cả các lớp này phải triển khai cùng interface thế nên publisher không cần ghép với lớp cụ thể. 55 | 5. Thông thường, subscriber cần một vài thông tin ngữ cảnh để xử lý cập nhật chính xác. Vì lý do này, publisher truyền một vài dữ liệu ngữ cảnh như các tham số cho phương thức thông báo. Publisher có thể truyền chính bản thân nó như một tham số, để subscriber có thể nạp bất kỳ dữ liệu cần thiết nào trực tiếp. 56 | 6. **Client** tạo đối tượng publisher và subscriber riêng biệt, sau đó subscriber đăng ký các bản cập nhật publisher. 57 | 58 | ## 👨‍💻 Mã giả 59 | 60 | Trong ví dụ này, Observer giúp trình soạn thảo thông báo cho các đối tượng dịch vụ về các thay đổi trạng thái. 61 | 62 | ![pseudocode](./assets/pseudocode.png) 63 | 64 | Danh sách các subscriber được biên dịch động: đối tượng có thể bắt đầu hoặc kết thúc lắng nghe thông báo khi đang chạy, dựa trên hành vi mong muốn của ứng dụng. 65 | 66 | Trong triển khai này, lớp soạn thảo không duy trì danh sách đăng ký chính nó. Nó uỷ thác công việc cho đối tượng hỗ trợ đặc biệt làm điều đó. Bạn có thể nâng cấp đối tượng này để phục vụ như một trung tâm điều phối sự kiện, giúp bất kỳ đối tượng nào cũng hành động như một publisher. 67 | 68 | Thêm các subscriber mới vào chương trình không yêu cầu thay đổi lớp publisher hiện có, miễn là chúng làm việc với subscriber thông qua cùng interface. 69 | 70 | ```c 71 | // Lớp publisher cơ sở bao gồm code quản lý đăng ký 72 | // và phương thức thông báo. 73 | class EventManager is 74 | private field listeners: hash map of event types and listeners 75 | 76 | method subscribe(eventType, listener) is 77 | listeners.add(eventType, listener) 78 | 79 | method unsubscribe(eventType, listener) is 80 | listeners.remove(eventType, listener) 81 | 82 | method notify(eventType, data) is 83 | foreach (listener in listeners.of(eventType)) do 84 | listener.update(data) 85 | 86 | // Publisher cụ thể bao gồm logic nghiệp vụ thực hấp dẫn một vài 87 | // subscriber. Ta có thể có dẫn xuất lớp này từ một publisher cơ 88 | // sở, nhưng điều này không luôn khả thi vì thực tế publisher cụ 89 | // thể sẽ có lớp con. Trong trường hợp này, ta có thể vá logic 90 | // đăng ký với composition. 91 | class Editor is 92 | public field events: EventManager 93 | private field file: File 94 | 95 | constructor Editor() is 96 | events = new EventManager() 97 | 98 | // Phương thức logic nghiệp vụ có thể thông báo subscriber 99 | // về các thay đổi. 100 | method openFile(path) is 101 | this.file = new File(path) 102 | events.notify("open", file.name) 103 | 104 | method saveFile() is 105 | file.write() 106 | events.notify("save", file.name) 107 | 108 | // ... 109 | 110 | 111 | // Đây là interface subscriber. Nếu ngôn ngữ lập trình của bạn 112 | // hỗ trợ kiểu function, bạn có thể thay thay toàn bộ hệ thống 113 | // phân cấp subscriber với một tập hợp function. 114 | interface EventListener is 115 | method update(filename) 116 | 117 | 118 | // Subscriber cụ thể phản ứng với bản cập nhật được phát hành 119 | // bởi publisher mà chúng được gắn. 120 | class LoggingListener implements EventListener is 121 | private field log: File 122 | private field message: string 123 | 124 | constructor LoggingListener(log_filename, message) is 125 | this.log = new File(log_filename) 126 | this.message = message 127 | 128 | method update(filename) is 129 | log.write(replace('%s',filename,message)) 130 | 131 | class EmailAlertsListener implements EventListener is 132 | private field email: string 133 | private field message: string 134 | 135 | constructor EmailAlertsListener(email, message) is 136 | this.email = email 137 | this.message = message 138 | 139 | method update(filename) is 140 | system.email(email, replace('%s',filename,message)) 141 | 142 | 143 | // Ứng dụng có thể cấu hình publisher và subscriber khi đang chạy 144 | class Application is 145 | method config() is 146 | editor = new Editor() 147 | 148 | logger = new LoggingListener( 149 | "/path/to/log.txt", 150 | "Someone has opened the file: %s") 151 | editor.events.subscribe("open", logger) 152 | 153 | emailAlerts = new EmailAlertsListener( 154 | "admin@example.com", 155 | "Someone has changed the file: %s") 156 | editor.events.subscribe("save", emailAlerts) 157 | ``` 158 | 159 | ## 💡 Ứng dụng 160 | 161 | **🐞 Sử dụng Observer khi thay đổi trạng thái của một đối tượng có thể yêu cầu thay đổi đối tượng khác, và tập hợp thực của đối tượng là không biết trước hoặc có thể thay đổi động** 162 | 163 | ⚡ Bạn có thể gặp vấn đề này khi làm việc với lớp giao diện người dùng. Ví dụ, bạn tạo nhiều lớp button tuỳ chỉnh, và bạn muốn client kết nối với một vài tuỳ chỉnh code button của bạn để nó kích hoạt bất cứ khi nào người dùng click. 164 | 165 | Observer giúp bất kỳ đối tượng nào triển khai interface subscriber đăng ký nhận thông báo sự kiện ở đối tượng publisher. Bạn có thể thêm cơ chế subscription cho button của bạn, giúp client kết nối với code tuỳ chỉnh thông qua lớp subscriber tuỳ chỉnh. 166 | 167 | **🐞 Sử dụng Observer khi một vài đối tượng trong ứng dụng phải quan sát đối tượng khác, nhưng chỉ giới hạn thời gian và trường hợp cụ thể** 168 | 169 | ⚡ Danh sách đăng ký là động, nên subscriber có thể tham gia hoặc rời danh sách khi chúng cần. 170 | 171 | ## 📋 Triển khai 172 | 173 | 1. Nhìn qua logic nghiệp vụ và chia nó làm hai phần: phần chức năng cốt lỗi độc lập với các phần khác, sẽ hành động như publisher. Phần còn lại sẽ là tập hợp lớp subscriber. 174 | 175 | 2. Khai báo interface subscriber. Ở mức tối thiếu, nó nên có một phương thức cập nhật duy nhất. 176 | 3. Khai báo interface publisher và mô tả một cặp phương thức cho thêm và xoá đối tượng subscriber khỏi danh sách. Hãy nhớ publisher phải làm việc với subscriber thông qua interface subscriber. 177 | 4. Quyết định nơi đặt danh sách đăng ký và triển khai phương thức đăng ký. Thông thường, code này như nhau với tất cả kiểu publisher, thế nên nơi rõ ràng để đặt nó là lớp trừu tượng được lấy trực tiếp từ interface publisher. Publisher cụ thể mở rộng từ lớp này, kế thừa các hành vi đăng ký. 178 | 179 | Tuy nhiên, nếu bạn áp dụng pattern với hệ phân cấp lớp hiện có, bao gồm cách tiếp cận dựa trên composition: đặt logic đăng ký vào đối tượng riêng biệt, và để cho tất cả publisher sử dụng nó. 180 | 5. Tạo lớp publisher cụ thể. Mỗi lần điều gì diễn ra trong publisher, sẽ phải thông báo cho tất cả subscriber. 181 | 182 | 6. Triển khai phương thức thông báo cập nhật ở lớp subscriber cụ thể. Hầu hết subscriber sẽ cần dữ liệu ngữ cảnh về sự kiện. Nó có thể được truyền như một tham số cho phương thức thông báo. 183 | 184 | Nhưng ở đây ta có lựa chọn khác. Khi nhận thông báo, subscriber có thể tìm nạp dữ liệu trực tiếp từ thông báo. Trong trường hợp này, publisher phải truyền bản thân nó thông qua phương thức cập nhật. Các lựa chọn kém linh hoạt hơn là liên kết publisher với subscriber vĩnh viễn qua hàm khởi tạo. 185 | 186 | 7. Client phải tạo subscriber cần thiết và đăng ký nó với publisher phù hợp. 187 | 188 | ## ⚖️ Ưu nhược điểm 189 | 190 | ### Ưu điểm 191 | 192 | ✔️ *Open/Closed Principle*. Bạn có thể thêm lớp subscriber mới mà không ảnh hưởng đến code publisher (và ngược lại nếu có interface publisher). 193 | 194 | ✔️ Bạn có thể thiết lập quan hệ giữa các đối tượng khi đang chạy. 195 | 196 | ### Nhược điểm 197 | 198 | ❌ Subscriber được thông báo theo thứ tự ngẫu nhiên. 199 | 200 | ## 🔁 Quan hệ với các pattern khác 201 | 202 | **Chain of Responsibility**, **Command**, **Mediator** và **Observer** giải quyết các cách khác nhau để kết nối người gửi và người nhận yêu cầu: 203 | 204 | - **CoR** chuyển một yêu cầu tuần tự dọc theo một chuỗi động gồm những người nhận tiềm năng cho đến khi một trong số họ xử lý nó. 205 | - **Command** thiết lập các kết nối một chiều giữa người gửi và người nhận. 206 | - **Mediator** loại bỏ các kết nối trực tiếp giữa người gửi và người nhận, buộc họ phải giao tiếp gián tiếp thông qua một đối tượng trung gian. 207 | - **Observer** cho phép người nhận đăng ký động và hủy đăng ký nhận yêu cầu. 208 | 209 | Sự khác biệt giữa **Mediator** và **Observer** thường khó nắm bắt. Trong hầu hết các trường hợp, bạn có thể triển khai một trong các pattern này; nhưng đôi khi bạn có thể áp dụng đồng thời cả hai. Hãy xem chúng ta có thể làm điều đó như thế nào. 210 | 211 | - Mục tiêu chính của **Mediator** là loại bỏ sự phụ thuộc lẫn nhau giữa một tập hợp các thành phần hệ thống. Thay vào đó, các thành phần này trở nên phụ thuộc vào một đối tượng mediator duy nhất. Mục tiêu của **Observer** là thiết lập các kết nối động một chiều giữa các đối tượng, nơi một số đối tượng đóng vai trò là cấp dưới của những đối tượng khác. 212 | 213 | - Có một cách triển khai phổ biến của **Mediator** vào **Observer**. Đối tượng mediator đóng vai trò là publisher và các thành phần đóng vai trò là subscribers, đăng ký và hủy đăng ký tham gia các sự kiện của mediator. Khi **Mediator** được triển khai theo cách này, nó có thể trông rất giống với **Observer**. 214 | 215 | - Nếu thấy bối rối, hãy nhớ rằng bạn có thể triển khai **Mediator** theo những cách khác. Ví dụ: bạn có thể liên kết vĩnh viễn tất cả các thành phần với cùng một đối tượng mediator. Việc triển khai này sẽ không giống với **Observer** nhưng vẫn sẽ là một bản sao của **Mediator**. 216 | 217 | - Bây giờ hãy tưởng tượng một chương trình mà tất cả các thành phần đã trở thành publisher, cho phép các kết nối động với nhau. Sẽ không có đối tượng mediator tập trung, chỉ có một nhóm observer phân tán. 218 | 219 | # Nguồn 220 | 221 | [refactoring](https://refactoring.guru/design-patterns/observer) 222 | -------------------------------------------------------------------------------- /behavioral-pattern/observer/assets/analogy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/observer/assets/analogy.png -------------------------------------------------------------------------------- /behavioral-pattern/observer/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/observer/assets/intent.png -------------------------------------------------------------------------------- /behavioral-pattern/observer/assets/problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/observer/assets/problem.png -------------------------------------------------------------------------------- /behavioral-pattern/observer/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/observer/assets/pseudocode.png -------------------------------------------------------------------------------- /behavioral-pattern/observer/assets/solution1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/observer/assets/solution1.png -------------------------------------------------------------------------------- /behavioral-pattern/observer/assets/solution2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/observer/assets/solution2.png -------------------------------------------------------------------------------- /behavioral-pattern/observer/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/observer/assets/structure.png -------------------------------------------------------------------------------- /behavioral-pattern/state/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/state/assets/intent.png -------------------------------------------------------------------------------- /behavioral-pattern/state/assets/problem1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/state/assets/problem1.png -------------------------------------------------------------------------------- /behavioral-pattern/state/assets/problem2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/state/assets/problem2.png -------------------------------------------------------------------------------- /behavioral-pattern/state/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/state/assets/pseudocode.png -------------------------------------------------------------------------------- /behavioral-pattern/state/assets/solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/state/assets/solution.png -------------------------------------------------------------------------------- /behavioral-pattern/state/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/state/assets/structure.png -------------------------------------------------------------------------------- /behavioral-pattern/strategy/README.md: -------------------------------------------------------------------------------- 1 | # Strategy 2 | 3 | ## 📜 Mục đích 4 | 5 | **Strategy** là một design pattern thuộc nhóm behavioral giúp bạn xác định một nhóm thuật toán, đặt chúng vào một lớp riêng biệt và làm cho các đối tượng của chúng có thể hoán đổi lẫn nhau. 6 | 7 | ![intent](./assets/intent.png) 8 | 9 | ## 😟 Vấn đề 10 | 11 | Vào một ngày đẹp trời, bạn định tạo một ứng dụng chỉ đường cho các khách du lịch. Ứng dụng xoay quanh các bản đồ đẹp mắt giúp người dùng dễ dàng đi đến bất cứ thành phố nào. 12 | 13 | Phần lớn chức năng yêu cầu của ứng dụng là tự thiết lập lộ trình đường đi. Người dùng sẽ nhập vào địa chỉ hiện tại của họ và thấy con đường nhanh nhất để đến đích trên bản đồ. 14 | 15 | Phiên bản đầu tiên của ứng dụng chỉ tập trung vào những đại lộ. Những người du lịch bằng xe sẽ cảm thấy vui sướng vì điều này. Nhưng mà rõ ràng là, không phải tất cả mọi người đều du lịch bằng xe. Thế nên ở bản cập nhật tiếp theo, bạn thêm tính năng chức năng cho người đi bộ. Ngay sau đó, bạn thêm các lựa chọn khác cho những phương tiện công cộng(bus, tàu điện ngầm,..) trên tuyến đường của họ. 16 | 17 | Tuy nhiên, mọi thứ vẫn chưa dừng lại. Sau đó bạn định thêm lộ trình cho người đi xe đạp, hay là về sau này bạn sẽ thêm các lựa chọn khác cho xây dựng các lộ trình qua tất cả điểm tham quan trong thành phố. 18 | 19 | ![problem](./assets/problem.png) 20 | 21 | Từ quan điểm kinh doanh, ứng dụng của bạn đã thành công, nhưng ở khía cạnh kỹ thuật bạn sẽ gặp nhiều vấn đề đau đầu. Mỗi lần bạn thêm một thuật toán chỉ đường mới, lớp chính của bộ chỉ đường sẽ gấp đôi kích thước. Và đến một thời điểm nào đó, nó sẽ như là một con quái vật, cực kỳ khó cho việc bảo trì. 22 | 23 | Bất kỳ một thuật toán nào thay đổi, cho dù chỉ là fix lỗi đơn giản hay một chút điều chỉnh lên các con đường nó cũng ảnh hưởng đến toàn bộ lớp, làm tăng nguy cơ sinh lỗi ở các đoạn code đã hoạt động. 24 | 25 | Bên cạnh đó, teamwork cũng bất tiện hơn. Các đồng nghiệp của bạn, những người gia nhập sau khi phiên bản đầu tiên phát hành sẽ than phiền rằng họ mất quá nhiều thời gian cho giải quyết các xung đột khi hợp nhất. Triển khai tính năng mới yêu cầu thay đổi cùng một lớp khổng lồ, xung đột với code được viết bởi những người khác. 26 | 27 | ## 😊 Giải pháp 28 | 29 | Strategy đề xuất giải pháp là bạn nên chọn một lớp làm điều gì đó cụ thể theo nhiều cách khác nhau và trích xuất tất cả thuật toán vào các lớp riêng biệt đấy gọi là *strategy*. 30 | 31 | Lớp gốc gọi là context, phải có một trường lưu trữ tham chiếu đến một trong các stategy. Context uỷ thác công việc cho đối tượng strategy được liên kết thay vì tự thực hiện nó. 32 | 33 | Context không có trách nhiệm chọn thuật toán phù hợp cho công việc. Thay vào đó, client truyền strategy mong muốn đến context. Thực tế, context không biết gì về strategy. Nó làm việc với mọi strategy thông qua interface chung, nó chỉ để lộ một phương thức duy nhất cho kích hoạt thuật toán đã đóng gói trong stategy được chọn. 34 | 35 | Với cách này, context trở nên độc lập với các strategy cụ thể, bạn có thể thêm hay chỉnh sửa thuật toán mà không ảnh hưởng gì đến code của context hay các strategy khác. 36 | 37 | ![solution](./assets/solution.png) 38 | 39 | Trở lại với ứng dụng chỉ đường, mội thuật toán định tuyến có thể được trích xuất vào lớp của chúng với phương thức `buildRoute` duy nhất. Phương thức nhận vào điểm đầu và đích đến, và trả về một tập hợp các trạm dừng của lộ trình. 40 | 41 | Mặc dùng cho cùng một tham số, mỗi lớp định tuyến sẽ có tạo một lộ trình khác nhau, lớp chính của ứng dụng không thực sự quan tâm thuật toán được chọn vì công việc chính của nó chỉ là hiển thị các trạm dừng trên bản đồ. Lớp có phương thức chuyển đổi các lịch trình đang hoạt động, thế nên người dùng với các button ở giao diện người dùng, có thể thay thế hành vi được chọn hiện tại với cái khác. 42 | 43 | ## 🚗 Thế Giới Thực 44 | 45 | ![analogy](./assets/analogy.png) 46 | 47 | Tưởng tượng để đi đến sân bay. Bạn có thể bắt xe bus, gọi taxi hay đi xe đập. Các phương tiện của bạn là strategy. Bạn có thể chọn một trong các strategy dựa vào các nhân tố như ví tiền hay thời gian. 48 | 49 | ## 🏢 Cấu trúc 50 | 51 | ![structure](./assets/structure.png) 52 | 53 | 1. **Context** duy trì một tham chiếu đến một trong các strategy cụ thể và giao tiếp với các đối tượng này thông qua interface strategy. 54 | 2. **Strategy** là interface chung cho tất cả strategy cụ thể. Nó khai báo một phương thức duy nhất cho context sử dụng để thực thi. 55 | 3. **Concrete Strategies** triển khai khác nhau của thuật toán mà context sử dụng. 56 | 4. Context gọi phương thức thực thi đến đối tượng strategy được liên kết mỗi lần nó cần chạy thuật toán. Context không cần biết chính xác kiểu strategy nào đang làm việc và thuật toán được thực thi thế nào. 57 | 5. **Client** tạo đối tượng strategy cụ thể và truyền nó vào context. Context để lộ một setter cho client thay thế với strategy được liên kết với context khi đang chạy. 58 | 59 | ## 👨‍💻 Mã giả 60 | 61 | Trong ví dụ này, context sử dụng nhiều strategy để thực hiện các phép toán khác nhau. 62 | 63 | ```c 64 | // Interface strategy khai báo các phép toán chung cho tất cả 65 | // phiên bản hỗ trợ của một vài thuật toán. Context sử dụng 66 | // interface này để gọi thuật toán đã xác định bởi concrete 67 | // strategies. 68 | interface Strategy is 69 | method execute(a, b) 70 | 71 | // Concrete strategies triển khai thuật toán khi đang theo 72 | // interface strategy cơ sở. Interface hoán đổi chúng với 73 | // nhau trong context. 74 | class ConcreteStrategyAdd implements Strategy is 75 | method execute(a, b) is 76 | return a + b 77 | 78 | class ConcreteStrategySubtract implements Strategy is 79 | method execute(a, b) is 80 | return a - b 81 | 82 | class ConcreteStrategyMultiply implements Strategy is 83 | method execute(a, b) is 84 | return a * b 85 | 86 | // Context xác định interface mà client mong muốn. 87 | class Context is 88 | // Context duy trì một tham chiếu đến một trong các đối tượng 89 | // strategy. Context không biết rõ lớp cụ thể của strategy. 90 | // Nó làm việc với mọi strategy thông qua interface strategy. 91 | private strategy: Strategy 92 | 93 | 94 | // Thông thường, context nhận strategy thông qua hàm khởi 95 | // tạo và cung cập một setter cho strategy có thể chuyển 96 | // đổi khi đang chạy. 97 | method setStrategy(Strategy strategy) is 98 | this.strategy = strategy 99 | 100 | 101 | // Context uỷ thác công việc cho đối tượng strategy thay 102 | // vì triển khai nhiều phiên bản thuật toán của chính nó. 103 | method executeStrategy(int a, int b) is 104 | return strategy.execute(a, b) 105 | 106 | 107 | // Code client chọn một concrete strategy và truyền nó vào 108 | // context. Client nên nhận thức được sự khác nhau giữa 109 | // các strategy theo trật tự để chọn đúng. 110 | class ExampleApplication is 111 | method main() is 112 | Create context object. 113 | 114 | Read first number. 115 | Read last number. 116 | Read the desired action from user input. 117 | 118 | if (action == addition) then 119 | context.setStrategy(new ConcreteStrategyAdd()) 120 | 121 | if (action == subtraction) then 122 | context.setStrategy(new ConcreteStrategySubtract()) 123 | 124 | if (action == multiplication) then 125 | context.setStrategy(new ConcreteStrategyMultiply()) 126 | 127 | result = context.executeStrategy(First number, Second number) 128 | 129 | Print result. 130 | ``` 131 | 132 | ## 💡 Ứng dụng 133 | 134 | **🐞 Sử dụng Strategy khi bạn muốn dùng các biến thể thuật toán khác nhau trong một đối tượng cho phép chuyển đổi từ thuật toán này sang thuật toán khác khi đang chạy**. 135 | 136 | ⚡ Strategy giúp bạn gián tiếp chỉnh sửa hành vi của đối tượng khi đang chạy bằng liên kết với các đối tượng con khác để thực hiện hành vi cụ thể theo các cách khác nhau. 137 | 138 | **🐞 Sử dụng Strategy khi bạn có nhiều lớp giống nhau chỉ khác nhau cách chúng thực hiện một vài hành vi** 139 | 140 | ⚡ Strategy giúp bạn trích xuất các hành vi khác nhau vào một hệ thống phân cấp lớp và kết hợp với lớp gốc thành một, bằng cách này sẽ làm giảm code trùng lặp. 141 | 142 | **🐞 Sử dụng Strategy để cô lập logic nghiệp vụ của một lớp khỏi triển khai chi tiết của thuật toán, thứ không mấy quan trọng trong ngữ cảnh của logic đó**. 143 | 144 | ⚡ Strategy giúp bạn cô lập code, dữ liệu bên trong và các phụ thuộc vào thuật toán với phần code còn lại. Các client khác nhau nhận về một interface đơn giản để thực thi thuật toán và chuyển đổi chúng khi đang chạy. 145 | 146 | **🐞 Sử dụng Strategy khi lớp của bạn có một lượng điều kiện khổng lồ để chuyển đổi các biến thể khác nhau với cùng thuật toán**. 147 | 148 | ⚡ Strategy giúp bạn bỏ đi các điều kiện bằng cách trích xuất tất cả thuật toán vào các lớp riêng biệt. Toàn bộ triển khai cùng interface. Đối tượng gốc uỷ thác thực thi cho một trong các đối tượng trên thay vì triển khai tất cả biến thể của thuật toán. 149 | 150 | ## 📋 Triển khai 151 | 152 | 1. Trong lớp context, xác định thuật toán dễ thay đổi. Nó còn có thể có một lượng lớn điều kiện để chọn và thực thi một biến thể của cùng một thuật toán khi đang chạy 153 | 2. Khai báo interface strategy chung cho tất cả biến thể của thuật toán. 154 | 3. Từng cái một, trích xuất tất cả thuật toán vào các lớp của nó. Chúng nên triển khai tất cả trên interface strategy. 155 | 4. Trong lớp context, thêm một trường cho lưu trữ tham chiếu đến đối tượng strategy. Cung cấp một setter cho thay thế giá trị của trường này. Context nên làm việc với đối tượng strategy thông qua interface strategy. Context có thể định nghĩa một interface để cho phép strategy truy cập dữ liệu của nó. 156 | 5. Client của context phải liên kết nó với strategy phù hợp để ứng với cách chúng mong đợi context thực hiện hành vi chính. 157 | 158 | ## ⚖️ Ưu nhược điểm 159 | 160 | ### Ưu điểm 161 | 162 | ✔️ Bạn có thể chuyển đổi thuật toán bên trong đối tượng khi đang chạy. 163 | 164 | ✔️ Bạn có thể cô lập triển khai chi tiết của thuật toán khỏi code sử dụng nó. 165 | 166 | ✔️ Bạn có thể thay thế kế thừa với hỗn hợp. 167 | 168 | ✔️ *Open/Closed Principle*. Bạn có thể thêm strategy mới mà không ảnh hưởng đến context. 169 | 170 | ### Nhược điểm 171 | 172 | ❌ Nếu bạn chỉ có một vài thuật toán và chúng hiếm khi thay đổi, thì không có lý do thực sự nào để làm phức tạp chương trình quá mức với các lớp và interface mới đi kèm với pattern. 173 | 174 | ❌ Client phải nhận thức được các strategy khác nhau để có thể chọn cái phù hợp. 175 | 176 | ❌ Rất nhiều ngôn ngữ lập trình hiện đại có hỗ trợ kiểu hàm cho phép bạn triển khai các phiên bản khác nhau của thuật toán bên trong một tập hợp các hàm ẩn danh. Sau đó, bạn có thể sử dụng các chức năng này chính xác như khi bạn đã sử dụng các đối tượng strategy, nhưng không làm tăng code của bạn với các lớp và giao diện bổ sung. 177 | 178 | ## 🔁 Quan hệ với các pattern khác 179 | 180 | **Bridge**, **State**, **Strategy** (và ở một mức độ nào đó là **Adapter**) có cấu trúc rất giống nhau. Thật vậy, tất cả các pattern này đều dựa trên nguyên tắc là ủy thác công việc cho các đối tượng khác. Tuy nhiên, chúng giải quyết các vấn đề khác nhau. Một pattern không chỉ là một công thức để cấu trúc code của bạn theo một cách cụ thể. Nó còn có thể truyền đạt đến các dev khác về vấn đề mà pattern giải quyết. 181 | 182 | **Command** và **Strategy** có thể trông giống nhau vì bạn có thể sử dụng cả hai để tham số hóa một đối tượng bằng một số hành động. Tuy nhiên, chúng có mục đích rất khác nhau. 183 | 184 | - Bạn có thể sử dụng **Command** để chuyển đổi bất kỳ thao tác nào thành một đối tượng. Các tham số của thao tác trở thành các trường của đối tượng đó. Việc chuyển đổi cho phép bạn trì hoãn việc thực hiện thao tác, xếp hàng đợi, lưu trữ lịch sử lệnh, gửi lệnh đến các dịch vụ từ xa, v.v. 185 | 186 | - Mặt khác, **Strategy** thường mô tả các cách khác nhau để thực hiện cùng một việc, cho phép bạn hoán đổi các thuật toán này trong một lớp ngữ cảnh duy nhất. 187 | 188 | **Decorator** cho phép bạn thay đổi vẻ ngoài của một đối tượng, trong khi **Strategy** cho phép bạn thay đổi ruột. 189 | 190 | **Template Method** dựa trên sự kế thừa: nó cho phép bạn thay đổi các phần của một thuật toán bằng cách mở rộng các phần đó trong các lớp con. **Strategy** dựa trên cấu tạo: bạn có thể thay đổi các phần trong hành vi của đối tượng bằng cách cung cấp cho đối tượng các strategy khác nhau tương ứng với hành vi đó. **Template Method** hoạt động ở cấp độ lớp, vì vậy nó là tĩnh. **Strategy** hoạt động ở cấp độ đối tượng, cho phép bạn chuyển đổi hành vi trong thời gian chạy. 191 | 192 | **State** có thể được coi là một phần mở rộng của **Strategy**. Cả hai pattern đều dựa trên kết hợp: chúng thay đổi hành vi của ngữ cảnh bằng cách ủy quyền một số công việc cho các đối tượng trợ giúp. **Strategy** làm cho các đối tượng này hoàn toàn độc lập và không biết về nhau. Tuy nhiên, **State** không hạn chế sự phụ thuộc giữa các trạng thái cụ thể, cho phép chúng thay đổi trạng thái của ngữ cảnh theo ý muốn. 193 | 194 | # Nguồn 195 | 196 | [**refactoring**](https://refactoring.guru/design-patterns/strategy) -------------------------------------------------------------------------------- /behavioral-pattern/strategy/assets/analogy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/strategy/assets/analogy.png -------------------------------------------------------------------------------- /behavioral-pattern/strategy/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/strategy/assets/intent.png -------------------------------------------------------------------------------- /behavioral-pattern/strategy/assets/problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/strategy/assets/problem.png -------------------------------------------------------------------------------- /behavioral-pattern/strategy/assets/solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/strategy/assets/solution.png -------------------------------------------------------------------------------- /behavioral-pattern/strategy/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/strategy/assets/structure.png -------------------------------------------------------------------------------- /behavioral-pattern/template-method/README.md: -------------------------------------------------------------------------------- 1 | # Template Method 2 | 3 | ## 📜 Mục đích 4 | 5 | **Template Method** là một design pattern thuộc nhóm behavioral giúp định nghĩa bộ khung của thuật toán ở lớp cha (superclass) nhưng các lớp con (subsclasses) có thể ghi đè lên các bước cụ thể của thuật toán mà không làm thay đổi cấu trúc của nó. 6 | 7 | ![intent](./assets/intent.png) 8 | 9 | ## 😟 Vấn đề 10 | 11 | Tưởng tượng bạn đang tạo một ứng dụng khai thác dữ liệu để phân tích tài liệu của công ty. Người dùng cung cấp các tài liệu cho ứng dụng với các định dạng khác nhau (PDF, DOC, CSV), ứng dụng sẽ trích xuất dữ liệu có ích từ các tài liệu này ở một định dạng thống nhất. 12 | 13 | Phiên bản đầu tiên của ứng dụng chỉ làm việc với file DOC. Trong phiên bản tiếp theo nó hỗ trợ file CSV. Và một tháng sau, nó trích xuất cả dữ liệu từ file PDF. 14 | 15 | ![problem](./assets/problem.png) 16 | 17 | Vào một thời điểm nào đó, bạn nhận thấy rằng cả code ở cả ba lớp có rất nhiều điểm tương đồng nhau. Mặc dù code để xử lý các định dạng dữ liệu khác nhau hoàn toàn khác nhau ở tất cả các lớp, nhưng code để xử lý và phân tích dữ liệu gần như giống hệt nhau. Sẽ rất tuyệt vời nếu ta có thể loại bỏ sự trùng lặp code nhưng vẫn giữ nguyên được cấu trúc của thuật toán. 18 | 19 | Và một vấn đề khác nữa liên quan đến code client, nơi sử dụng các lớp này, là nó có rất nhiều điều kiện để chọn quá trình hành động thích hợp tùy thuộc vào lớp của đối tượng xử lý. Nếu cả ba lớp xử lý đều có một interface chung hoặc một lớp cơ sở, bạn có thể loại bỏ các điều kiện trong code client và sử dụng tính đa hình khi gọi các phương thức trên một đối tượng xử lý. 20 | 21 | ## 😊 Giải pháp 22 | 23 | Template Method gợi ý rằng bạn nên chia nhỏ thuật toán thành một chuỗi các bước, biến các bước này thành các phương thức và đặt một loạt lệnh gọi đến các phương thức này bên trong một phương thức template duy nhất. Các bước có thể là `abstract` (trừu tượng) hoặc có một số triển khai mặc định. Để sử dụng thuật toán, client phải cung cấp lớp con của chính nó, thực hiện tất cả các bước trừu tượng và ghi đè một số bước tùy chọn nếu cần (nhưng không được ghi lên phương thức template). 24 | 25 | Hãy xem cách nó làm việc trong ứng dụng khai thác dữ liệu. Ta có thể tạo một lớp cơ sở cho cả ba thuật toán phân tích. Lớp này định nghĩa một phương thức template bao gồm một loạt các lệnh gọi đến các bước xử lý tài liệu khác nhau. 26 | 27 | ![solution](./assets/solution.png) 28 | 29 | Lúc đầu, ta có thể khai báo tất cả các bước là `abstract`, buộc các lớp con cung cấp các triển khai riêng của chúng cho các phương thức này. Trong trường hợp này, các lớp con đã có tất cả các triển khai cần thiết, vì vậy điều duy nhất ta cần làm là điều chỉnh signature của các phương thức để phù hợp với các phương thức của lớp cha. 30 | 31 | Bây giờ, hãy xem cách để có thể loại bỏ code trùng lặp. Có thể thấy code để mở/đóng file và trích xuất/phân tích dữ liệu là khác nhau đối với các định dạng dữ liệu khác nhau, vì vậy bạn không cần phải đụng đến các phương pháp đó. Tuy nhiên, việc thực hiện các bước khác, chẳng hạn như phân tích dữ liệu raw và soạn báo cáo, rất giống nhau, vì vậy nó có thể được kéo lên lớp cơ sở, nơi các lớp con có thể chia sẻ code đó. 32 | 33 | Như bạn có thể thấy, ta có hai loại bước: 34 | 35 | - các bước trừu tượng phải được thực hiện bởi mọi lớp con. 36 | - các bước tùy chọn đã có một số triển khai mặc định, nhưng vẫn có thể bị ghi đè nếu cần. 37 | 38 | Có một loại bước khác, được gọi là hook. Hook là một bước tùy chọn với phần thân trống. Phương thức template sẽ hoạt động ngay cả khi hook không bị ghi đè. Thông thường, các hook được đặt trước và sau các bước quan trọng của thuật toán, cung cấp các lớp con với các điểm mở rộng bổ sung cho một thuật toán. 39 | 40 | ## 🚗 Thế Giới Thực 41 | 42 | ![analogy](./assets/analogy.png) 43 | 44 | Phương pháp template có thể được sử dụng trong xây dựng hàng loạt ngôi nhà. Kế hoạch kiến trúc để xây dựng một ngôi nhà tiêu chuẩn có thể chứa một số điểm mở rộng cho phép người chủ sở hữu có tiềm năng điều chỉnh một số chi tiết của ngôi nhà. 45 | 46 | Mỗi công đoạn xây dựng như đổ móng, đóng khung, xây tường, lắp đặt hệ thống ống nước, đi dây điện nước,… đều có thể thay đổi đôi chút để tạo cho ngôi nhà có một chút khác biệt so với những ngôi nhà khác. 47 | 48 | ## 🏢 Cấu trúc 49 | 50 | ![structure](./assets/structure.png) 51 | 52 | 1. **Abstract Class** là lớp trừu tượng khai báo các phương thức hoạt động như các bước của một thuật toán, cũng như phương thức template để gọi các phương thức này theo một thứ tự cụ thể. Các bước có thể được khai báo là trừu tượng hoặc có một số triển khai mặc định. 53 | 2. **Concrete Classes** có thể ghi đè tất cả các bước, nhưng không thể ghi đè lên phương thức template. 54 | 55 | ## 👨‍💻 Mã giả 56 | 57 | Trong ví dụ này, phương thức Template cung cấp một "bộ khung" cho các branch khác nhau của trí thông minh nhân tạo trên một trò chơi điện tử mô phỏng đơn giản. 58 | 59 | ![pseudocode](./assets/pseudocode.png) 60 | 61 | Tất cả chủng tộc trong trò chơi gần như giống nhau về kiểu unit và cách xây dựng. Do đó bạn có thể sử dụng lại cùng một cấu trúc AI cho các chủng tộc khác nhau, đồng thời vẫn có thể ghi đè lên một vài chi tiết. Với cách tiếp cận này, bạn có thể ghi đè lên AI của loài orc làm cho chúng hung dữ, làm cho loài người có khả năng phòng thủ và lũ quái vật không thể xây dựng bất kỳ thứ gì. Thêm một chủng tộc mới trong game yêu cầu tạo một lớp con AI mới và ghi đè lên phương thức mặc định đã tạo ở lớp AI cơ sở. 62 | 63 | ```c 64 | // Lớp abstract định nghĩa một phương thức template bao gồm 65 | // bộ khung của một vài thuật toán được gọi, thông thường là 66 | // các thao tác nguyên thuỷ trừu tượng. Concrete subclass triển 67 | // khai các thao tác này, nhưng vẫn giữ nguyên phương thức 68 | // template. 69 | class GameAI is 70 | // Phương thức template định nghĩa khung của một thuật toán. 71 | method turn() is 72 | collectResources() 73 | buildStructures() 74 | buildUnits() 75 | attack() 76 | 77 | 78 | // Một vài bước có thể triển khai ở tại lớp cơ sở. 79 | method collectResources() is 80 | foreach (s in this.builtStructures) do 81 | s.collect() 82 | 83 | // Và một số có thể định nghĩa là trừu tượng. 84 | abstract method buildStructures() 85 | abstract method buildUnits() 86 | 87 | // Một lớp có thể có nhiều phương thức template. 88 | method attack() is 89 | enemy = closestEnemy() 90 | if (enemy == null) 91 | sendScouts(map.center) 92 | else 93 | sendWarriors(enemy.position) 94 | 95 | abstract method sendScouts(position) 96 | abstract method sendWarriors(position) 97 | 98 | 99 | // Concrete class phải triển khai tất cả thao tác trừu tượng 100 | // của lớp cơ sở và không được ghi đè lên phương thức template. 101 | class OrcsAI extends GameAI is 102 | method buildStructures() is 103 | if (there are some resources) then 104 | // Xây dựng trang trại, sau đó là doanh trại, sau đó là thành trì. 105 | 106 | method buildUnits() is 107 | if (there are plenty of resources) then 108 | if (there are no scouts) 109 | // Xây dựng người liên lạc, thêm nó vào nhóm trinh sát. 110 | else 111 | // Xây dựng grunt, thêm nó vào nhóm chiến binh. 112 | 113 | // ... 114 | 115 | method sendScouts(position) is 116 | if (scouts.length > 0) then 117 | // Cử trinh sát đến vị trí. 118 | 119 | method sendWarriors(position) is 120 | if (warriors.length > 5) then 121 | // Đưa chiến binh vào vị trí. 122 | 123 | 124 | // Lớp con có thể ghi đè một vài thao tác với 125 | // triển khai mặc định. 126 | class MonstersAI extends GameAI is 127 | method collectResources() is 128 | // Quái vật không thể thu thập tài nguyên. 129 | 130 | method buildStructures() is 131 | // Quái vật không thể xây dựng kiến trúc.. 132 | 133 | method buildUnits() is 134 | // Quái vật không thể xây dựng đơn vị. 135 | ``` 136 | 137 | ## 💡 Ứng dụng 138 | 139 | **🐞 Sử dụng phương thức Template khi bạn muốn client chỉ mở rộng các bước cụ thể của thuật toán chứ không phải toàn bộ cấu trúc của nó** 140 | 141 | ⚡ Phương thức Template giúp bạn chuyển một khối thuật toán thành một loạt các bước riêng rẽ để dễ mở rộng bởi lớp con trong khi vẫn giữ nguyên cấu trúc đã định nghĩa ở lớp cha. 142 | 143 | **🐞 Sử dụng template khi bạn có nhiều lớp bao gồm các thuật toán giống nhau chỉ có một ít là khác biệt. Và bạn phải chỉnh sửa tất cả lớp khi thuật toán thay đổi**. 144 | 145 | ⚡ Khi bạn chuyển một thuật toán thành phương thức template, bạn có thể kéo các bước với triển khai giống nhau lên lớp cha, giảm thiểu code trùng lặp. Code khác nhau ở lớp con có thể được giữ lại ở đấy. 146 | 147 | ## 📋 Triển khai 148 | 149 | 1. Phân tích thuật toán mục tiêu để xem liệu bạn có thể chia nó thành các bước hay không. Xem xét bước nào là chung cho tất cả các lớp con và bước nào là duy nhất. 150 | 2. Tạo lớp trừu tường (`abstract`) và khai báo phương thức template và một tập hợp của phương thức trừu tượng để biểu diễn các bước của thuật toán. Phác thảo cấu trúc của thuật toán trong phương pháp template bằng cách thực hiện các bước tương ứng. Cân nhắc việc tạo phương thức template cuối cùng để ngăn các lớp con ghi đè nó. 151 | 3. Sẽ không sao nếu tất cả các bước đều trừu tượng. Tuy nhiên, một số bước có thể được hưởng lợi từ việc triển khai mặc định. Các lớp con không phải triển khai các phương thức đó. 152 | 4. Thêm hook giữa các bước cốt lõi của thuật toán. 153 | 5. Đối với mỗi biến thể của thuật toán, hãy tạo một lớp con cụ thể(concrete subclasses) mới. Nó phải triển khai tất cả các bước trừu tượng, nhưng cũng có thể ghi đè một số bước tùy chọn. 154 | 155 | ## ⚖️ Ưu nhược điểm 156 | 157 | ### Ưu điểm 158 | 159 | ✔️ Bạn chỉ cho phép client ghi đè một số phần nhất định của một thuật toán lớn, giúp chúng ít bị ảnh hưởng bởi những thay đổi xảy ra với các phần khác của thuật toán. 160 | 161 | ✔️ Bạn có thể gom code trùng lặp vào một lớp cha. 162 | 163 | ### Nhược điểm 164 | 165 | ❌ Một số client có thể bị giới hạn bởi khung thuật toán được cung cấp. 166 | 167 | ❌ Bạn có thể vi phạm Nguyên tắc Liskov Substitution, khi chặn triển khai bước mặc định thông qua một lớp con. 168 | 169 | ❌ Các phương pháp template có xu hướng khó bảo trì hơn khi chúng có nhiều bước hơn. 170 | 171 | ## 🔁 Quan hệ với các pattern khác 172 | 173 | **Factory Method** là một chuyên môn hóa của **Template Method**. Đồng thời, **Factory Method** có thể đóng vai trò là một bước trong một **Template Method** lớn. 174 | 175 | **Template Method** dựa trên sự kế thừa: nó cho phép bạn thay đổi các phần của một thuật toán bằng cách mở rộng các phần đó trong các lớp con. **Strategy** dựa trên cấu tạo: bạn có thể thay đổi các phần trong hành vi của đối tượng bằng cách cung cấp cho đối tượng các strategy khác nhau tương ứng với hành vi đó. **Template Method** hoạt động ở cấp độ lớp, vì vậy nó là tĩnh. **Strategy** hoạt động ở cấp độ đối tượng, cho phép bạn chuyển đổi hành vi trong thời gian chạy. 176 | 177 | # Nguồn 178 | 179 | [**refactoring**](https://refactoring.guru/design-patterns/template-method) -------------------------------------------------------------------------------- /behavioral-pattern/template-method/assets/analogy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/template-method/assets/analogy.png -------------------------------------------------------------------------------- /behavioral-pattern/template-method/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/template-method/assets/intent.png -------------------------------------------------------------------------------- /behavioral-pattern/template-method/assets/problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/template-method/assets/problem.png -------------------------------------------------------------------------------- /behavioral-pattern/template-method/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/template-method/assets/pseudocode.png -------------------------------------------------------------------------------- /behavioral-pattern/template-method/assets/solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/template-method/assets/solution.png -------------------------------------------------------------------------------- /behavioral-pattern/template-method/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/template-method/assets/structure.png -------------------------------------------------------------------------------- /behavioral-pattern/visitor/assets/analogy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/visitor/assets/analogy.png -------------------------------------------------------------------------------- /behavioral-pattern/visitor/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/visitor/assets/intent.png -------------------------------------------------------------------------------- /behavioral-pattern/visitor/assets/problem1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/visitor/assets/problem1.png -------------------------------------------------------------------------------- /behavioral-pattern/visitor/assets/problem2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/visitor/assets/problem2.png -------------------------------------------------------------------------------- /behavioral-pattern/visitor/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/visitor/assets/pseudocode.png -------------------------------------------------------------------------------- /behavioral-pattern/visitor/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/behavioral-pattern/visitor/assets/structure.png -------------------------------------------------------------------------------- /creational-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Creational Design Patterns 2 | 3 | Creational pattern cung cấp các cơ chế tạo đối tượng khác nhau, giúp tăng tính linh hoạt và tái sử dụng code hiện có. 4 | 5 | ## Factory Method 6 | 7 | [![factory-method-mini](./assets/factory-method-mini.png)](./factory-method) 8 | 9 | Cung cấp một interface cho tạo đối tượng ở lớp cha, nhưng cho phép lớp con thay đổi kiểu đối tượng sẽ được tạo. 10 | 11 | ## Abstract Factory 12 | 13 | [![abstract-factory-mini](./assets/abstract-factory-mini.png)](./abstract-factory) 14 | 15 | Giúp bạn tạo các đối tượng có liên quan với nhau trong một nhóm mà không cần chỉ định đến lớp cụ thể của chúng. 16 | 17 | ## Builder 18 | 19 | [![builder-mini](./assets/builder-mini.png)](./builder) 20 | 21 | Xây dựng các đối tượng phức tạp từng bước một. Pattern này cho phép bạn tạo và biểu diễn các kiểu đối tượng khác nhau bằng code khởi tạo giống nhau. 22 | 23 | ## Prototype 24 | 25 | [![prototype-mini](./assets/prototype-mini.png)](./prototype) 26 | 27 | Giúp bạn sao chép một đối tượng mà code của bạn sẽ không phụ thuộc vào lớp của đối tượng đó. 28 | 29 | ## Singleton 30 | 31 | [![singleton-mini](./assets/singleton-mini.png)](./singleton) 32 | 33 | Đảm bảo ràng một lớp chỉ với một thực thể duy nhất, trong khi cung cấp điểm truy cập toàn cục cho thực thể đấy. -------------------------------------------------------------------------------- /creational-pattern/abstract-factory/README.md: -------------------------------------------------------------------------------- 1 | # Abstract Factory 2 | 3 | ## 📜 Mục đích 4 | 5 | **Abstract Factory** là một design pattern thuộc nhóm creational, dùng để tạo ra các đối tượng có quan hệ gần gũi với nhau mà không cần chỉ định đến lớp cụ thể của chúng. 6 | 7 | ![intent](./assets/intent.png) 8 | 9 | ## 😟 Vấn đề 10 | 11 | Giả sử bạn đang tạo một trang bán đồ nội thất. Code của bạn bao gồm các lớp sau: 12 | 1. Các sản phẩm có quan hệ với nhau như: `Sofa`, `Chair` và `CoffeTable`. 13 | 2. Các biến thể của nhóm sản phẩm đó. Ví dụ như nhóm `Sofa` + `Chair` + `CoffeTable` có các biến thể như `Modern`, `Victorian` và `ArtDeco`. 14 | 15 | ![problem](./assets/problem.png) 16 | 17 | Bạn cần có cách để khi tạo một đồ nội thất đơn lẻ, nó phải phù hợp với các đồ vật khác trong nhóm của nó. Khách hàng sẽ khó chịu khi họ nhận về những đồ vật trong nhóm có biến thể khác nhau. 18 | 19 | ![problem1](./assets/problem1.png) 20 | 21 | Bên cạnh đó, bạn không muốn thay đổi code mỗi khi thêm sản phẩm hoặc nhóm sản phẩm trong chương trình. Danh mục nội thất được cập nhật rất thường xuyên, và bạn không muốn thay đổi code mỗi khi nó diễn ra. 22 | 23 | ## 😊 Giải pháp 24 | 25 | Việc đầu tiên cần làm theo Abstract Factory là khai báo inteface rõ ràng cho mỗi sản phẩm riêng biệt trong nhóm sản phẩm. Và tạo tất cả biến thể của sản phẩm theo sau inteface đó. Ví dụ tất cả biến thể của ghế được triển khai trong interface `Chair`, tất cả sofa được triển khai trong interface `Sofa` ,... 26 | 27 | ![solution1](./assets/solution1.png) 28 | 29 | Bước tiếp theo là khai báo *Abstract Factory* - là interface chứa tất cả phương thức tạo cho tất cả sản phẩm trong nhóm sản phẩm (vd: `createChair`, `createSofa` và `createCoffeTable`). Các phương thức này trả về một kiểu sản phẩm **trừu tượng (abstract)** được biểu diễn bởi interface mà chúng ta trích xuất trước đó: `Chair`, `Sofa`, `CoffeTable`,... 30 | 31 | ![solution2](./assets/solution2.png) 32 | 33 | Vậy còn các biến thể của sản phẩm? Với từng biến thể của nhóm sản phẩm, ta tạo ra một lớp factory riêng biệt dựa trên interface **Abstract Factory**. Factory là lớp trả về kiểu sản phẩm riêng biệt. Ví dụ, `ModernFurnitureFactory` có thể tạo ra các đối tượng `ModernChair`, `ModernSofa` hay `ModernCoffeTable`. 34 | 35 | Code client làm việc với factory hay sản phẩm thông qua interface trừu tượng. Thế nên bạn có thể thay đổi kiểu factory hay biến thể của sản phẩm cho code client nhận mà không gây ra bất kỳ lỗi gì. 36 | 37 | ![solution3](./assets/solution3.png) 38 | 39 | Giả sử client muốn một factory để tạo ghế (chair). Nó sẽ không cần quan tâm kiểu của lớp factory đó, cũng như kiểu ghế nhận về. Dù là Modern hay Victorian, nó cũng sẽ xử lý theo cùng một cách là thông qua interface trừu tượng `Chair`. Với cách tiếp cận này client chỉ cần quan tâm là ghế sẽ triển khai phương thức `sitOn` như thế nào. Bên cạnh đó, bất kỳ biến thể nào của `chair`, nó cũng sẽ phù hợp với `sofa` và `coffe-table` được tạo cùng đối tượng factory. 40 | 41 | Nếu bạn thắc mắc: client chỉ làm việc với interface trừu tượng, vậy thì cái gì sẽ tạo ra factory ?. Thông thường ứng dụng sẽ tạo đối tượng factory cụ thể ở giai đoạn khởi tạo, nhưng trước đó ứng dụng sẽ tạo factory dựa trên kiểu cấu hình hoặc thiết lập môi trường. 42 | 43 | ## 🏢 Cấu trúc 44 | 45 | ![structure](./assets/structure.png) 46 | 47 | 1. **Abstract Product** là inteface cho các sản phẩm riêng biệt nhưng có quan hệ với nhau tạo nên một nhóm sản phẩm. 48 | 2. **Concrete Product** là các triển khai biến thể của abstract product, được gom nhóm theo biến thể. Mỗi abstract product (chair/sofa) sẽ được triển khai tất cả biến thể (modern, victorian). 49 | 3. **Abstract Factory** là interface có tập hợp phương thức khởi tạo cho từng abstract product. 50 | 4. **Concrete Factory** là triển khai phương thức khởi tạo của abstract factory. Mỗi concrete factory tương ứng với biến thể cụ thể của sản phẩm và chỉ tạo sản phẩm theo biến thể đó. 51 | 5. Mặc dù concrete factory tạo ra các concrete product, nhưng signature của phương thức khởi tạo trả về sẽ tương ứng với abstract product. Với cách này code client sử dụng factory sẽ không cần quan tâm tới biến thể cụ thể của sản phẩm từ factory. Nó có thể làm việc với bất kỳ biến thể nào miễn là giao tiếp với các đối tượng thông qua interface trừu tượng. 52 | 53 | ## 👨‍💻 Mã giả 54 | 55 | Ví dụ này minh hoạ cách Abstract Factory có thể sử dụng để tạo các phần tử UI đa nền tảng mà không cần ghép code client với lớp UI cụ thể, đồng thời giữ cho tất cả các phần tử được tạo nhất quán với hệ điều hành đã chọn 56 | 57 | ![pseudocode](./assets/pseudocode.png) 58 | 59 | Các phần tử UI trên các ứng dụng đa nền tảng sẽ được triển khai giống nhau, sẽ có một chút khác biệt trên các hệ điều hành khác nhau. Hơn nữa, nhiệm vụ của bạn là đảm bảo style của các phần tử UI phù hợp với hệ điều hành hiện tại. Bạn sẽ không muốn chương trình hiển thị MacOS control khi được thực thi trên Windows. 60 | 61 | Abstract Factory sẽ khai báo tập hợp các phương thức khởi tạo mà code client có thể dùng để tạo các kiểu phần tử UI khác nhau. Concrete factory tương ứng với hệ điều hành cụ thể sẽ tạo ra các phần tử UI phù hợp với hệ điều hành đó. 62 | 63 | Nó hoạt động như sau: khi ứng dụng bắt đầu chạy, nó sẽ kiểm tra loại hệ điều hành hiện tại. Ứng dụng sẽ dùng thông tin đó để tạo ra một đối tượng factory từ lớp tương ứng với hệ điều hành. Các phần còn lại sẽ dùng factory để tạo ra phần tử UI. Điều này ngăn việc tạo các phần tử UI sai lệch. 64 | 65 | Với cách tiếp cận này, code client không phụ thuộc lớp cụ thể của factory hay phần tử UI, miễn là nó làm việc với các đối tượng thông qua abstract factory. Không những thế, code client còn hỗ trợ các factory khác và phần tử UI bạn thêm vào trong tương lai. 66 | 67 | Như vậy bạn không cần chỉnh sửa code client mỗi lần thêm biến thể của phần tử UI trong ứng dụng. Bạn chỉ cần tạo một lớp factory mới tạo ra các phần tử này và sửa đổi một chút code khởi tạo để ứng dụng chọn lớp đó khi thích hợp. 68 | 69 | ```c 70 | // Interface Abstract Factory khai bao tập hợp phương thức 71 | // trả về kiểu abstract product khác nhau. Các sản phẩm chung 72 | // nhóm có quan hệ với nhau về chủ đề hoặc một khái niệm cấp cao. 73 | // Sản phẩm từ một nhóm thường có thể cộng tác với nhau. Nhóm 74 | // sản phẩm thường có một hay nhiều biến thể, nhưng sản phẩm của 75 | // một biến thể này sẽ không tương thích với biến thể khác. 76 | interface GUIFactory is 77 | method createButton():Button 78 | method createCheckbox():Checkbox 79 | 80 | 81 | // Concrete factory tạo ra nhóm sản phẩm thuộc về một biến thể. 82 | // Factory đảm bảo rằng sản phẩm tạo ra luôn tương thích. 83 | // Signature của phương thức concrete factory trả về 84 | // abstract product, trong khi bên trong phương thức, 85 | // concrete product được tạo ra. 86 | class WinFactory implements GUIFactory is 87 | method createButton():Button is 88 | return new WinButton() 89 | method createCheckbox():Checkbox is 90 | return new WinCheckbox() 91 | 92 | // Mỗi concrete factory có một biến thể sản phẩm tương ứng. 93 | class MacFactory implements GUIFactory is 94 | method createButton():Button is 95 | return new MacButton() 96 | method createCheckbox():Checkbox is 97 | return new MacCheckbox() 98 | 99 | 100 | // Mỗi sản phẩm riêng biệt của nhóm sản phẩm nên có một interface 101 | // cơ sở. Tất cả biết thể của sản phẩm sẽ được triển khai từ 102 | // interface này 103 | interface Button is 104 | method paint() 105 | 106 | 107 | // Concrete product được tạo bởi concrete factory tương ứng. 108 | class WinButton implements Button is 109 | method paint() is 110 | // Hiển thị button trong Windows 111 | 112 | class MacButton implements Button is 113 | method paint() is 114 | // Hiển thị button trong MacOS 115 | 116 | // Đây là interface cơ sở của một sản phẩm khác. Tất cả 117 | // sản phẩm có thể tương tác với nhau, nhưng tương tác 118 | // chỉ khả dụng giữa hai sản phẩm cùng một biến thể. 119 | interface Checkbox is 120 | method paint() 121 | 122 | class WinCheckbox implements Checkbox is 123 | method paint() is 124 | // Hiển thị checkbox trong Windows 125 | 126 | class MacCheckbox implements Checkbox is 127 | method paint() is 128 | // Hiển thị checkbox trong MacOS 129 | 130 | 131 | // Code client làm việc với factory và sản phẩm chỉ thông qua 132 | // kiểu trừu tượng: GUIFactory, Button và Checkbox. Nó giúp bạn 133 | // chuyển bất kỳ lớp con nào của factory sang code client mà 134 | // không làm hỏng nó. 135 | class Application is 136 | private field factory: GUIFactory 137 | private field button: Button 138 | constructor Application(factory: GUIFactory) is 139 | this.factory = factory 140 | method createUI() is 141 | this.button = factory.createButton() 142 | method paint() is 143 | button.paint() 144 | 145 | 146 | // Ứng dụng chọn kiểu factory tùy thuộc vào cấu hình hiện tại 147 | // hoặc thiết lập môi trường và tạo nó trong thời gian chạy 148 | // (thường ở giai đoạn khởi tạo). 149 | class ApplicationConfigurator is 150 | method main() is 151 | config = readApplicationConfigFile() 152 | 153 | if (config.OS == "Windows") then 154 | factory = new WinFactory() 155 | else if (config.OS == "Mac") then 156 | factory = new MacFactory() 157 | else 158 | throw new Exception("Error! Unknown operating system.") 159 | 160 | Application app = new Application(factory) 161 | ``` 162 | 163 | ## 💡 Ứng dụng 164 | 165 | **🐞 Sử dụng Abstract Factory khi bạn cần làm việc với nhiều biến thể của một nhóm sản phẩm, mà bạn không muốn phụ thuộc vào lớp cụ thể của sản phẩm đó - chúng có thể chưa biết trước hoặc đơn giản là bạn muốn mở rộng trong tương lai.** 166 | 167 | ⚡ Abstract Factory cung cấp cho bạn interface để tạo các đối tượng từ mỗi lớp trong nhóm sản phẩm miễn là code của bạn tạo đối tượng thông qua interface, bạn sẽ không phải lo lắng các vấn đề tạo sai biến thể của sản phẩm hay không phù hợp với sản phẩm đã tạo trong ứng dụng. 168 | 169 | - Khi triển khai Abstract Factory cần lưu ý nến bạn có một lớp với một tập hợp phương thức Factory, nó có thể làm mờ nhiệm vụ chính của Abstract. 170 | - Một thiết kế chương trình tốt là khi *mỗi lớp sẽ chỉ làm một nhiệm vụ*. Khi một lớp xử lý nhiều loại sản phẩm, bạn có thể trích xuất các phương thức factory của nó thành một lớp factory độc lập hoặc triển khai Abstract Factory toàn diện. 171 | 172 | ## 📋 Triển khai 173 | 174 | 1. Lập sơ đồ ma trận cho các loại sản phẩm riêng biệt so với các biến thể của chúng. 175 | 176 | 2. Khai báo interface, Abstract Product cho tất cả loại sản phẩm. Sau đó tạo ra lớp concrete product triển khai interface này. 177 | 178 | 3. Khai báo interface, Abstract Factory với tập hợp phương thức khởi tạo cho tất cả abstract product. 179 | 180 | 4. Triển khai các lớp concrete factory cho từng loại biến thể của sản phẩm 181 | 182 | 5. Tạo code khởi tạo factory đâu đó trong ứng dụng. Nó sẽ khởi tạo một trong các lớp concrete factory, dựa trên cấu hình ứng dụng hoặc thiết lập môi trường. Truyền đối tượng factory này cho tất cả lớp tạo sản phẩm. 183 | 184 | 6. Kiểm tra code và tìm tất cả các lệnh gọi trực tiếp đến constructor của sản phẩm. Thay thế chúng bằng các lệnh gọi đến phương thức tạo thích hợp trên đối tượng factory. 185 | 186 | ## ⚖️ Ưu nhược điểm 187 | 188 | ### Ưu điểm 189 | 190 | ✔️ Bạn có thể chắc chắn rằng các sản phẩm lấy từ một factory sẽ tương thích với nhau. 191 | 192 | ✔️ Tránh được kết hợp quá chặt chẽ giữa code client và concrete product. 193 | 194 | ✔️ *Single Responsibility Principle*. Bạn có thể di chuyển code tạo sản phẩm vào một nơi trong chương trình, giúp hỗ trợ code dễ dàng hơn. 195 | 196 | ✔️ *Open/Closed Principle*. Bạn có thể thêm các biến thể mới vào chương trình, mà không làm ảnh hưởng đến code client hiện tại. 197 | 198 | ### Nhược điểm 199 | 200 | ❌ Code có thể trở nên phức tạp khi bạn thêm vào quá nhiều interface và lớp để triển khai pattern. 201 | 202 | ## 🔁 Quan hệ với các pattern khác 203 | 204 | Nhiều pattern bắt đầu bằng cách sử dụng **Factory Method** (ít phức tạp hơn và có thể tùy chỉnh nhiều hơn thông qua các lớp con) và phát triển theo hướng **Abstract Factory**, **Prototype** hoặc **Builder** (linh hoạt hơn nhưng phức tạp hơn). 205 | 206 | Các lớp **Abstract Factory** thường dựa trên một tập hợp các **Factory Method**, nhưng bạn cũng có thể sử dụng **Prototype** để cấu trúc các phương thức trên các lớp này. 207 | 208 | **Builder** tập trung vào việc xây dựng các đối tượng phức tạp theo từng bước. **Abstract Factory** chuyên tạo các nhóm đối tượng. **Abstract Factory** trả lại sản phẩm ngay lập tức, trong khi **Builder** cho phép bạn chạy một số bước xây dựng bổ sung trước khi tìm nạp sản phẩm. 209 | 210 | **Abstract Factory** có thể dùng như một giải pháp thay thế cho **Facade** khi bạn chỉ muốn ẩn cách các đối tượng hệ thống con được tạo ra khỏi code client. 211 | 212 | Bạn có thể sử dụng **Abstract Factory** cùng với **Bridge**. Việc ghép nối này rất hữu ích khi một số abstract được xác định bởi **Bridge** chỉ có thể hoạt động với các triển khai cụ thể. Trong trường hợp này, **Abstract Factory** có thể đóng gói các quan hệ này và ẩn sự phức tạp khỏi code client. 213 | 214 | Tất cả các **Abstract Factory**, **Builder** và **Prototype** đều có thể được triển khai dưới dạng các **Singleton**. 215 | 216 | # Nguồn 217 | 218 | [**refactoring**](https://refactoring.guru/design-patterns/abstract-factory) -------------------------------------------------------------------------------- /creational-pattern/abstract-factory/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/abstract-factory/assets/intent.png -------------------------------------------------------------------------------- /creational-pattern/abstract-factory/assets/problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/abstract-factory/assets/problem.png -------------------------------------------------------------------------------- /creational-pattern/abstract-factory/assets/problem1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/abstract-factory/assets/problem1.png -------------------------------------------------------------------------------- /creational-pattern/abstract-factory/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/abstract-factory/assets/pseudocode.png -------------------------------------------------------------------------------- /creational-pattern/abstract-factory/assets/solution1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/abstract-factory/assets/solution1.png -------------------------------------------------------------------------------- /creational-pattern/abstract-factory/assets/solution2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/abstract-factory/assets/solution2.png -------------------------------------------------------------------------------- /creational-pattern/abstract-factory/assets/solution3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/abstract-factory/assets/solution3.png -------------------------------------------------------------------------------- /creational-pattern/abstract-factory/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/abstract-factory/assets/structure.png -------------------------------------------------------------------------------- /creational-pattern/assets/abstract-factory-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/assets/abstract-factory-mini.png -------------------------------------------------------------------------------- /creational-pattern/assets/builder-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/assets/builder-mini.png -------------------------------------------------------------------------------- /creational-pattern/assets/factory-method-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/assets/factory-method-mini.png -------------------------------------------------------------------------------- /creational-pattern/assets/prototype-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/assets/prototype-mini.png -------------------------------------------------------------------------------- /creational-pattern/assets/singleton-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/assets/singleton-mini.png -------------------------------------------------------------------------------- /creational-pattern/builder/assets/directory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/builder/assets/directory.png -------------------------------------------------------------------------------- /creational-pattern/builder/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/builder/assets/intent.png -------------------------------------------------------------------------------- /creational-pattern/builder/assets/problem1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/builder/assets/problem1.png -------------------------------------------------------------------------------- /creational-pattern/builder/assets/problem2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/builder/assets/problem2.png -------------------------------------------------------------------------------- /creational-pattern/builder/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/builder/assets/pseudocode.png -------------------------------------------------------------------------------- /creational-pattern/builder/assets/solution1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/builder/assets/solution1.png -------------------------------------------------------------------------------- /creational-pattern/builder/assets/solution2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/builder/assets/solution2.png -------------------------------------------------------------------------------- /creational-pattern/builder/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/builder/assets/structure.png -------------------------------------------------------------------------------- /creational-pattern/factory-method/README.md: -------------------------------------------------------------------------------- 1 | # Factory Method 2 | 3 | ## 📜 Mục đích 4 | 5 | **Factory method** là một design pattern thuộc nhóm Creational, nó cung cấp một interface để tạo đối tượng cho lớp cha (superclass), nhưng cũng cho phép các lớp con (subclass) thay đổi đối tượng sẽ được tạo. 6 | 7 | ![intent](./assets/intent.png) 8 | 9 | ## 😟 Vấn đề 10 | 11 | Tưởng tượng bạn đang tạo một ứng dụng quản lý chuỗi cung ứng. Phiên bản đầu tiên của ứng dụng chỉ quản lý vận chuyển cho các xe tải, thế nên phần lớn code của bạn sẽ nằm trong lớp `Truck`. 12 | 13 | Sau đó ứng dụng của bạn ngày càng phổ biến và bạn nhận được yêu cầu từ các công ty hàng hải để hợp nhất chuỗi cung ứng qua đường biển vào ứng dụng. Đấy là một thông tin tuyệt vời! Nhưng còn code thì sao? 14 | 15 | ![problem](./assets/problem.png) 16 | 17 | *Việc thêm một lớp mới vào ứng dụng không hề đơn giản nếu phần lớn code đã được kết nối với các lớp hiện có.* 18 | 19 | Hiện tại hầu hết code của bạn đã được ghép với lớp `Truck`. Việc thêm `Ship` vào ứng dụng sẽ yêu cầu các thay đổi với toàn bộ codebase. Và nếu bạn thêm một phương tiện vận tải nào nữa vào ứng dụng, thì bạn sẽ thay đổi code lần nữa. 20 | 21 | Kết quả là bạn có một đống code tạp nham với rất nhiều điều kiện thay đổi của ứng dụng tùy thuộc vào loại đối tượng vận chuyển. 22 | 23 | ## 😊 Giải pháp 24 | 25 | Factory Method gợi ý giải pháp là thay vì tạo đối tượng qua các lệnh khởi tạo trực tiếp (sử dụng toán tử `new`) thì hãy tạo bằng cách gọi phương thức *factory*. Lưu ý là đối tượng vẫn được tạo thông qua toán tử `new`, nhưng nó sẽ được gọi từ trong phương thức *factory*. Các đối tượng được trả về theo phương thức factory thường được gọi là **products**. 26 | 27 | ![solution1](./assets/solution1.png) 28 | 29 | *Các lớp con có thể chỉnh sửa đối tượng trả về từ phương thức factory* 30 | 31 | Thoạt nhìn, thay đổi này có vẻ vô nghĩa: chúng ta chỉ chuyển lệnh gọi *constructor* từ phần này sang phần khác của chương trình. Tuy nhiên, hãy xem xét thật kỹ lưỡng : bây giờ bạn có thể ghi đè(override) phương thức factory trong một lớp con và thay đổi `product` đang được tạo bởi phương thức này. 32 | 33 | Song, nó vẫn có một hạn chế nhỏ: các lớp con có thể trả về các kiểu `product` khác nhau chỉ khi các `product` này có lớp cơ sở hoặc interface chung. Ngoài ra, phương thức factory trong lớp cơ sở nên có kiểu trả về được khai báo là interface này. 34 | 35 | ![solution2](./assets/solution2.png) 36 | 37 | Ví dụ, cả hai lớp `Truck` và `Ship` đều được triển khai từ interface `Transport`, interface này khai báo một phương thức là `deliver`. Mỗi lớp sẽ triển khai phương thức này theo cách khác nhau, xe tải (truck) sẽ phân phối (deliver) hàng hoá theo đường bộ, còn tàu(ship) sẽ phân phối theo đường biển. Phương thức factory `RoadLogistics` sẽ trả về đối tượng `Truck`, còn `SeaLogistics` sẽ trả về đối tượng `Ship`. 38 | 39 | ![solution3](./assets/solution3.png) 40 | 41 | Đoạn code sử dụng phương thức factory (thường được gọi là *code client*), không nhìn thấy sự khác biệt giữa những `product` trả về bởi các lớp con khác nhau. Client coi tất cả `product` là lớp trừu tượng **Transport**, đồng thời nó cũng biết các đối tượng transport phải có phương thức `deliver`. Nhưng chi tiết cách hoạt động thì nó không cần quan tâm. 42 | 43 | ## 🏢 Cấu trúc 44 | 45 | ![structure](./assets/structure.png) 46 | 47 | 1. **Product** là interface chung cho tất cả đối tượng có thể được tạo ra bởi creator hay các lớp con của nó. 48 | 2. **Concrete Product** là các triển khai khác nhau từ interface **Product**. Tạm hiểu là product con. 49 | 3. **Creator** lớp này khai báo một phương thức factory trả về đối tượng product mới. Kiểu trả về của phương thức này phải tương ứng với interface **Product**. Bạn có thể định nghĩa phương thức factory là trừu tượng để tất cả lớp con triển khai phiên bản riêng của chúng. Và phương thức factory cơ sở sẽ trả về các kiểu product mặc định. 50 | 51 | 4. **Concreta Creator** sẽ ghi đè (override) phương thức factory cơ sở để trả về một kiểu product mới. Không phải lúc nào phương thức factory cũng tạo ra một đối tượng mới, nó có thể trả về đối tượng đã tồn tại từ cache, object pool hay từ một nguồn nào đó. 52 | 53 | ## 👨‍💻 Mã giả 54 | 55 | Ví dụ này minh hoạ cách phương thức Factory có thể sử dụng để tạo các phần tử UI đa nền tảng mà không cần ghép code client với lớp UI cụ thể. 56 | 57 | ![pseudocode](./assets/pseudocode.png) 58 | 59 | Lớp dialog cơ sở dùng hiển thị các phần tử UI khác nhau cho hiển thị cửa sổ. Dưới các hệ điều hành khác nhau, các phần tử này có thể có vài khác biệt nhỏ, song nó vẫn phải đồng nhất. Button trên Window vẫn là button trên Linux. 60 | 61 | Khi sử dụng phương thức factory, bạn không cần viết lại các logic cho dialog vớI từng hệ điều hành. Nếu ta khai báo phương thức factory để tạo button trong lớp dialog, sau này ta có thể tạo các lớp con trả về button kiểu Windows từ phương thức factory. Lớp con sau đó sẽ kế thừa phần lớn code của dialog từ lớp cơ sở, nhờ vào phương thức factory ta có thể hiển thị các button kiểu window trên màn hình. 62 | 63 | Với pattern này khi làm việc, các lớp dialog cơ sở phải làm việc với button trừu tượng: lớp cơ sở hoặc interface cho tất cả concrete button. Theo cách này, đoạn code còn lại của dialog vẫn hoạt động, dù phải làm việc với bất kỳ kiểu button nào. 64 | 65 | Tất nhiên, bạn có thể dùng cách này cho các phần tử UI khác. Tuy nhiên, với mỗi phương thức factory mà bạn thêm vào diago, ta sẽ dần tiến đến Abstract Factory pattern. Ta sẽ nói về pattern này ở các bài viết sau. 66 | 67 | ```c 68 | // Lớp creator khai báo phương thức factory phải trả về 69 | // đối tượng của lớp product. Lớp con của creator tạo 70 | // các triển khai khác của phương thức. 71 | 72 | // Creator có thể tạo các triển khai mặc định cho 73 | // phương thức factory. 74 | class Dialog is 75 | 76 | abstract method createButton():Button 77 | 78 | // Lưu ý, dù tên là creator, song không phải nhiệm vụ chính 79 | // của nó là tạo product. Nó được dùng để chứa nhưng logic 80 | // nghiệp vụ cốt lõi dựa trên đối tượng product trả về từ phương 81 | // thức factory. Các lớp con có thể gián tiếp thay đổi logic 82 | // bằng cách ghi đè lên phương thức factory và trả về kiểu 83 | // product khác từ nó. 84 | method render() is 85 | // Gọi phương thức factory để tạo đối tượng product. 86 | Button okButton = createButton() 87 | // Sử dụng product. 88 | okButton.onClick(closeDialog) 89 | okButton.render() 90 | 91 | 92 | // Concrete creator ghi đè lên phương thức factory để 93 | // thay đổi kiểu product trả về. 94 | class WindowsDialog extends Dialog is 95 | method createButton():Button is 96 | return new WindowsButton() 97 | 98 | class WebDialog extends Dialog is 99 | method createButton():Button is 100 | return new HTMLButton() 101 | 102 | // Interface product khai báo phương thức cho tất cả 103 | // concrete product cần triển khai. 104 | interface Button is 105 | method render() 106 | method onClick(f) 107 | 108 | // Concrete product tạo ra các triển khai với interface product. 109 | class WindowsButton implements Button is 110 | method render(a, b) is 111 | // Hiển thị button kiểu Windows 112 | method onClick(f) is 113 | // Đánh đấu sự kiện click trên hệ điều hành. 114 | 115 | class HTMLButton implements Button is 116 | method render(a, b) is 117 | // Hiển thị button dưới dạng HTML 118 | method onClick(f) is 119 | // Đánh dấu sự kiện click trên trình duyệt. 120 | 121 | 122 | class Application is 123 | field dialog: Dialog 124 | 125 | // Ứng dụng chon kiểu creator dựa trên cấu hình 126 | // hiện tại hoặc môi trường thiết lập. 127 | method initialize() is 128 | config = readApplicationConfigFile() 129 | 130 | if (config.OS == "Windows") then 131 | dialog = new WindowsDialog() 132 | else if (config.OS == "Web") then 133 | dialog = new WebDialog() 134 | else 135 | throw new Exception("Error! Unknown operating system.") 136 | 137 | // Code client làm việc với thực thể của concrete creator, 138 | // thông qua interface cơ sở. Miễn là client vẫn làm việc với 139 | // creator thông qua interface, bạn có thể chuyển nó vào bất 140 | // kỳ lớp con nào của creator. 141 | method main() is 142 | this.initialize() 143 | dialog.render() 144 | ``` 145 | 146 | ## 💡 Ứng dụng 147 | 148 | 🐞 **Sử dụng phương thức Factory khi bạn không biết chính xác kiểu và phụ thuộc của đối tượng mà code bạn sẽ làm việc** 149 | 150 | ⚡ Phương pháp Factory phân tách code khởi tạo product với code sử dụng lại product. Do đó việc mở rộng code khởi tạo product với phần code còn lại sẽ dễ dàng hơn. 151 | 152 | 🐞 **Sử dụng phương thức Factory khi bạn cung cấp cho người dùng thư viện hay framework với cách mở rộng các thành phần trong nó** 153 | 154 | ⚡ Kế thừa có lẽ là cách dễ nhất để mở rộng các hành vi mặc định của một thư viện hay framework. Nhưng làm thế nào để framework nhận diện được đâu là lớp con của bạn, đâu là thành phần tiêu chuẩn. Giải pháp ở đây là làm giảm code khởi tạo component của framework thành một phương thức factory duy nhất và cho phép bất kỳ ai ghi đè (override) lên phương thức để thêm các phần mở rộng cho component đó. 155 | 156 | Ví dụ, bạn đang tạo một ứng dụng dựa trên một framework UI mã nguồn mở. Bây giờ bạn muốn có một button dạng tròn cho ứng dụng, nhưng framework chỉ hỗ trợ button vuông. Thế nên bạn cần mở rộng lớp tiêu chuẩn `Button` thành lớp con `RoundButton`. Bây giờ bạn cần nói cho lớp chính `UIFramework` rằng sử dụng lớp con button mới thay vì mặc định. Để làm được điều đó, bạn tạo lớp con `UIWithRoundButtons` từ lớp cơ sở của framework và ghi đè lên phương thức `createButton`. Trong khi phương thức ở lớp cơ sở trả về đối tượng `Button` thì lớp con của bạn sẽ trả về đối tượng `RoundButton`. Bây giờ bạn sẽ sử dụng `UIWithRoundButtons` thay vì `UIFramework`. 157 | 158 | 🐞 **Sử dụng phương thức Factory khi bạn muốn tiết kiệm tài nguyên hệ thống bằng cách sử dụng lại đối tượng hiện có thay vì tạo mới chúng mỗi lần** 159 | 160 | ⚡ Bạn thường gặp phải yêu cầu này khi làm việc với các đối tượng lớn, sử dụng nhiều tài nguyên như kết nối cơ sở dữ liệu, hệ thống file, tài nguyên mạng,.. 161 | 162 | Bây giờ hãy nghĩ về những việc phải làm với đối tượng hiện có: 163 | 164 | 1. Bạn cần nơi để lưu trữ tất cả các đối tượng đã tạo. 165 | 2. Khi ai đó yêu cầu một đối tượng, chương trình sẽ thực hiện tìm kiếm đối tượng đó trong pool. 166 | 3. ...và trả về cho code client. 167 | 4. Nếu không có đối tượng, chương trình sẽ tạo ra một đối tượng mới (và thêm nó vào pool). 168 | 169 | Có khá nhiều code, và ta phải đặt chúng vào một nơi duy nhất để không rối chương trình do các đoạn code có thể bị trùng. Có lẽ nơi rõ ràng và thuận tiện nhất mà code này có thể được đặt là tại hàm khởi tạo của lớp có các đối tượng mà ta đang cố gắng sử dụng lại. Tuy nhiên, một hàm khởi tạo luôn phải trả về các đối tượng mới theo định nghĩa. Nó không thể trả lại các phiên bản hiện có. 170 | 171 | Do đó, bạn cần phải có một phương thức có khả năng tạo các đối tượng mới cũng như sử dụng lại các đối tượng hiện có. Và đó chính là phương thức Factory. 172 | 173 | ## 📋 Triển khai 174 | 175 | 1. Tạo tất cả product theo cùng một interface. Interface này nên khai báo phương thức có ý nghĩa với tất cả product. 176 | 177 | 2. Thêm phương thức factory trống vào lớp creator. Kiểu trả về của phương thức nên tương ứng với interface product chung. 178 | 179 | 3. Trong code creator tìm tất cả tham chiếu đến hàm khởi tạo product. Từng cái một, thay thế nó với lệnh gọi phương thức factory, trong khi trích xuất code tạo product vào phương thức factory. Bạn cần thêm tham số mẫu vào phương thức factory để điều khiển kiểu trả về của product. 180 | 181 | 4. Bây giờ, tạo tập hợp lớp con của creator cho từng kiểu product trong phương thức factory. Ghi đè lên phương thức factory ở lớp con và trích xuất các bit phù hợp từ hàm khởi tạo với phương thức cơ sở. 182 | 183 | 5. Nếu có quá nhiều kiểu product và nó không phù hợp tạo lớp con cho chúng, ta có thể tái sử dụng tham số điều khiển từ lớp cở sở ở lớp con. Ví dụ, bạn có một hệ thống phân cấp các lớp như sau: lớp cơ sở `Mail` với hai lớp con: `AirMail` và `GroundMail`; lớp `Transport` có `Plane`, `Truck` và `Train`. Trong khi lớp `AirMail` chỉ dùng đối tượng `Plane`, thì `GroundMail` làm việc với cả hai đối tượng `Truck` và `Train`. Bạn có thể tạo lớp con (tạm gọi `TrainMail`) để xử lý cả hai trường hợp, nhưng cũng có lựa chọn khác. Code client có thể gửi tham số vào phương thức factory của `GroundMail` để điều khiển kiểu product mà nó muốn nhận. 184 | 185 | 6. Sau cùng, nếu phương thức factory ở lớp cơ sở trống, bạn có thể chuyển nó thành trừu tượng(abstract). Nếu còn sót lại gì đó thì ta sẽ thiết lập đó là hành vi mặc định của phương thức. 186 | 187 | ## ⚖️ Ưu nhược điểm 188 | 189 | **Ưu điểm** 190 | 191 | ✔️ Tránh được kết hợp quá chặt chẽ giữa creator và concrete product. 192 | 193 | ✔️ *Single Responsibility Principle*. Bạn có thể di chuyển code tạo product vào một nơi trong chương trình, giúp code hỗ trợ dễ dàng hơn. 194 | 195 | ✔️ *Open/Closed Principle*. Bạn có thể thêm các kiểu product mới vào chương trình, mà không làm ảnh hưởng đến code client hiện tại. 196 | 197 | **Nhược điểm** 198 | 199 | ❌ Code có thể trở nên phức tạp khi bạn thêm vào quá nhiều lớp con để triển khai pattern. Trường hợp tốt nhất là khi bạn triển khai pattern bằng cách sử dụng hệ thống phân cấp của lớp creator. 200 | 201 | ## 🔁 Quan hệ với các pattern khác 202 | 203 | Nhiều pattern bắt đầu bằng cách sử dụng **Factory Method** (ít phức tạp hơn và có thể tùy chỉnh nhiều hơn thông qua các lớp con) và phát triển theo hướng **Abstract Factory**, **Prototype** hoặc **Builder** (linh hoạt hơn nhưng phức tạp hơn). 204 | 205 | Các lớp **Abstract Factory** thường dựa trên một tập hợp các **Factory Method**, nhưng bạn cũng có thể sử dụng **Prototype** để cấu trúc các phương thức trên các lớp này. 206 | 207 | Bạn có thể sử dụng **Factory Method** cùng với **Iterator** để cho phép các lớp con của collection trả về các kiểu vòng lặp khác nhau tương thích với các collection. 208 | 209 | **Prototype** không dựa trên sự kế thừa, vì vậy nó không có nhược điểm. Mặt khác, **Prototype** yêu cầu khởi tạo nhân bản đối tượng phức tạp. **Factory Method** dựa trên kế thừa nhưng không yêu cầu bước khởi tạo. 210 | 211 | **Factory Method** là một chuyên môn hóa của **Template Method**. Đồng thời, **Factory Method** có thể đóng vai trò là một bước trong một **Template Method** lớn. 212 | 213 | # Nguồn 214 | 215 | [**refactoring**](https://refactoring.guru/design-patterns/factory-method) 216 | -------------------------------------------------------------------------------- /creational-pattern/factory-method/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/factory-method/assets/intent.png -------------------------------------------------------------------------------- /creational-pattern/factory-method/assets/problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/factory-method/assets/problem.png -------------------------------------------------------------------------------- /creational-pattern/factory-method/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/factory-method/assets/pseudocode.png -------------------------------------------------------------------------------- /creational-pattern/factory-method/assets/solution1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/factory-method/assets/solution1.png -------------------------------------------------------------------------------- /creational-pattern/factory-method/assets/solution2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/factory-method/assets/solution2.png -------------------------------------------------------------------------------- /creational-pattern/factory-method/assets/solution3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/factory-method/assets/solution3.png -------------------------------------------------------------------------------- /creational-pattern/factory-method/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/factory-method/assets/structure.png -------------------------------------------------------------------------------- /creational-pattern/prototype/README.md: -------------------------------------------------------------------------------- 1 | # Prototype 2 | 3 | ## 📜 Mục đích 4 | 5 | **Prototype** là một design pattern thuộc nhóm creational, giúp bạn sao chép một đối tượng mà code của bạn sẽ không phụ thuộc vào lớp của đối tượng đó. 6 | 7 | ![intent](./assets/intent.png) 8 | 9 | ## 😟 Vấn đề 10 | 11 | Giả sử bạn đang có một đối tượng và bạn muốn tạo ra bản sao của nó. Vậy làm thế nào ? Đầu tiên bạn sẽ tạo ra một đối tượng mới có cùng lớp, sau đó bạn sẽ lấy giá trị từ tất cả các trường của đối tượng gốc và gán nó sang cho đối tượng mới. 12 | 13 | Hay lắm ! Nhưng nó có vấn đề. Không phải tất cả đối tượng đều có thể sao chép theo cách này vì có thể một vài trường của nó là riêng tư (private) và không thể truy cập từ bên ngoài đối tượng. 14 | 15 | ![problem](./assets/problem.png) 16 | 17 | *Copy đối tượng từ bên ngoài không phải lúc nào cũng tốt* 18 | 19 | Có nhiều hơn một vấn đề với cách tiếp cận này, là khi bạn biết lớp của đối tượng mà bạn tạo bản sao, code của bạn sẽ trở nên phụ thuộc vào lớp đó. Nếu điều này chưa làm bạn lo lắng thì còn một vấn đề nữa. Là thỉnh thoảng bạn chỉ biết interface của đối tượng, chứ không biết đến lớp cụ thể, khi đó tham số trong các phương thức của bạn sẽ chấp nhận bất kỳ đối tượng nào theo interface đấy. 20 | 21 | ## 😊 Giải pháp 22 | 23 | Pattern Prototype uỷ thác quá trình sao chép cho các đối tượng thực đang được sao chép. Pattern này khai báo một interface chung hỗ trợ sao chép cho tất cả đối tượng. Interface này giúp bạn sao chép đối tượng mà không cần ghép code của bạn với lớp của đối tượng. Thông thường, interface như vậy chỉ bao gồm một phương thức `clone`. 24 | 25 | Triển khai phương thức `clone` là như nhau với mọi lớp. Phương thức tạo một đối tượng của lớp hiện tại và chuyển tất cả trường giá trị của đối tượng cũ sang đối tượng mới. Thậm chí có thể sao chép cả trường riêng tư vì phần lớn ngôn ngữ lập trình cho phép đối tượng truy cập trường riêng tư của đối tượng khác nếu chúng cùng thuộc một lớp. 26 | 27 | Đối tượng hỗ trợ sao chép gọi là *prototype*. Khi đối tượng của bạn có hàng chục trường và hàng trăm cấu hình khả khi, nhân bản chúng có thể xem như một giải pháp thay thế cho tạo lớp con. 28 | 29 | ![solution](./assets/solution.png) 30 | 31 | *Các prototype tạo sẵn có thể thay thế cho phân lớp.* 32 | 33 | Cách mà nó hoạt động: bạn tạo một tập hợp các đối tượng, được cấu hình theo nhiều cách khác nhau. Khi bạn cần một đối tượng giống như đối tượng bạn đã cấu hình, bạn chỉ cần sao chép một prototype thay vì xây dựng một đối tượng mới từ đầu. 34 | 35 | ## 🚗 Thế Giới Thực 36 | 37 | Trong cuộc sống thực, các prototype được sử dụng để thực hiện các thử nghiệm khác nhau trước khi bắt đầu sản xuất hàng loạt một sản phẩm. Tuy nhiên, trong trường hợp này các prototype không tham gia vào bất kỳ quá trình sản xuất thực nào, nó chỉ đóng vai trò thụ động. Vì các prototype trong công nghiệp không thực sự tự sao chép. 38 | 39 | ![analogy](./assets/analogy.png) 40 | 41 | Một ví dụ thực nữa của pattern này quá trình nguyên phân trong sinh học. Sau khi nguyên phân, một cặp tế bào giống hệt nhau được hình thành. Tế bào gốc lúc này hoạt động như một prototype và đóng vai trò chủ động. 42 | 43 | ## 🏢 Cấu trúc 44 | 45 | ### Triển khai cơ bản 46 | 47 | ![structure1](./assets/structure1.png) 48 | 49 | 1. **Prototype** là interface khai báo phương thức sao chép. Trong đa số trường hợp, nó chỉ có một phương thức `clone`. 50 | 2. **Concrete Prototype** lớp triển khai phương thức sao chép. Ngoài việc sao chép dữ liệu của đối tượng ban đầu sang bản sao, phương pháp này cũng có thể xử lý một số trường hợp của quá trình sao chép liên quan đến việc sao chép các đối tượng được liên kết, gỡ rối các phụ thuộc đệ quy, ... 51 | 3. **Client** có thể tạo một bản sao của bất kỳ đối tượng nào theo interface prototype. 52 | 53 | ### Triển khai prototype registry 54 | 55 | ![structure2](./assets/structure2.png) 56 | 57 | 1. **Prototype Registry** cung cấp cách để truy cập dễ dàng các prototype được sử dụng thường xuyên. Nó lưu trữ một tập hợp đối tượng đã tạo sẵn cho việc sao chép. Prototype registry đơn giản nhất là là một hashmap `name → prototype`. Tuy nhiên, nếu bạn cần các tiêu chí tìm kiếm tốt hơn, bạn có thể tự xây dựng một phiên bản registry mạnh mẽ hơn. 58 | 59 | ## 👨‍💻 Mã giả 60 | 61 | Trong ví dụ này, Prototype Pattern cho phép bạn tạo các bản sao chính xác của các đối tượng hình học mà không cần ghép code với các lớp của chúng. 62 | 63 | ![pseudocode](./assets/pseudocode.png) 64 | 65 | Tất cả các lớp hình dạng theo sau một interface, cung cấp phương thức sao chép. Lớp con có thể gọi phương thức sao chép của lớp cha trước khi sao chép các trường giá trị của chính nó vào đối tượng kết quả. 66 | 67 | ```c 68 | // Prototype cơ sở. 69 | abstract class Shape is 70 | field X: int 71 | field Y: int 72 | field color: string 73 | 74 | // Hàm khởi tạo thông thường. 75 | constructor Shape() is 76 | // ... 77 | 78 | // Hàm khởi tạo prototype. Đối tượng rỗng được tạo 79 | // với giá trị từ đối tượng đã tồn tại. 80 | constructor Shape(source: Shape) is 81 | this() 82 | this.X = source.X 83 | this.Y = source.Y 84 | this.color = source.color 85 | 86 | // Phương thức clone trả về một trong những lớp con Shape. 87 | abstract method clone():Shape 88 | 89 | 90 | // Concrete prototype. Phương thức sao chép tạo đối tượng mới 91 | // và truyền vào hàm khởi tạo. Cho đến khi hàm khởi tạo kết thúc 92 | // nó sẽ tham chiếu đến bản sao. Do đó không có ai có thể truy cập 93 | // đến bản sao chưa hoàn chỉnh. Nó giữ cho bản sao nhất quán. 94 | class Rectangle extends Shape is 95 | field width: int 96 | field height: int 97 | 98 | constructor Rectangle(source: Rectangle) is 99 | // Lệnh gọi hàm khởi tạo cha là cần thiết 100 | // để sao chép trường riêng tư được định 101 | // nghĩa trong lớp cha 102 | super(source) 103 | this.width = source.width 104 | this.height = source.height 105 | 106 | method clone():Shape is 107 | return new Rectangle(this) 108 | 109 | 110 | class Circle extends Shape is 111 | field radius: int 112 | 113 | constructor Circle(source: Circle) is 114 | super(source) 115 | this.radius = source.radius 116 | 117 | method clone():Shape is 118 | return new Circle(this) 119 | 120 | 121 | // Đâu đó trong code client. 122 | class Application is 123 | field shapes: array of Shape 124 | 125 | constructor Application() is 126 | Circle circle = new Circle() 127 | circle.X = 10 128 | circle.Y = 10 129 | circle.radius = 20 130 | shapes.add(circle) 131 | 132 | Circle anotherCircle = circle.clone() 133 | shapes.add(anotherCircle) 134 | // Biến `anotherCircle` bao gồm bản sao 135 | // chính xác của đối tượng `circle`. 136 | 137 | Rectangle rectangle = new Rectangle() 138 | rectangle.width = 10 139 | rectangle.height = 20 140 | shapes.add(rectangle) 141 | 142 | method businessLogic() is 143 | // Prototype chắc chắn vì nó giúp bạn tạo ra bản sao của đối 144 | // tượng mà không cần biết bất cứ điều gì về kiểu của nó. 145 | Array shapesCopy = new Array of Shapes. 146 | 147 | // Ví dụ, bạn không biết chính xác phần tử trong mảng shape. 148 | // Tất cả những gì ta biết chỉ là đấy là hình dạng. 149 | // Nhưng nhờ tính đa hình, khi ta gọi phương thức `clone` 150 | // trên một hình dạng, chương trình kiểm tra lớp có thực và 151 | // chạy phương thức clone phù hợp đã định nghĩa trong lớp. 152 | // Đó là lý do vì sao ta nhận được bản sao phù hợp thay vì 153 | // một mảng đối tượng Shape thông thường. 154 | foreach (s in shapes) do 155 | shapesCopy.add(s.clone()) 156 | 157 | // Mảng `shapeCopy` bao gồm các bản sao chính xác 158 | // của mảng `shape`. 159 | ``` 160 | 161 | ## 💡 Ứng dụng 162 | 163 | **🐞 Sử dụng Prototype khi code bạn không muốn phụ thuộc vào một lớp cụ thể của đối tượng cần sao chép** 164 | 165 | ⚡ Chuyện này xảy ra khi bạn làm việc với đối tượng được truyền đến từ code bên thứ ba thông qua một vài interface. Các lớp cụ thể của đối tượng này là không xác định nên bạn không thể phụ thuộc nó, ngay cả khi muốn. 166 | 167 | Pattern Prototype cung cấp cho code client một interface chung để làm việc với tất cả đối tượng hỗ trợ sao chép. Interface này giúp code client độc lập với lớp cụ thể từ đối tượng mà nó sao chép. 168 | 169 | **🐞 Sử dụng Prototype khi bạn muốn giảm số lượng lớp con, chỉ khác nhau về cách chúng khởi tạo các đối tượng tương ứng** 170 | 171 | ⚡ Pattern Prototype cho phép bạn sử dụng một tập hợp các đối tượng được tạo sẵn, được cấu hình theo nhiều cách khác nhau, là prototype. 172 | 173 | Thay vì khởi tạo một lớp con phù hợp với một số cấu hình, client chỉ cần tìm kiếm một prototype thích hợp và sao chép nó. 174 | 175 | ## 📋 Triển khai 176 | 177 | 1. Tạo interface prototype và khai báo phương thức sao chép trong đó. Hoặc thêm phương thức vào tất cả các lớp của hệ phân cấp lớp, nếu bạn có. 178 | 179 | 2. Lớp prototype phải định nghĩa hàm khởi tạo thay thế để chấp nhận đối tượng của lớp đó như một tham số. Hàm khởi tạo phải sao chép giá trị từ tất cả trường đã định nghĩa trong lớp từ đối tượng được truyền vào đối tượng mới được tạo. Nếu bạn đang thay đổi một lớp con, bạn phải gọi hàm khởi tạo cha để cho phép lớp cha xử lý việc sao chép các trường riêng tư của nó. 180 | 181 | Nếu ngôn ngữ lập trình của bạn không hỗ trợ phương thức overloading, bạn phải định nghĩa phương thức đặc biệt cho sao chép đối tượng dữ liệu. Hàm khởi tạo là một nơi tiện lợi để làm điều này vì nó cung cấp kết quả đối tượng ngay sau khi bạn gọi toán tử `new`. 182 | 183 | 3. Phương thức sao chép thường chỉ bao gồm một dòng: chạy một toán tử `new` với phiên bản prototype của hàm khởi tạo. Lưu ý rằng mọi lớp phải ghi đè(override) rõ ràng phương thức sao chép và sử dụng tên lớp của chính nó cùng với toán tử `new`. Nếu không, phương thức sao chép có thể tạo ra một đối tượng của lớp cha. 184 | 185 | 4. Tuỳ chọn, tạo một prototype registry để lưu trữ một danh mục các prototype thường được sử dụng. 186 | 187 | Bạn có thể triển khai registry như một lớp factory hoặc đặt nó vào một lớp prototype cơ sở với phương thức tĩnh cho tìm nạp prototype. Phương thức này tìm kiếm prototype dựa trên các tiêu chí tìm kiếm mà code client truyền đến phương thức. Tiêu chí có thể là một chuỗi đơn giản hoặc cũng có thể là một tập hợp các tham số tìm kiếm phức tạp. Sau khi tìm thấy prototype thích hợp, registry sẽ sao chép nó và trả lại bản sao cho client. 188 | 189 | 5. Cuối cùng, thay thế các lệnh gọi trực tiếp đến các hàm khởi tạo của lớp con bằng các lệnh gọi đến phương thức factory của prototype registry. 190 | 191 | ## ⚖️ Ưu nhược điểm 192 | 193 | ### Ưu điểm 194 | 195 | ✔️ Bạn có thể sao chép đối tượng mà không cần quan tâm đến lớp cụ thể. 196 | 197 | ✔️ Bạn có thể tránh code khởi tạo lặp đi lặp lại bằng việc sao chép các prototype có sẵn. 198 | 199 | ✔️ Bạn có thể tạo các đối tượng phức tạp thuận tiện. 200 | 201 | ✔️ Bạn nhận được một giải pháp thay thế cho kế thừa khi phải xử lý các cấu hình cho các đối tượng phức tạp. 202 | 203 | ### Nhược điểm 204 | 205 | ❌ Sao chép các đối tượng phức tạp có tham chiếu vòng tròn có thể rất khó. 206 | 207 | ## 🔁 Quan hệ với các pattern khác 208 | 209 | Nhiều pattern bắt đầu bằng cách sử dụng **Factory Method** (ít phức tạp hơn và có thể tùy chỉnh nhiều hơn thông qua các lớp con) và phát triển theo hướng **Abstract Factory**, **Prototype** hoặc **Builder** (linh hoạt hơn nhưng phức tạp hơn). 210 | 211 | Các thiết kế sử dụng nhiều **Composite** và **Decorator** thường có thể được hưởng lợi từ việc sử dụng **Prototype**. Áp dụng pattern cho phép bạn sao chép các cấu trúc phức tạp thay vì xây dựng lại chúng từ đầu. 212 | 213 | Các lớp **Abstract Factory** thường dựa trên một tập hợp các **Factory Method**, nhưng bạn cũng có thể sử dụng **Prototype** để cấu trúc các phương thức trên các lớp này. 214 | 215 | **Prototype** không dựa trên sự kế thừa, vì vậy nó không có nhược điểm. Mặt khác, **Prototype** yêu cầu khởi tạo nhân bản đối tượng phức tạp. **Factory Method** dựa trên kế thừa nhưng không yêu cầu bước khởi tạo. 216 | 217 | **Prototype** có thể hữu ích khi bạn cần lưu các bản sao của **Commands** vào lịch sử. 218 | 219 | Đôi khi **Prototype** có thể là một giải pháp thay thế đơn giản hơn cho **Memento**. Điều này hoạt động nếu đối tượng, trạng thái mà bạn muốn lưu trữ trong lịch sử, khá đơn giản và không có liên kết đến tài nguyên bên ngoài hoặc các liên kết dễ thiết lập lại. 220 | 221 | Tất cả các **Abstract Factory**, **Builder** và **Prototype** đều có thể được triển khai dưới dạng các **Singleton**. 222 | 223 | # Nguồn 224 | 225 | [**refactoring**](https://refactoring.guru/design-patterns/prototype) -------------------------------------------------------------------------------- /creational-pattern/prototype/assets/analogy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/prototype/assets/analogy.png -------------------------------------------------------------------------------- /creational-pattern/prototype/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/prototype/assets/intent.png -------------------------------------------------------------------------------- /creational-pattern/prototype/assets/problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/prototype/assets/problem.png -------------------------------------------------------------------------------- /creational-pattern/prototype/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/prototype/assets/pseudocode.png -------------------------------------------------------------------------------- /creational-pattern/prototype/assets/solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/prototype/assets/solution.png -------------------------------------------------------------------------------- /creational-pattern/prototype/assets/structure1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/prototype/assets/structure1.png -------------------------------------------------------------------------------- /creational-pattern/prototype/assets/structure2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/prototype/assets/structure2.png -------------------------------------------------------------------------------- /creational-pattern/singleton/README.md: -------------------------------------------------------------------------------- 1 | # Singleton 2 | 3 | ## 📜 Mục đích 4 | 5 | **Singleton** là một design pattern thuộc nhóm creational giúp bạn tạo ra một lớp chỉ với một thực thế duy nhất, trong khi cung cấp điểm truy cập toàn cục cho thực thế đấy. 6 | 7 | ![intent](./assets/intent.png) 8 | 9 | ## 😟 Vấn đề 10 | 11 | Singleton cùng lúc giải quyết hai vấn đề, vi phạm đến *Nguyên tắc Trách nhiệm đơn lẻ (Single Responsibility Principle)*: 12 | 13 | 1. **Đảm bảo mỗi lớp chỉ có một thực thể**: Tại sao bất kỳ ai cũng muốn kiểm soát số lượng thực thể (instance) mà một lớp có ? Lý do phổ biến nhất là để quản lý truy cập đến các tài nguyên chung, vd như cơ sở dữ liệu hay file. 14 | 15 | Tưởng tượng, bạn đã tạo một đối tượng nhưng sau một thời gian bạn quyết định tạo một đối tượng mới. Bởi vì Singleton bảo đảm chỉ có duy nhất một thực thể được tạo ra, nên thay vì nhận một đối tượng mới hoàn toàn bạn sẽ nhận về đối tượng đã được tạo. 16 | 17 | Lưu ý: Điều này không thể thực thi với các hàm khởi tạo (constructor) thông thường vì chúng sẽ luôn trả về đối tượng mới. 18 | 19 | ![problem](./assets/problem.png) 20 | 21 | *Client sẽ không nhận ra được họ đang làm việc với một đối tượng mọi lúc* 22 | 23 | 2. **Cung cấp một điểm truy cập toàn cục cho thực thể**: Hãy nhớ lại các biến toàn cục mà bạn đã dùng để lưu trữ một số đối tượng thiết yếu. Mặc dù khá tiện dụng nhưng chúng lại không an toàn vì bất kỳ đoạn code nào cũng có khả năng ghi đè lên nội dung của biến đó và làm hỏng chương trình. 24 | 25 | Giống như biến toàn cục, Singleton giúp bạn truy cập đến các đối tượng ở bất cứ đâu trong chương trình. Tuy nhiên, không như biến, nó sẽ bảo vệ các thực thể khỏi bị ghi đè bởi code khác. 26 | 27 | Mặt khác của vấn đề: bạn không muốn code giải quyết vấn đề #1 bị phân tán khắp chương trình. Sẽ tốn hơn nếu có nó trong một lớp, đặc biệt nếu phần còn lại của code bạn đã phụ thuộc vào nó. 28 | 29 | ## 😊 Giải pháp 30 | 31 | Tất cả triển khai của Singelton đều có hai bước chung là: 32 | - Mặc định các hàm khởi tạo là riêng tư, để tránh các đối tượng khác sử dụng toán từ `new` với lớp Singleton. 33 | - Tạo một phương thức tĩnh hoạt động như một hàm khởi tạo. Bên trong phương thức này, sẽ gọi đến hàm khởi tạo riêng tư để tạo một đối tượng và lưu nó vào một trường tĩnh. Tất cả lệnh gọi theo sau phương thức này trả về một đối tượng cache. 34 | 35 | Nếu code của bạn có quyền truy cập vào lớp Singleton thì nó có thể gọi phương thức tĩnh của Singleton. Vì vậy, bất cứ khi nào phương thức đó được gọi, sẽ có cùng một đối tượng được trả về. 36 | 37 | ## 🚗 Thế Giới Thực 38 | 39 | Chính phủ là một ví dụ điển hình của pattern Singleton. Mỗi quốc gia chỉ có thể có một chính phủ chính thức. Bất kể danh tính cá nhân của các cá nhân đại diện chính phủ, danh hiệu, “Chính phủ X”, đều là điểm truy cập toàn cục để xác định danh tính nhóm người phụ trách. 40 | 41 | ## 🏢 Cấu trúc 42 | 43 | ![structure](./assets/structure.png) 44 | 45 | 1. **Lớp Singleton** khai báo phương thức tĩnh `getInstance` để trả về cùng một thực thể của lớp đó. 46 | 47 | Hàm khởi tạo Singleton sẽ bị ẩn ở code client, gọi phương thức `getInstance` là cách duy nhất để lấy đối tượng Singleton. 48 | 49 | ## 👨‍💻 Mã giả 50 | 51 | Trong ví dụ này, cơ sở dữ liệu sẽ kết nối với một lớp hoạt động như một Singleton. Nó không có hàm khởi tạo công khai, cách duy nhất để lấy đối tượng là gọi phương thức `getInstane`. Phương thức này lưu đối tượng tạo trong lần đầu vào cache và sử dụng nó cho các lần gọi tiếp theo. 52 | 53 | ```c 54 | // Lớp Database định nghĩa phương thức `getInstance` để giúp 55 | // client truy cập vào cùng đối tượng mà cơ sở dữ liệu kết 56 | // nối trong suốt chương trình. 57 | class Database is 58 | // Trường này để lưu trữ thực thể singleton nên khai 59 | // báo là tĩnh (static) 60 | private static field instance: Database 61 | 62 | // Hàm khởi tạo singleton luôn luôn là riêng tư 63 | // để tránh khởi tạo trực tiếp với toán tử `new`. 64 | private constructor Database() is 65 | // Một vài code khởi tạo ở đây, như là 66 | // kết nối đến server của cơ sở dữ liệu. 67 | 68 | // Phương thức tĩnh điều khiển truy cập đến thực thể 69 | // singleton. 70 | public static method getInstance() is 71 | if (Database.instance == null) then 72 | acquireThreadLock() and then 73 | // Đảm bảo rằng thực thể chưa được tạo bởi 74 | // luồng(thread) khác trong khi luồng này đang 75 | // đợi giải phóng lock. 76 | if (Database.instance == null) then 77 | Database.instance = new Database() 78 | return Database.instance 79 | 80 | // Cuối cùng, bất kỳ singleton nào cũng có một vài logic nghiệp vụ 81 | // để có thể thực thi các thực thể của nó. 82 | public method query(sql) is 83 | // Ví dụ, tất cả truy vấn cơ sở dữ liệu của ứng dụng 84 | // trong phương thức này. Do đó bạn có thể đặt logic 85 | // điều tiết hoặc cache vào đây. 86 | 87 | class Application is 88 | method main() is 89 | Database foo = Database.getInstance() 90 | foo.query("SELECT ...") 91 | // ... 92 | Database bar = Database.getInstance() 93 | bar.query("SELECT ...") 94 | // Biến `bar` sẽ bao gồm cùng đối tượng với 95 | // biến `foo`. 96 | ``` 97 | 98 | ## 💡 Ứng dụng 99 | 100 | **🐞 Sử dụng mẫu Singleton khi một lớp trong chương trình của bạn chỉ nên có một thực thể duy nhất cho tất cả client** 101 | 102 | ⚡ Thường thấy khi dùng một đối tượng cơ sở dữ liệu duy nhất kết nối đến các phần khác nhau trong ứng dụng. Singleton vô hiệu hoá tất cả cách tạo đối tượng của lớp trừ phương thức tạo đặc biệt. 103 | 104 | **🐞 Sử dụng Singleton khi bạn cần kiểm soát chặt chẽ hơn đối với các biến toàn cục** 105 | 106 | ⚡ Không giống như các biến toàn cục, Singleton đảm bảo rằng không có gì khác ngoại trừ chính lớp Singleton có thể thay đổi được thực thể được lưu. 107 | 108 | *Lưu ý rằng bạn luôn có thể điều chỉnh giới hạn thực thể trong Singleton và cho phép tạo bất kỳ số lượng thực thể nào. Đoạn code duy nhất cần thay đổi là phần thân của phương thức `getInstance`.* 109 | 110 | ## 📋 Triển khai 111 | 112 | 1. Thêm một trường tĩnh vào lớp cho lưu trữ thực thể singleton. 113 | 114 | 2. Khai báo phương thức tĩnh công khai cho lấy thực thể singleton. 115 | 116 | 3. Triển khai "lazy initialization" bên trong phương thức tĩnh. Nó tạo ra đối tượng mới cho lần gọi đầu tiên và đặt vào trong trường tĩnh. Phương thức luôn trả về thực thể đó cho tất cả gần gọi tiếp theo. 117 | 118 | 4. Thiết lập hàm khởi tạo riêng tư cho lớp. Phương thức tĩnh của lớp vẫn có thể gọi hàm khởi tạo nhưng không thể gọi đối tượng khác. 119 | 120 | 5. Đi đến code client và thay thế tất cả lệnh gọi trực tiếp đến hàm khởi tạo singleton bằng cách gọi đến phương thức khởi tạo tĩnh. 121 | 122 | ## ⚖️ Ưu nhược điểm 123 | 124 | ### Ưu điểm 125 | 126 | ✔️ Bạn có thể chắc chắn mỗi lớp chỉ có một thực thể. 127 | 128 | ✔️ Bạn có điểm truy cập toàn cục đến thực thể đó. 129 | 130 | ✔️ Đối tượng singleton được tạo chỉ một lần duy nhất cho lệnh gọi đầu tiên. 131 | 132 | ### Nhược điểm 133 | 134 | ❌ Như đã nói, cả hai vấn đề của Singleton đều vi phạm *Nguyên tắc trách nhiệm đơn lẻ*. 135 | 136 | ❌ Singleton có thể giấu đi các thiết kế tệ. Ví dụ như khi các thành phần trong chương trình biết quá nhiều về nhau. 137 | 138 | ❌ Pattern yêu cầu được xử lý đặc biệt trong môi trường đa luồng, để nhiều luồng sẽ không tạo ra một đối tượng Singleton nhiều lần. 139 | 140 | ❌ Gặp khó khăn khi thực hiện unit test cho code client của Singleton, vì các framework test dựa trên kế thừa khi tạo ra các đối tượng giả. Và hàm khởi tạo của lớp Singleton là private cùng với việc ghi đè các phương thức tĩnh là không thể trong hầu hết các ngôn ngữ, nên bạn sẽ cần phải nghĩ ra một cách sáng tạo để mô phỏng lớp singleton. Hoặc chỉ không thực hiện test. Hoặc không sử dụng Singleton. 141 | 142 | ## 🔁 Quan hệ với các pattern khác 143 | 144 | Một lớp **Facade** thường có thể được chuyển đổi thành **Singleton** vì một đối tượng facade duy nhất là đủ trong hầu hết các trường hợp. 145 | 146 | **Flyweight** sẽ giống với **Singleton** nếu bạn bằng cách nào đó giảm được tất cả các trạng thái được chia sẻ của các đối tượng xuống chỉ còn một đối tượng flyweight. Nhưng có hai điểm khác biệt cơ bản giữa các pattern này: 147 | 148 | - Chỉ nên có một thực thể **Singleton**, trong khi lóp **Flyweight** có thể có nhiều thực thể với các trạng thái nội tại khác nhau. 149 | - Đối tượng **Singleton** có thể thay đổi được. Đối tượng **Flyweight** là bất biến. 150 | 151 | Tất cả các **Abstract Factory**, **Builder** và **Prototype** đều có thể được triển khai dưới dạng các **Singleton**. 152 | 153 | # Nguồn 154 | 155 | [**refactoring**](https://refactoring.guru/design-patterns/singleton) -------------------------------------------------------------------------------- /creational-pattern/singleton/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/singleton/assets/intent.png -------------------------------------------------------------------------------- /creational-pattern/singleton/assets/problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/singleton/assets/problem.png -------------------------------------------------------------------------------- /creational-pattern/singleton/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/creational-pattern/singleton/assets/structure.png -------------------------------------------------------------------------------- /structural-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Structural Design Patterns 2 | 3 | Structural Pattern giải thích cách tập hợp các đối tượng và lớp thành các cấu trúc lớn hơn, trong khi vẫn giữ cho cấu trúc linh hoạt và hiệu quả. 4 | 5 | ## Adapter 6 | 7 | ![adapter-mini](./assets/adapter-mini.png) 8 | 9 | Cho phép các đối tượng có interface không tương thích cộng tác với nhau. 10 | 11 | ## Bridge 12 | 13 | ![bridge-mini](./assets/bridge-mini.png) 14 | 15 | Giúp bạn tách một lớp khổng lồ hoặc một tập hợp lớp có quan hệ gần gũi với nhau thành hai hệ thống phân cấp lớp riêng biệt là - abstraction(trừu tượng) và implementation(triển khai) - có thể phát triển độc lập với nhau. 16 | 17 | ## Composite 18 | 19 | ![composite-mini](./assets/composite-mini.png) 20 | 21 | Giúp bạn sắp xếp các đối tượng thành cấu trúc cây và sau đó làm việc với các cấu trúc này như thể chúng là các đối tượng riêng lẻ. 22 | 23 | ## Decorator 24 | 25 | ![decorator-mini](./assets/decorator-mini.png) 26 | 27 | Giúp bạn thêm một hành vi mới vào đối tượng bằng cách đặt đối tượng đó vào trong một đối tượng đặc biệt có chứa hành vi đấy 28 | 29 | ## Facade 30 | 31 | ![facade-mini](./assets/facade-mini.png) 32 | 33 | Cung cấp interface đơn giản cho thư viện, framework hoặc bất kỳ tập hợp lớp phức tạp nào khác. 34 | 35 | ## Flyweight 36 | 37 | ![flyweight-mini](./assets/flyweight-mini.png) 38 | 39 | Giúp bạn chỉnh các đối tượng vừa với lượng RAM khả dụng bằng các chia sẻ trạng thái chung giữa các đối tượng thay vì giữ tất cả dữ liệu ở mỗi đối tượng. 40 | 41 | ## Proxy 42 | 43 | ![proxy-mini](./assets/proxy-mini.png) 44 | 45 | Cho phép bạn cung cấp một vật thay thế hoặc vật giữ chỗ cho một đối tượng khác. Một proxy kiểm soát quyền truy cập đến đối tượng ban đầu, cho phép bạn thực hiện điều gì đó trước hoặc sau khi yêu cầu được chuyển đến đối tượng ban đầu. -------------------------------------------------------------------------------- /structural-pattern/adapter/README.md: -------------------------------------------------------------------------------- 1 | # Adapter 2 | 3 | ## 📜 Mục đích 4 | 5 | **Adapter** là một design pattern thuộc nhóm structural cho phép các đối tượng có interface không tương thích cộng tác với nhau. 6 | 7 | ![intent](./assets/intent.png) 8 | 9 | ## 😟 Vấn đề 10 | 11 | Tượng tượng bạn đang tạo một ứng dụng giám sát thị trường chứng khoán. Ứng dụng sẽ tải dữ liệu chứng khoán từ nhiều nguồn dưới dạng XML, và hiển thị nó dưới dạng biểu đồ và sơ đồ cho người dùng. 12 | 13 | Vào một thời điểm nào đó, bạn quyết định cải tiến ứng dụng bằng việc tích hợp một thư viện bên thứ ba cho phân tích dữ liệu. Nhưng có một vấn đề là thư viện này chỉ làm việc với dữ liệu dạng JSON. 14 | 15 | ![problem](./assets/problem.png) 16 | 17 | *Bạn không thể sử dụng thư viện phân tích vì nó yêu cầu dữ liệu không tương thích với ứng dụng của bạn* 18 | 19 | Nếu bạn thay đổi thư viện để nó làm việc với XML nó có thể ảnh hưởng đến những đoạn code hiện có trong thư viện. Hoặc tệ hơn là ngay từ đầu bạn không được phép truy cập đến mã nguồn của thư viện. Nên cách tiếp cận này là bất khả thi. 20 | 21 | ## 😊 Giải pháp 22 | 23 | Bạn có thể tạo ra một *adapter*, là một đối tượng đặc biệt có thể chuyển đổi interface của một đối tượng cho các đối tượng khác hiểu được nó. 24 | 25 | Adapter sẽ bọc một đối tượng để giấu đi sự phức tạp đằng sau chuyển đổi đấy. Đối tượng được bọc thậm chí còn không biết gì về adapter. Ví dụ như bạn có thể bọc một đối tượng có đơn vị đo lường là kilomet và met, với bộ adapter sẽ chuyển đổi tất cả dữ liệu sang các đơn vị là feet và miles. 26 | 27 | Adapter không chỉ có thể chuyển đổi dữ liệu thành nhiều định dạng khác nhau mà còn có thể giúp các đối tượng có interface khác nhau cộng tác. Đây là cách nó hoạt động: 28 | 1. Adapter lấy interface và tương thích của nó với một đối tượng đã tồn tại. 29 | 2. Sử dụng interface, đối tượng đã tồn tại có thể gọi đến phương thức adapter. 30 | 3. Khi nhận được lệnh gọi, adapter sẽ chuyển yêu cầu đến đối tượng thứ hai, nhưng theo một định dạng và trật tự mà đối tượng thứ hai có thể hiểu được. 31 | 32 | Đôi khi, ta có thể tạo adapter hai chiều để chuyển đổi cuộc gọi theo cả hai hướng. 33 | 34 | ![solution](./assets/solution.png) 35 | 36 | Trở về với ứng dụng thị trường chứng khoán, để giải quyết tình trạng định dạng không tương thích, ta sẽ tạo một adapter XML-to-JSON cho tất cả các lớp trong thư viện phân tích để code bạn làm việc trực tiếp. Sau đó bạn điều chỉnh code của bạn làm việc với thư viện thông qua adapter. 37 | 38 | Khi adapter nhận được một lệnh gọi, nó sẽ dịch dữ liệu XML sang cấu trúc JSON và chuyển hướng cuộc gọi đến các phương thức thích hợp của đối tượng phân tích đã được bọc. 39 | 40 | ## 🚗 Thế Giới Thực 41 | 42 | ![analogy](./assets/analogy.png) 43 | 44 | Khi bạn đi du lịch từ Mỹ đến châu Âu lần đầu tiên, bạn có thể sẽ sốc khi sạc laptop của mình. Vì chuẩn ổ cắm và nguồn điện của các quốc gia khác nhau sẽ khác nhau. Đó là lý do tại sao phích cắm Hoa Kỳ sẽ không phù hợp với ổ cắm ở Đức. Vấn đề có thể được giải quyết bằng cách sử dụng bộ chuyển đổi có ổ cắm kiểu Hoa Kỳ và phích cắm kiểu châu Âu. 45 | 46 | ## 🏢 Cấu trúc 47 | 48 | ### Đối tượng adapter 49 | 50 | Việc triển khai này sử dụng nguyên tắc cấu thành đối tượng: adapter triển khai interface của một đối tượng và bọc một đối tượng khác. Nó có thể triển khai trên mọi ngôn ngữ lập trình. 51 | 52 | ![object_structure](./assets/structure1.png) 53 | 54 | 1. **Client** là lớp bao gồm các code logic hiện có của chương trình. 55 | 2. **Client Interface** mô tả giao thức mà các lớp khác phải theo để có thể cộng tác với code client. 56 | 3. **Service** là một vài lớp hữu ích (thường là bên thứ 3 hoặc kế thừa). Client không thể sử dụng trực tiếp vì không tương thích interface. 57 | 4. **Adapter** là lớp có thể làm việc với cả client và service. Nó triển khai client interface trong khi bọc đối tượng service. Adapter nhận lệnh gọi từ client thông qua adapter interface và dịch nó, sau đó nó gọi lại đối tượng service được bọc dưới định dạnh service có thể hiểu được. 58 | 5. Code client không cần phải ghép với lớp adapter cụ thể miễn là nó làm việc với adapter thông qua client interface. Nhờ điều đó, bạn có thể thêm kiểu adapter mới vào chương trình mà không ảnh hưởng đến code client. Điều này có thể hữu ích khi interface của lớp service có thay đổi: bạn có thể tạo lớp adapter mới mà không cần thay đổi code client. 59 | 60 | ### Lớp adapter 61 | 62 | Triển khai này sử dụng kế thừa: adapter kế thừa interface từ cả hai đối tượng vào cùng thời điểm. Lưu ý là cách tiếp cận này chỉ triển khai trên các ngôn ngữ lập trình hỗ trợ đa kế thừa, như C++. 63 | 64 | ![class_structure](./assets/structure2.png) 65 | 66 | **Lớp Adapter** không cần bọc bất kỳ đối tượng nào vì nó sẽ kế thừa hành vi từ cả client và service. Sự chuyển đối diễn ra bên trong phương thức override. Adapter kết quả có thể sử dụng 67 | thay thể cho một lớp client hiện có. 68 | 69 | ## 👨‍💻 Mã giả 70 | 71 | Ví dụ cho pattern Adapter sẽ dựa trên câu thành ngữ: "chốt gỗ vuông trong lỗ tròn"(Square pegs in round holes). 72 | 73 | ![pseudocode](./assets/pseudocode.png) 74 | 75 | Adapter giả vờ là một chốt tròn(round peg), có bán kính bằng một nửa đường chéo của hình vuông (nói cách khác, là bán kính của hình tròn nhỏ nhất có thể chứa vừa hình vuông). 76 | 77 | ```c 78 | // Giả sử bạn có hai lớp với interface tương thích: 79 | // RoundHole và RoundPeg. 80 | class RoundHole is 81 | constructor RoundHole(radius) { ... } 82 | 83 | method getRadius() is 84 | // Trả về bán kính của lỗ. 85 | 86 | method fits(peg: RoundPeg) is 87 | return this.getRadius() >= peg.getRadius() 88 | 89 | class RoundPeg is 90 | constructor RoundPeg(radius) { ... } 91 | 92 | method getRadius() is 93 | // Trả về bán kính của chốt. 94 | 95 | 96 | // Nhưng có một lớp không tương thích là: SquarePeg. 97 | class SquarePeg is 98 | constructor SquarePeg(width) { ... } 99 | 100 | method getWidth() is 101 | // Trả về độ dài của chốt. 102 | 103 | 104 | // Lớp adapter giúp bạn cho chốt vuông vào vừa lỗ tròn. 105 | // Nó mở rộng lớp RoundPeg để giúp đối tượng adapter hành 106 | // động như một chốt tròn. 107 | class SquarePegAdapter extends RoundPeg is 108 | // Thực tế, adapter bao gồm một thực thể của 109 | // lớp SquarePeg 110 | private field peg: SquarePeg 111 | 112 | constructor SquarePegAdapter(peg: SquarePeg) is 113 | this.peg = peg 114 | 115 | method getRadius() is 116 | // Apdater giả vờ là một chốt tròn với bán kính 117 | // vừa với chốt vuông mà adapter thực sự bọc. 118 | return peg.getWidth() * Math.sqrt(2) / 2 119 | 120 | 121 | // Đâu đó trong code client. 122 | hole = new RoundHole(5) 123 | rpeg = new RoundPeg(5) 124 | hole.fits(rpeg) // true 125 | 126 | small_sqpeg = new SquarePeg(5) 127 | large_sqpeg = new SquarePeg(10) 128 | hole.fits(small_sqpeg) // điều này sẽ không biên dịch (các loại không tương thích) 129 | 130 | small_sqpeg_adapter = new SquarePegAdapter(small_sqpeg) 131 | large_sqpeg_adapter = new SquarePegAdapter(large_sqpeg) 132 | hole.fits(small_sqpeg_adapter) // true 133 | hole.fits(large_sqpeg_adapter) // false 134 | ``` 135 | 136 | ## 💡 Ứng dụng 137 | 138 | **🐞 Sử dụng lớp Adapter khi bạn muốn dùng một số lớp hiện có, nhưng interface của nó không tương thích với code của bạn** 139 | 140 | ⚡ Adapter cho phép bạn tạo một lớp trung gian đóng vai trò như một trình dịch giữa code của bạn và lớp kế thừa, lớp của bên thứ 3 hoặc bất kỳ lớp nào khác có interface không tương thích. 141 | 142 | **🐞 Sử dụng Adapter khi bạn muốn sử dụng lại các lớp con hiện có nhưng thiếu một số hàm chung không thể thêm vào lớp cha** 143 | 144 | ⚡ Bạn có thể mở rộng từng lớp con và đưa hàm còn thiếu vào các lớp con mới. Tuy nhiên, bạn sẽ cần phải sao chép code trên tất cả các lớp mới này, điều này khá tệ. 145 | 146 | Giải pháp tốt hơn là đưa các hàm bị thiếu vào lớp adapter. Sau khi bạn bọc đối tượng với chức năng thiếu trong adapter, thu được các tính năng cần thiết một cách linh động. Để hoạt động, các lớp đích phải có interface chung và trường của adapter phải theo interface. Các tiếp cận này có phần tương đồng pattern Decorator. 147 | 148 | ## 📋 Triển khai 149 | 150 | 1. Chắc chắn rằng bạn có ít nhất hai lớp với interface không tương thích: 151 | - Lớp service, bạn không thể thay đổi (như bên thứ 3 hay kế thừa) 152 | - Một hoặc nhiều lớp client sẽ có ích khi sử dụng lớp service. 153 | 154 | 2. Khai báo client interface và mô tả cách client giao tiếp với service. 155 | 156 | 3. Tạo lớp adapter theo client interface. Tạm thời để trống tất cả phương thức. 157 | 158 | 4. Thêm trường vào lớp adapter để lưu trữ tham chiếu đến đối tượng service. Cách phổ biến là tạo trường này thông qua hàm khởi tạo, nhưng đôi khi truyền nó cho adapter khi gọi các phương thức của nó sẽ thuận tiện hơn. 159 | 160 | 5. Triển khai tất cả phương thức của client interface trong lớp adapter. Adapter nên uỷ thác phần lớp phần lớn công việc thực cho đối tượng service, và chỉ xử lý interface hoặc chuyển đổi định dạng dữ liệu. 161 | 162 | 6. Client nên sử dụng adapter thông qua client interface. Điều này giúp bạn thay đổi hoặc mở rộng adapter mà không ảnh hưởng đến code client. 163 | 164 | ## ⚖️ Ưu nhược điểm 165 | 166 | ### Ưu điểm 167 | 168 | ✔️ *Single Responsibility Principle* Bạn có thể tách interface hoặc code chuyển đổi dữ liệu khỏi logic nghiệp vụ chính của chương trình. 169 | 170 | ✔️ *Open/Closed Principle* Bạn có thể giới thiệu các loại adapter mới vào chương trình mà không ảnh hưởng code client hiện có, miễn là chúng hoạt động với adapter thông qua client interface. 171 | 172 | ### Nhược điểm 173 | 174 | ❌ Độ phức tạp tổng thể của code tăng lên vì bạn cần giới thiệu một tập hợp các interface và lớp mới. Đôi khi, việc thay đổi lớp service sao cho phù hợp với phần còn lại của code của bạn sẽ đơn giản hơn. 175 | 176 | ## 🔁 Quan hệ với các pattern khác 177 | 178 | **Bridge** thường được thiết kế từ trước, cho phép bạn phát triển các phần của ứng dụng một cách độc lập với nhau. Mặt khác, **Adapter** thường được sử dụng với một ứng dụng hiện có để một số lớp không tương thích hoạt động với nhau. 179 | 180 | **Adapter** thay đổi interface của một đối tượng hiện có, trong khi **Decorator** nâng cao một đối tượng mà không thay đổi interface của nó. Ngoài ra, **Decorator** hỗ trợ thành phần đệ quy, điều này không thể thực hiện được khi bạn sử dụng **Adapter**. 181 | 182 | **Adapter** cung cấp một interface khác cho đối tượng được bọc, **Proxy** cung cấp cho nó một interface tương tự và **Decorator** cung cấp cho nó một interface nâng cao. 183 | 184 | **Facade** định nghĩa một interface mới cho các đối tượng hiện có, trong khi **Adapter** cố gắng làm cho interface hiện có có thể sử dụng được. **Adapter** thường chỉ bọc một đối tượng, trong khi **Facade** hoạt động với toàn bộ hệ thống con của các đối tượng. 185 | 186 | **Bridge**, **State**, **Strategy** (và ở một mức độ nào đó là **Adapter**) có cấu trúc rất giống nhau. Thật vậy, tất cả các pattern này đều dựa trên nguyên tắc là ủy thác công việc cho các đối tượng khác. Tuy nhiên, chúng giải quyết các vấn đề khác nhau. Một pattern không chỉ là một công thức để cấu trúc code của bạn theo một cách cụ thể. Nó còn có thể giao tiếp với các nhà phát triển khác về vấn đề mà pattern giải quyết. 187 | 188 | # Nguồn 189 | 190 | [**refactoring**](https://refactoring.guru/design-patterns/adapter) -------------------------------------------------------------------------------- /structural-pattern/adapter/assets/analogy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/adapter/assets/analogy.png -------------------------------------------------------------------------------- /structural-pattern/adapter/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/adapter/assets/intent.png -------------------------------------------------------------------------------- /structural-pattern/adapter/assets/problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/adapter/assets/problem.png -------------------------------------------------------------------------------- /structural-pattern/adapter/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/adapter/assets/pseudocode.png -------------------------------------------------------------------------------- /structural-pattern/adapter/assets/solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/adapter/assets/solution.png -------------------------------------------------------------------------------- /structural-pattern/adapter/assets/structure1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/adapter/assets/structure1.png -------------------------------------------------------------------------------- /structural-pattern/adapter/assets/structure2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/adapter/assets/structure2.png -------------------------------------------------------------------------------- /structural-pattern/assets/adapter-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/assets/adapter-mini.png -------------------------------------------------------------------------------- /structural-pattern/assets/bridge-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/assets/bridge-mini.png -------------------------------------------------------------------------------- /structural-pattern/assets/composite-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/assets/composite-mini.png -------------------------------------------------------------------------------- /structural-pattern/assets/decorator-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/assets/decorator-mini.png -------------------------------------------------------------------------------- /structural-pattern/assets/facade-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/assets/facade-mini.png -------------------------------------------------------------------------------- /structural-pattern/assets/flyweight-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/assets/flyweight-mini.png -------------------------------------------------------------------------------- /structural-pattern/assets/proxy-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/assets/proxy-mini.png -------------------------------------------------------------------------------- /structural-pattern/bridge/README.md: -------------------------------------------------------------------------------- 1 | # Bridge 2 | 3 | ## 📜 Mục đích 4 | 5 | **Bridge** là design pattern thuộc nhóm structural giúp bạn tách một lớp khổng lồ hoặc một tập hợp lớp có quan hệ gần gũi với nhau thành hai hệ thống phân cấp lớp riêng biệt là - abstraction(trừu tượng) và implementation(triển khai) - có thể phát triển độc lập với nhau. 6 | 7 | ![intent](./assets/intent.png) 8 | 9 | ## 😟 Vấn đề 10 | 11 | Giả sử bạn có một lớp hình học `Shape` với các lớp con: `Circle` và `Square`. Bạn muốn mở rộng hệ thống phân cấp lớp này để kết hợp với màu sắc, kế hoạch của bạn là tạo lớp con `Red` và `Blue`. Tuy nhiên, vì bạn đã có hai lớp con rồi, nên bạn sẽ cần tạo bốn lớp kết hợp, vd như `BlueCircle` và `RedSquare`. 12 | 13 | ![problem](./assets/problem.png) 14 | 15 | *Số lượng kết hợp tăng lên theo tiến trình hình học.* 16 | 17 | Thêm một hình dạng mới hay màu sắc vào hệ thống phân cấp sẽ làm nó phát triển theo cấp số mũ. Ví dụ, thêm hình tam giác vào bạn sẽ cần thêm hai lớp con cho mỗi màu, sau đó nếu thêm một màu vàng vào sẽ lại cần thêm ba lớp con cho từng hình dạng. Càng thêm nhiều thì mọi thứ càng tệ đi. 18 | 19 | ## 😊 Giải pháp 20 | 21 | Vấn đề này xảy ra khi ta cố gắng mở rộng lớp `Shape` thành hai phần độc lập là: hình dạng và màu sắc. Đây là vấn đề rất phổ biến với kế thừa lớp. 22 | 23 | Pattern Bridge giải quyết vấn đề này bằng cách chuyển từ kế thừa sang cấu thành đối tượng. Có nghĩa là bạn sẽ trích xuất một phần trong lớp gốc thành các hệ thống phân cấp lớp riêng biệt, thế nên lúc này lớp gốc của bạn sẽ tham chiếu đến đối tượng của hệ thống phân cấp mới, thay vì chứa tất cả trạng thái và hành vi của nó trong một lớp. 24 | 25 | ![solution](./assets/solution.png) 26 | 27 | *Bạn có thể ngăn chặn sự bùng nổ của hệ thống phân cấp lớp bằng cách chuyển đổi nó thành một số hệ thống phân cấp có quan hệ* 28 | 29 | Với cách tiếp cận này ta sẽ trích xuất code liên quan đến màu sắc thành một lớp riêng với hai lớp con: `Red` và `Blue`. Lớp `Shape` sẽ lấy trường tham chiếu đến một trong những đối tượng màu sắc. Bây giờ `Shape` có thể uỷ thác mọi công việc liên quan đến màu sắc cho đối tượng `color` được liên kết. Tham chiếu này hoạt động như một cây cầu (bridge) giữa lớp `Shape` và `Color`. Từ giờ khi thêm màu mới sẽ không cần thay đổi hệ thống phân cấp lớp `Shape` và ngược lại. 30 | 31 | ### Abstraction và Implementation 32 | 33 | Quyền sách [GoF](https://www.amazon.com/Design-Patterns-Object-Oriented-Addison-Wesley-Professional-ebook/dp/B000SEIBB8) giới thiệu thuật ngữ *Abstraction* và *Implementation* như là một phần trong định nghĩa Bridge. Theo quan điểm cá nhân thì thuật ngữ này khá hàn lâm và làm cho pattern trông phức tạp hơn. Sau khi xem ví dụ đơn giản hơn với màu sắc và hình dạng, ta có thể diễn giải lại nó như sau: 34 | 35 | **Abstraction** (còn gọi là interface) là một lớp(tầng) điều khiển cấp cao cho một vài thực thể. Lớp này không thực hiện bất cứ công việc gì trong nó. Nó uỷ thác công việc cho lớp **implementation** (còn gọi là platform). 36 | 37 | Lưu ý, ở đây ta không nói về *interface* hay lớp trừu tượng trong ngôn ngữ lập trình của bạn. Đây là hai thứ hoàn toàn khác nhau. 38 | 39 | Ví dụ một ứng dụng thực tế, abstraction có thể biểu diễn bằng giao diện đồ hoạ người dùng (GUI), và implementation có thể là những đoạn code hệ điều hành cơ bản (API), nơi lớp GUI gọi để phản hồi tương tác người dùng. 40 | 41 | Khi bạn mở rộng ứng dụng theo hai hướng độc lập: 42 | - Có nhiều GUI khác nhau (vd như giao diện cho người dùng hay admin). 43 | - Hỗ trợ nhiều API khác nhau(ví dụ như chạy ứng dụng dưới các hệ điều hành Windows, Linux, MacOS) 44 | 45 | Trong trường hợp xấu nhất, ứng dụng này có thể trông giống như một tô mì spaghetti khổng lồ, nơi hàng trăm điều kiện kết nối các loại GUI khác nhau với các API khác nhau trên toàn bộ code. 46 | 47 | ![abstraction](./assets/abstraction.png) 48 | 49 | Bạn có thể làm cho mọi thứ ổn định hơn bằng cách trích xuất code liên quan đến interface-platform thành các lớp riêng biệt. Tuy nhiên, sẽ có rất nhiều lớp trong đấy. Hệ thống phân cấp lớp sẽ phát triển theo cấp số mũ khi thêm một GUI hay hỗ trợ một API khác, vì sẽ yêu cầu tạo thêm nhiều và nhiều hơn các lớp nữa. 50 | 51 | Vấn đề này sẽ được giải quyết bằng Bridge, khi ta chia các lớp thành hai hệ thống phân cấp lớp: 52 | - Abstraction: lớp GUI của ứng dụng. 53 | - Implementation: các API của hệ điều hành. 54 | 55 | ![implement](./assets/implement.png) 56 | 57 | Đối tượng abstraction điều khiển vẻ ngoài của ứng dụng, uỷ thác các công việc thực cho đối tượng implementation được liên kết. Sự khác nhau của các implementation có thể hoán đổi với nhau miễn là nó theo một interface chung, cho phép cùng một GUI chạy trên cả Windows và Linux. 58 | 59 | Kết quả là, bạn có thể thay đổi lớp GUI mà không cần đụng đến lớp API liên quan. Hơn thế nữa khi thêm hỗ trợ cho một hệ điều hành khác chỉ yêu cầu tạo lớp con cho hệ thống phân cấp implementation. 60 | 61 | ## 🏢 Cấu trúc 62 | 63 | ![structure](./assets/structure.png) 64 | 65 | 1. **Abstraction** cung cấp logic điều khiển cấp cao. Nó dựa vào các đối tượng implementation để thực hiện các công việc ở cấp thấp hơn. 66 | 2. **Implementation** khai bao interface chung cho tất cả concrete implementations. Abstraction chỉ giao tiếp với đối tượng implementation thông qua phương thức đã khai báo ở đây. 67 | 3. **Concrete implementation** bao gồm code platform cụ thể. 68 | 4. **Refined Abstractions** cung cấp biến thể cho logic điều khiển. Giống như lớp cha, nó làm việc với implementation thông qua interface implementation chung. 69 | 5. Thông thường, **Client** chỉ quan tâm đến việc làm việc với abstraction. Tuy nhiên, nhiệm vụ của client là liên kết đối tượng abstraction với một trong các đối tượng implementation. 70 | 71 | ## 👨‍💻 Mã giả 72 | 73 | Ví dụ minh hoạ cách Bridge phân chia khối code của ứng dụng để quản lý thiết bị và điều khiển từ xa. Lớp `Device` hoạt động như implementation, trong khi `Remote` hoạt động như abstraction. 74 | 75 | ![pseudocode](./assets/pseudocode.png) 76 | 77 | Lớp cơ sở của `Remote` khai báo trường tham chiếu liên kết với đối tượng `Device`. Tất cả điều khiển làm việc với thiết bị thông qua interface `Device` chung, nó giúp cùng một điều khiển hỗ trợ nhiều loại thiết bị. 78 | 79 | Bạn có thể phát triển lớp `Remote` độc lập với lớp `Device`. Tất cả những gì cần chỉ là tạo một lớp con remote mới. Ví dụ, điều khiển từ xa đơn gian chỉ có hai nút, bạn có thể mở rộng nó với các tính năng như pin dự phòng hay cảm ứng. 80 | 81 | Code client liên kết với remote mong muốn và đối tượng thiết bị cụ thể thông qua hàm khởi tạo của remote. 82 | 83 | ```c 84 | // "Abstraction" định nghĩa interface cho "control" của 85 | // hai hệ thống phân cấp lớp. Nó duy trì một liên kết đến đối 86 | // tượng của hệ thống phân cấp "implementation" và uỷ thác 87 | // các công việc thực cho đối tượng đó. 88 | class RemoteControl is 89 | protected field device: Device 90 | constructor RemoteControl(device: Device) is 91 | this.device = device 92 | method togglePower() is 93 | if (device.isEnabled()) then 94 | device.disable() 95 | else 96 | device.enable() 97 | method volumeDown() is 98 | device.setVolume(device.getVolume() - 10) 99 | method volumeUp() is 100 | device.setVolume(device.getVolume() + 10) 101 | method channelDown() is 102 | device.setChannel(device.getChannel() - 1) 103 | method channelUp() is 104 | device.setChannel(device.getChannel() + 1) 105 | 106 | 107 | // Bạn có thể mở rộng lớp từ hệ thống phân cấp abstraction 108 | // độc lập với lớp device. 109 | class AdvancedRemoteControl extends RemoteControl is 110 | method mute() is 111 | device.setVolume(0) 112 | 113 | 114 | // Interface "implementation" khai báo phương thức chung cho 115 | // tất cả lớp implementation cụ thể. Nó không cần ứng với 116 | // interface abstraction. Sự thật là hai interface có thể 117 | // khác nhau toàn bộ. Tiêu biểu như interface implementation 118 | // chỉ cung cấp toán tử nguyên thuỷ, trong khi abstraction 119 | // khai báo các toán tử cấp cao dựa trên các nguyên thuỷ đó. 120 | interface Device is 121 | method isEnabled() 122 | method enable() 123 | method disable() 124 | method getVolume() 125 | method setVolume(percent) 126 | method getChannel() 127 | method setChannel(channel) 128 | 129 | 130 | // Tất cả thiết bị theo cùng một interface. 131 | class Tv implements Device is 132 | // ... 133 | 134 | class Radio implements Device is 135 | // ... 136 | 137 | 138 | // Đâu đó trong code client. 139 | tv = new Tv() 140 | remote = new RemoteControl(tv) 141 | remote.togglePower() 142 | 143 | radio = new Radio() 144 | remote = new AdvancedRemoteControl(radio) 145 | ``` 146 | 147 | ## 💡 Ứng dụng 148 | 149 | **🐞 Sử dụng Bridge khi bạn muốn phân chia và tổ chức khối lớp của bạn thành các phần biến thể cho một số chức năng** 150 | 151 | ⚡ Ví dụ như khi bạn làm việc với server của các cơ sở dữ liệu khác nhau. Lớp có thể trở nên quá to và khó khăn để tìm hiểu cách nó hoạt động, và khó hơn nữa để thay đổi nó. Các thay đổi với một biến thể của chức năng có thể yêu cầu thay đổi lên toàn bộ lớp, kết quả có thể tạo ra lỗi hoặc các tác dụng phụ nghiệm trọng. 152 | 153 | Bridge giúp bạn tách khối lớp thành các hệ thống phân cấp lớp. Sau đó bạn có thể thay đổi lớp của một hệ thống phân cấp độc lập với các lớp khác. Cách tiếp cận này giúp code dễ bảo trì và tối thiểu nguy cơ code đang có bị ảnh hưởng. 154 | 155 | **🐞 Sử dụng Bridge nếu bạn muốn mở rộng các lớp độc lập với nhau** 156 | 157 | ⚡ Bridge giúp bạn trích xuất một hệ thống phân cấp lớp riêng biệt cho từng phần. Lớp ban đầu ủy thác công việc liên quan cho các đối tượng thuộc các phân cấp đó thay vì tự làm mọi thứ. 158 | 159 | **🐞 Sử dụng Bridge nếu bạn cần chuyển đổi các implementation trong thời gian chạy** 160 | 161 | ⚡ Mặc dù là tùy chọn, nhưng Bridge cho phép bạn thay thế đối tượng implementation bên trong phần abstraction. Nó dễ dàng như việc gán một giá trị mới cho một trường. 162 | 163 | Nhân tiện, mục cuối cùng này là lý do chính khiến nhiều người nhầm lẫn giữa pattern Bridge với pattern Strategy. Hãy nhớ rằng một pattern không chỉ là cách để cấu trúc các lớp của bạn. Nó cũng có thể là một ý tưởng cho một vấn đề đang được giải quyết. 164 | 165 | ## 📋 Triển khai 166 | 167 | 1. Xác định các phần độc lập trong lớp của bạn. Các khái niệm độc lập có thể là: abstraction/platform, domain/infrastructure, front-end/back-end, or interface/implementation. 168 | 169 | 2. Xem các hoạt động mà client cần và định nghĩa chúng ở lớp abstraction cơ sở. 170 | 3. Xác định các hoạt động có sẵn trên tất cả nền tảng. Khai báo những cái mà abstraction cần trong interface implementation chung. 171 | 4. Với tất cả nền tảng trong miền của bạn, tạo các lớp concrete implementation, đảm bảo rằng tất cả chúng theo interface implementation. 172 | 5. Trong lớp abstraction, thêm trường tham chiếu cho kiểu implementation. Abstraction uỷ quyền hầu hất công việc cho đối tượng implementation có liên kết với trường đó. 173 | 6. Nếu bạn có các logic cấp cao khác nhau, tạo refined abstraction cho từng biến thể được mở rộng bằng lớp abstraction cơ sở. 174 | 7. Code client nên truyền đối tượng implementation vào hàm khởi tạo abstraction để liên kết một đối tượng này với đối tượng kia. Sau đó, client có thể quên đi implementation và chỉ làm việc với đối tượng abstraction. 175 | 176 | ## ⚖️ Ưu nhược điểm 177 | 178 | ### Ưu điểm 179 | 180 | ✔️ Bạn có thể tạo các lớp và ứng dụng độc lập với nền tảng. 181 | 182 | ✔️ Code client hoạt động với abstraction cấp cao. Nó không hiển thị với các nền tảng chi tiết. 183 | 184 | ✔️ *Open/Closed Principle* Bạn có thể giới thiệu abstraction và implementation mới một cách độc lập với nhau. 185 | 186 | ✔️ *Single Responsibility Principle* Bạn có thể tập trung vào logic cấp cao trong phần abstraction và chi tiết nền tảng trong quá trình implementation. 187 | 188 | ### Nhược điểm 189 | 190 | ❌ Bạn có thể làm cho code phức tạp hơn bằng cách áp dụng pattern cho một lớp có tính dính kết cao. 191 | 192 | ## 🔁 Quan hệ với các pattern khác 193 | 194 | **Bridge** thường được thiết kế từ trước, cho phép bạn phát triển các phần của ứng dụng một cách độc lập với nhau. Mặt khác, **Adapter** thường được sử dụng với một ứng dụng hiện có để một số lớp không tương thích hoạt động với nhau. 195 | 196 | **Bridge**, **State**, **Strategy** (và ở một mức độ nào đó là **Adapter**) có cấu trúc rất giống nhau. Thật vậy, tất cả các pattern này đều dựa trên nguyên tắc là ủy thác công việc cho các đối tượng khác. Tuy nhiên, chúng giải quyết các vấn đề khác nhau. Một pattern không chỉ là một công thức để cấu trúc code của bạn theo một cách cụ thể. Nó còn có thể giao tiếp với các nhà phát triển khác về vấn đề mà pattern giải quyết. 197 | 198 | Bạn có thể sử dụng **Abstract Factory** cùng với **Bridge**. Việc ghép nối này rất hữu ích khi một số abstract được xác định bởi **Bridge** chỉ có thể hoạt động với các implementation cụ thể. Trong trường hợp này, **Abstract Factory** có thể đóng gói các quan hệ này và ẩn sự phức tạp khỏi code client. 199 | 200 | Bạn có thể kết hợp **Builder** với **Bridge**: lớp director đóng vai trò abstraction, trong khi các builder khác đóng vai trò implementation. 201 | 202 | # Nguồn 203 | 204 | [**refactoring**](https://refactoring.guru/design-patterns/bridge) -------------------------------------------------------------------------------- /structural-pattern/bridge/assets/abstraction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/bridge/assets/abstraction.png -------------------------------------------------------------------------------- /structural-pattern/bridge/assets/implement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/bridge/assets/implement.png -------------------------------------------------------------------------------- /structural-pattern/bridge/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/bridge/assets/intent.png -------------------------------------------------------------------------------- /structural-pattern/bridge/assets/problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/bridge/assets/problem.png -------------------------------------------------------------------------------- /structural-pattern/bridge/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/bridge/assets/pseudocode.png -------------------------------------------------------------------------------- /structural-pattern/bridge/assets/solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/bridge/assets/solution.png -------------------------------------------------------------------------------- /structural-pattern/bridge/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/bridge/assets/structure.png -------------------------------------------------------------------------------- /structural-pattern/composite/README.md: -------------------------------------------------------------------------------- 1 | # Composite 2 | 3 | ## 📜 Mục đích 4 | 5 | **Composite** là một design pattern thuộc nhóm structural cho phép bạn sắp xếp các đối tượng thành cấu trúc cây và sau đó làm việc với các cấu trúc này như thể chúng là các đối tượng riêng lẻ. 6 | 7 | ![intent](./assets/intent.png) 8 | 9 | ## 😟 Vấn đề 10 | 11 | Sử dụng pattern Composite chỉ hợp lý khi mô hình cốt lõi của ứng dụng bạn có thể biểu diễn dạng cây. 12 | 13 | Ví dụ, tưởng tượng bạn có hai loại đối tượng: `Product` và `Box`. `Box` có thể bao gồm nhiều `Product` và một số lượng `Box` nhỏ hơn.Các box nhỏ hơn đó có thể bao gồm `Product` hoặc `Box` nhỏ hơn khác nữa. 14 | 15 | Khi bạn định tạo một hệ thống đặt hàng sử dụng các lớp đấy. Đơn hàng (`Order`) có thể bao gồm một sản phẩm đơn giản không bị bọc, cũng có thể là một cái hộp đựng sản phẩm và các hộp khác. 16 | Vậy làm thế nào để bạn tính tổng giá trị của đơn hàng? 17 | 18 | ![problem](./assets/problem.png) 19 | 20 | Bạn có thể giải quyết trực tiếp: mở tất cả các hộp và cộng tất cả các sản phẩm lại với nhau. Đây là cách giải quyết trong đời sống thực, nhưng trong các chương trình máy tính nó không đơn giản như vậy. Bạn phải biết lớp của `Product` và `Box` mà bạn cần, cấp độ lồng nhau của hộp và các chi tiết khó hiểu khác. Tất cả điều này khiến cách tiếp cận trực tiếp trở nên khó khăn thậm chí là không thể. 21 | 22 | ## 😊 Giải pháp 23 | 24 | Pattern Composite cho bạn ý tưởng là làm việc với `Product` và `Box` qua một interface chung, interface này đã khai báo phương thức tính tổng giá tiền. 25 | 26 | Vậy phương thức này vận hành thế nào ? Với sản phẩm, rất đơn giản để trả về giá tiền của sản phẩm đó. Với hộp, nó sẽ đi qua tất cả thành phần trong hộp, lấy giá tiền và trả về tổng giá cho hộp. Nếu một thành phần là hộp nhỏ hơn, hộp đấy sẽ bất đầu đi qua các thành phần trong đó, cho đến tính xong giá của tất cả thành phần bên trong nó. Một hộp thậm chí có thể thêm một số chi phí bổ sung vào giá cuối cùng, chẳng hạn như chi phí đóng gói. 27 | 28 | ![solution](./assets/solution.png) 29 | 30 | *Composite cho phép bạn chạy đệ quy trên tất cả các thành phần của cây đối tượng* 31 | 32 | Lợi ích tuyệt vời của giải pháp này là bạn không cần quan tâm lớp cụ thể của đối tượng cấu thành cây. Bạn không cần biết đối tượng đó là một sản phẩm đơn giản hay một hộp phức tạp. Bạn có thể xử lý như nhau thông qua interface chung. Khi bạn gọi phương thức, đối tượng sẽ tự động chuyển yêu cầu xuống phía dưới cây. 33 | 34 | ## 🚗 Thế Giới Thực 35 | 36 | ![analogy](./assets/analogy.png) 37 | 38 | Quân đội của hầu hết quốc gia đều có cấu trúc như cây phân cấp. Quân đội sẽ bao gồm các sư đoàn: các sư đoàn bao gồm các trung đoàn, các trung đoàn bao gồm các tiểu đoàn, các tiểu đoàn lại bao gồm các đại đội, trung đội, tiểu đội. Các mệnh lệnh được đưa ra ở trên cùng của hệ thống phân cấp sẽ được truyền xuống từng cấp độ cho đến khi mọi người lính biết những gì cần phải làm. 39 | 40 | ## 🏢 Cấu trúc 41 | 42 | ![structure](./assets/structure.png) 43 | 44 | 1. **Component** là interface khai báo hoạt động chung cho cả phần tử phức tạp và đơn giản của cây. 45 | 2. **Leaf** là phần tử cơ bản của cây, không bao gồm bất kỳ phần tử con nào. Thông thường, các thành phần leaf thực hiện hầu hết các công việc thực, vì chúng không có bất kỳ ai để ủy thác. 46 | 3. **Container** (hoặc composite-phức hợp) là một phần tử có các phần tử con là leaf hoặc các container khác. Một container không biết các lớp cụ thể của các lớp con của nó. Nó chỉ hoạt động với tất cả các phần tử con thông qua interface component. 47 | 4. **Client** làm việc với tất cả các phần tử thông qua interface component. Do đó, client có thể làm việc theo cùng một cách với cả các phần tử đơn giản hoặc phức tạp của cây. 48 | 49 | ## 👨‍💻 Mã giả 50 | 51 | Ở ví dụ này, Composite giúp bạn triển khai ngăn xếp của các dạng hình học ở trình thiết kế đồ hoạ. 52 | 53 | ![pseudocode](./assets/pseudocode.png) 54 | 55 | Lớp `CompoundGraphic` là một container có thể bao gồm bất kỳ số lượng hình dạng con nào, bao gồm cả các container khác. Một hình dạng container có cùng phương thức với hình dạng đơn giản. Tuy nhiên, thay vì thực hiện điều gì đó trên chính nó, thì nó chuyển yêu cầu xuống tất cả con của nó và tính tổng bằng đệ quy. 56 | 57 | Code client làm việc với tất cả hình dạng qua interface chung duy nhất cho tất cả lớp hình dạng. Do đó, client không biết nó đang làm việc với dạng đơn giản hay phức hợp. Nó có thể làm việc với các đối tượng có cấu trúc phức tạp mà không cần ghép với lớp cụ thể tạo nên cấu trúc đấy. 58 | 59 | ```c 60 | // Interface component khai báo các hoạt động chung cho 61 | // cả đối tượng đơn giản và phức tạp của cây. 62 | interface Graphic is 63 | method move(x, y) 64 | method draw() 65 | 66 | // Lớp leaf biểu diễn đối tượng cuối cùng của cây. Đối tượng 67 | // leaf không thể có bất kỳ đối tượng con nào. Thông thường 68 | // leaf của một đối tượng là nơi làm việc thực, trong khi các 69 | // đối tượng container uỷ thác cho con của nó. 70 | class Dot implements Graphic is 71 | field x, y 72 | 73 | constructor Dot(x, y) { ... } 74 | 75 | method move(x, y) is 76 | this.x += x, this.y += y 77 | 78 | method draw() is 79 | // Vẽ chấm X và Y. 80 | 81 | // Tất cả lớp component có thể mở rộng các component khác. 82 | class Circle extends Dot is 83 | field radius 84 | 85 | constructor Circle(x, y, radius) { ... } 86 | 87 | method draw() is 88 | // Vẽ hình tròn tại X và Y với bán kính R. 89 | 90 | // Lớp container biểu diễn component phức tạp có thể có con. 91 | // Đối tượng container thường uỷ thác công việc thực cho 92 | // con của nó và tính kết quả tổng. 93 | class CompoundGraphic implements Graphic is 94 | field children: array of Graphic 95 | 96 | // Đối tượng container có thể thêm hoặc xoá component khác 97 | // (cả đơn giản và phức tạp) vào hoặc khỏi danh sách con 98 | // của nó. 99 | method add(child: Graphic) is 100 | // Thêm con vào mảng danh sách con. 101 | 102 | method remove(child: Graphic) is 103 | // Xoá con khỏi mảng danh sách con. 104 | 105 | method move(x, y) is 106 | foreach (child in children) do 107 | child.move(x, y) 108 | 109 | // Một container thực hiện logic chính của nó theo một cách cụ thể. 110 | // Nó duyệt đệ quy qua tất cả các con của nó, thu thập và tổng hợp 111 | // kết quả của chúng. Vì các con của container chuyển các lệnh gọi 112 | // này cho các con của chính chúng và tiếp tục như thế, nên kết quả 113 | // cuối cùng là toàn bộ cây đối tượng được duyệt qua. 114 | method draw() is 115 | // 1. Với mỗi component con: 116 | // - Vẽ component 117 | // - Cập nhật giới hạn hình chữ nhật. 118 | // 2. Vẽ một hình chữ nhật đứt nét bằng cách 119 | // sử dụng các tọa độ giới hạn. 120 | 121 | 122 | // Code client làm việc với tất cả component thông qua 123 | // interface cơ sở. Với cách này code client có thể 124 | // hỗ trợ leaf đơn giản cũng như các container phức tạp. 125 | class ImageEditor is 126 | field all: CompoundGraphic 127 | 128 | method load() is 129 | all = new CompoundGraphic() 130 | all.add(new Dot(1, 2)) 131 | all.add(new Circle(5, 3, 10)) 132 | // ... 133 | 134 | // Kết hợp các component đã chọn thành một 135 | // container phức tạp. 136 | method groupSelected(components: array of Graphic) is 137 | group = new CompoundGraphic() 138 | foreach (component in components) do 139 | group.add(component) 140 | all.remove(component) 141 | all.add(group) 142 | // Tất cả component đều được vẽ. 143 | all.draw() 144 | ``` 145 | 146 | ## 💡 Ứng dụng 147 | 148 | **🐞 Sử dụng Composite khi bạn muốn triển khai đối tượng có cấu trúc giống cây** 149 | 150 | ⚡ Pattern Composite cung cấp cho bạn hai kiểu phần tử đơn giản có interface chung: leaf đơn giản và container(composite) phức tạp. Container có thể bao gồm leaf và container khác. Nó giúp bạn khởi tạo các đối tượng có cấu trúc đệ quy lồng nhau giống cây. 151 | 152 | **🐞 Sử dụng Composite khi bạn muốn code client xử lý đồng nhất cả phần tử đơn giản và phức tạp** 153 | 154 | ⚡ Tất cả phần tử được xác định với pattern Composite chia sẻ chung interface. Sử dụng interface này, code client không cần quan tâm đến lớp cụ thể mà đối tượng làm việc. 155 | 156 | ## 📋 Triển khai 157 | 158 | 1. Đảm bảo rằng mô hình cốt lõi của ứng dụng có thể biểu diễn cấu trúc cây. Cố gắng chia nhỏ thành các phần tử đơn giản và container. Nhớ là container có thể bao gồm cả phần tử đơn giản và container khác. 159 | 160 | 2. Khai báo interface component với danh sách phương thức hợp lý cho cả component đơn giản và phức tạp. 161 | 162 | 3. Tạo lớp leaf để biểu diễn phần tử đơn giản. Chương trình có thể có nhiều lớp leaf khác nhau. 163 | 164 | 4. Tại lớp container biểu diễn phần tử phức tạp. Trong lớp này, cung cấp mảng để lưu trữ tham chiếu đến phần tử. Mảng có thể lưu cả leaf và container khác, chắc chắn rằng nó được khai báo với kiểu interface component. 165 | Trong khi triển khai phương thức của interface component, hãy nhớ container thường uỷ thác phần lớn công việc cho phần tử con. 166 | 167 | 5. Cuối cùng định nghĩa phương thức thêm và xoá phần tử con ở container. Hãy ghi nhớ rằng các hoạt động có thể khai báo ở interface component. Điều này sẽ vi phạm *Nguyên tắc Phân tách Interface* vì phương thức sẽ trống trong lớp leaf. Tuy nhiên, client sẽ xử lý tất cả phần tử như nhau, khi cấu thành cây. 168 | 169 | ## ⚖️ Ưu nhược điểm 170 | 171 | ### Ưu điểm 172 | 173 | ✔️ Bạn có thể làm việc với cấu trúc cây phức tạp thuật tiện hơn: sử dụng kiểu đa hình và đệ quy. 174 | 175 | ✔️ *Open/Closed Principle* Bạn có thể thêm một kiểu phần tử mới vào ứng dụng mà không ảnh hưởng đến code hiện có, chỉ hiệu quả với đối tượng cây. 176 | 177 | ### Nhược điểm 178 | 179 | ❌ Có thể khó cung cấp một interface chung cho các lớp có chức năng khác nhau quá nhiều. Trong một số trường hợp nhất định, bạn cần phải tổng quát hóa quá mức interface component, khiến nó khó hiểu hơn. 180 | 181 | ## 🔁 Quan hệ với các pattern khác 182 | 183 | Bạn có thể sử dụng **Builder** khi tạo các cây **Composite** phức tạp vì bạn có thể lập trình các bước xây dựng của nó để hoạt động bằng đệ quy. 184 | 185 | **Chain of Responsibility** thường được sử dụng cùng với **Composite**. Trong trường hợp này, khi một thành phần leaf nhận được một yêu cầu, nó có thể chuyển nó qua chuỗi của tất cả các thành phần mẹ xuống gốc của cây đối tượng. 186 | 187 | Bạn có thể sử dụng **Iterator** để duyệt qua các cây **Composite**. 188 | 189 | Bạn có thể sử dụng **Visitor** để thực hiện một hoạt động trên toàn bộ cây **Composite**. 190 | 191 | Bạn có thể triển khai các nút leaf chia sẻ của cây **Composite** dưới dạng **Flyweights** để tiết kiệm dung lượng RAM. 192 | 193 | **Composite** và **Decorator** có các sơ đồ cấu trúc tương tự vì cả hai đều dựa vào thành phần đệ quy để tổ chức một số lượng các đối tượng kết thúc mở. 194 | 195 | - **Decorator** giống như **Composite** nhưng chỉ có một thành phần con. Sự khác biệt đáng kể khác là **Decorator** thêm các trách nhiệm bổ sung cho đối tượng được bao bọc, trong khi **Composite** chỉ "tính tổng" các kết quả con của nó. 196 | 197 | - Tuy nhiên, các pattern cũng có thể hợp tác: bạn có thể sử dụng **Decorator** để mở rộng hành vi của một đối tượng cụ thể trong cây **Composite**. 198 | 199 | Các thiết kế sử dụng nhiều **Composite** và **Decorator** thường có thể được hưởng lợi từ việc sử dụng Prototype. Áp dụng pattern cho phép bạn sao chép các cấu trúc phức tạp thay vì xây dựng lại chúng từ đầu. 200 | 201 | # Nguồn 202 | 203 | [**refactoring**](https://refactoring.guru/design-patterns/composite) -------------------------------------------------------------------------------- /structural-pattern/composite/assets/analogy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/composite/assets/analogy.png -------------------------------------------------------------------------------- /structural-pattern/composite/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/composite/assets/intent.png -------------------------------------------------------------------------------- /structural-pattern/composite/assets/problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/composite/assets/problem.png -------------------------------------------------------------------------------- /structural-pattern/composite/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/composite/assets/pseudocode.png -------------------------------------------------------------------------------- /structural-pattern/composite/assets/solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/composite/assets/solution.png -------------------------------------------------------------------------------- /structural-pattern/composite/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/composite/assets/structure.png -------------------------------------------------------------------------------- /structural-pattern/decorator/assets/analogy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/decorator/assets/analogy.png -------------------------------------------------------------------------------- /structural-pattern/decorator/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/decorator/assets/intent.png -------------------------------------------------------------------------------- /structural-pattern/decorator/assets/problem1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/decorator/assets/problem1.png -------------------------------------------------------------------------------- /structural-pattern/decorator/assets/problem2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/decorator/assets/problem2.png -------------------------------------------------------------------------------- /structural-pattern/decorator/assets/problem3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/decorator/assets/problem3.png -------------------------------------------------------------------------------- /structural-pattern/decorator/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/decorator/assets/pseudocode.png -------------------------------------------------------------------------------- /structural-pattern/decorator/assets/solution1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/decorator/assets/solution1.png -------------------------------------------------------------------------------- /structural-pattern/decorator/assets/solution2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/decorator/assets/solution2.png -------------------------------------------------------------------------------- /structural-pattern/decorator/assets/solution3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/decorator/assets/solution3.png -------------------------------------------------------------------------------- /structural-pattern/decorator/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/decorator/assets/structure.png -------------------------------------------------------------------------------- /structural-pattern/facade/README.md: -------------------------------------------------------------------------------- 1 | # Facade 2 | 3 | ## 📜 Mục đích 4 | 5 | **Facade** là design pattern thuộc nhóm structural cung cấp interface đơn giản cho thư viện, framework hoặc bất kỳ tập hợp lớp phức tạp nào khác. 6 | 7 | ![intent](./assets/intent.png) 8 | 9 | ## 😟 Vấn đề 10 | 11 | Hãy tưởng tượng rằng bạn phải sử dụng code của mình để làm việc với một loạt các đối tượng thuộc về một thư viện hoặc framework phức tạp. Thông thường, bạn cần khởi tạo tất cả các đối tượng đó, theo dõi các phụ thuộc, thực thi các phương thức theo đúng thứ tự, ... 12 | 13 | Do đó, logic nghiệp vụ trong các lớp của bạn sẽ trở nên lệ thuộc chặt chẽ với các chi tiết triển khai của các lớp bên thứ ba, khiến cho việc hiểu rõ và bảo trì trở nên khó khăn. 14 | 15 | ## 😊 Giải pháp 16 | 17 | Facade là một lớp cung cấp interface đơn giản cho cho một hệ thống con phức tạp chứa nhiều bộ phận chuyển động. Một facade có thể cung cấp chức năng hạn chế so với làm việc trực tiếp qua hệ thống con. Tuy nhiên, nó sẽ bao gồm những tính năng mà client thực sự quan tâm. 18 | 19 | Có một facade rất tiện lợi khi bạn cần tích hợp ứng dụng của mình với một thư viện phức tạp có hàng tá tính năng, nhưng bạn chỉ cần một vài chức năng trong đó 20 | 21 | Ví dụ: một ứng dụng tải video ngắn hài hước về mèo lên mạng xã hội có thể sử dụng thư viện chuyển đổi video chuyên nghiệp. Tuy nhiên, tất cả những gì bạn thực sự cần là một lớp với một phương thức duy nhất `encode(filename, format)`. Sau khi tạo một lớp như vậy và kết nối nó với thư viện chuyển đổi video, bạn sẽ có facade đầu tiên của mình.. 22 | 23 | ## 🚗 Thế Giới Thực 24 | 25 | ![analogy](./assets/analogy.png) 26 | 27 | Khi bạn gọi đến một cửa hàng để đặt hàng qua điện thoại, nhân viên tổng đài sẽ là facade của bạn đối với tất cả các dịch vụ và bộ phận của cửa hàng. Nhà điều hành cung cấp cho bạn một interface giọng nói đơn giản với hệ thống đặt hàng, cổng thanh toán và các dịch vụ giao hàng khác nhau. 28 | 29 | ## 🏢 Cấu trúc 30 | 31 | ![structure](./assets/structure.png) 32 | 33 | 1. **Facade** cung cấp khả năng truy cập thuận tiện vào một phần cụ thể của chức năng trong hệ thống con. Nó biết nơi định hướng yêu cầu của client và cách vận hành tất cả các bộ phận hoạt động. 34 | 2. **Additional Facade**, lớp bổ sung có thể được tạo để ngăn chặn việc làm hỏng một facede đơn lẻ với các tính năng không liên quan khiến nó trở thành một cấu trúc phức tạp. Additional Facade có thể được sử dụng bởi client và facade khác. 35 | 3. **Complex Subsystem** bao gồm hàng chục đối tượng khác nhau. Để tất cả bọn chúng làm điều gì đó có ý nghĩa, bạn phải đi sâu vào chi tiết triển khai của hệ thống con, chẳng hạn như khởi tạo các đối tượng theo đúng thứ tự và cung cấp cho chúng dữ liệu ở định dạng thích hợp. 36 | Các lớp hệ thống con không biết về sự tồn tại của facade. Chúng hoạt động trong hệ thống và làm việc trực tiếp với nhau. 37 | 4. **Client** sử dụng facade thay vì gọi trực tiếp đến hệ thống con. 38 | 39 | ## 👨‍💻 Mã giả 40 | 41 | Trong ví dụ này, Facade đơn giản hóa việc tương tác với một framework chuyển đổi video phức tạp. 42 | 43 | ![pseudocode](./assets/pseudocode.png) 44 | 45 | Thay vì sử dụng code của bạn làm việc trực tiếp với hàng chục lớp framework, bạn tạo một lớp facade đóng gói chức năng đó và ẩn nó khỏi phần code còn lại. Cấu trúc này cũng giúp bạn giảm thiểu việc nâng cấp lên các phiên bản trong tương lai của framework hoặc thay thế nó bằng một framework khác. Điều duy nhất bạn cần thay đổi trong ứng dụng của mình sẽ là triển khai các phương pháp của facade. 46 | 47 | ```c 48 | // Đây là một số lớp phức tạp của framework chuyển đổi video 49 | // bên thứ 3. Ta không kiểm soát code này nên không đơn 50 | // giản nó được. 51 | 52 | class VideoFile 53 | // ... 54 | 55 | class OggCompressionCodec 56 | // ... 57 | 58 | class MPEG4CompressionCodec 59 | // ... 60 | 61 | class CodecFactory 62 | // ... 63 | 64 | class BitrateReader 65 | // ... 66 | 67 | class AudioMixer 68 | // ... 69 | 70 | 71 | // Ta tạo lớp facade để ẩn framework phức tạp sau interface 72 | // đơn giản. Nó là đánh đổi giữa đầy đủ chức năng và tính 73 | // đơn giản. 74 | class VideoConverter is 75 | method convert(filename, format):File is 76 | file = new VideoFile(filename) 77 | sourceCodec = new CodecFactory.extract(file) 78 | if (format == "mp4") 79 | destinationCodec = new MPEG4CompressionCodec() 80 | else 81 | destinationCodec = new OggCompressionCodec() 82 | buffer = BitrateReader.read(filename, sourceCodec) 83 | result = BitrateReader.convert(buffer, destinationCodec) 84 | result = (new AudioMixer()).fix(result) 85 | return new File(result) 86 | 87 | // Lớp ứng dụng không phụ thuộc vào hàng tỉ lớp được cung cấp 88 | // bởi framework phức tạp. Nếu bạn muốn đổi framework, bạn chỉ 89 | // cần viết lại lớp facade. 90 | class Application is 91 | method main() is 92 | convertor = new VideoConverter() 93 | mp4 = convertor.convert("funny-cats-video.ogg", "mp4") 94 | mp4.save() 95 | ``` 96 | 97 | ## 💡 Ứng dụng 98 | 99 | **🐞 Sử dụng Facade khi bạn cần có một interface hạn chế nhưng đơn giản cho một hệ thống con phức tạp** 100 | 101 | ⚡ Thông thường, các hệ thống con trở nên phức tạp hơn theo thời gian. Ngay cả việc áp dụng các design pattern thường dẫn đến việc tạo ra nhiều lớp hơn. Một hệ thống con có thể trở nên linh hoạt hơn và dễ dàng sử dụng lại trong các ngữ cảnh khác nhau, nhưng số lượng cấu hình và code có sẵn mà nó yêu cầu từ client ngày càng lớn hơn. Facade cố gắng khắc phục sự cố này bằng cách cung cấp một lối tắt đến các tính năng được sử dụng nhiều nhất của hệ thống con phù hợp với hầu hết các yêu cầu của client. 102 | 103 | **🐞 Sử dụng Facade khi bạn muốn cấu trúc một hệ thống con thành các lớp** 104 | 105 | ⚡ Tạo các facade để xác định các điểm vào cho mỗi cấp của một hệ thống con. Bạn có thể giảm sự ghép nối giữa nhiều hệ thống con bằng cách yêu cầu chúng chỉ giao tiếp thông qua các facade. 106 | 107 | Ví dụ: hãy quay lại framework chuyển đổi video. Nó có thể được chia thành hai lớp: liên quan đến video và âm thanh. Đối với mỗi lớp, bạn có thể tạo một facade và sau đó làm cho các lớp của mỗi lớp giao tiếp với nhau thông qua các facade. Cách tiếp cận này trông rất giống với **Mediator**. 108 | 109 | ## 📋 Triển khai 110 | 111 | 1. Kiểm tra xem liệu có thể cung cấp interface đơn giản hơn những gì hệ thống con hiện tại đã cung cấp hay không. Bạn đang đi đúng hướng nếu interface này làm cho code client độc lập với nhiều lớp của hệ thống con. 112 | 2. Khai báo và triển khai interface này trong một lớp facade mới. Facade phải chuyển hướng các cuộc gọi từ code client đến các đối tượng thích hợp của hệ thống con. Facade phải chịu trách nhiệm khởi tạo hệ thống con và quản lý vòng đời tiếp theo của nó trừ khi code client đã thực hiện điều này. 113 | 3. Để có được toàn bộ lợi ích từ thiết kế, hãy làm cho tất cả code client chỉ giao tiếp với hệ thống con thông qua facade. Bây giờ code client được bảo vệ khỏi bất kỳ thay đổi nào trong code hệ thống con. Ví dụ: khi một hệ thống con được nâng cấp lên phiên bản mới, bạn sẽ chỉ cần sửa đổi code trong facade. 114 | 4. Nếu facade trở nên quá lớn, hãy xem xét trích xuất một phần hành vi của nó sang một lớp facade mới. 115 | 116 | ## ⚖️ Ưu nhược điểm 117 | 118 | ### Ưu điểm 119 | 120 | ✔️ Bạn có thể tách code của mình khỏi sự phức tạp của một hệ thống con. 121 | 122 | ### Nhược điểm 123 | 124 | ❌ Một facade có thể trở thành một đối tượng thần thánh cùng với tất cả các lớp của một ứng dụng. 125 | 126 | ## 🔁 Quan hệ với các pattern khác 127 | 128 | **Facade** định nghĩa một interface mới cho các đối tượng hiện có, trong khi **Adapter** cố gắng làm cho interface hiện có có thể sử dụng được. **Adapter** thường chỉ bọc một đối tượng, trong khi **Facade** hoạt động với toàn bộ hệ thống con của các đối tượng. 129 | 130 | **Abstract Factory** có thể dùng như một giải pháp thay thế cho **Facade** khi bạn chỉ muốn ẩn cách các đối tượng hệ thống con được tạo ra khỏi code client. 131 | 132 | **Flyweight** cho thấy cách tạo nhiều đối tượng nhỏ, trong khi **Facade** cho thấy cách tạo một đối tượng duy nhất đại diện cho toàn bộ hệ thống con. 133 | 134 | **Facade** và **Mediator** có những công việc tương tự nhau: cố gắng tổ chức sự hợp tác giữa nhiều lớp được kết hợp chặt chẽ với nhau. 135 | 136 | - **Facade** xác định một interface đơn giản cho một hệ thống con của các đối tượng, nhưng nó không giới thiệu bất kỳ chức năng mới nào. Bản thân hệ thống con không biết về facade. Các đối tượng trong hệ thống con có thể giao tiếp trực tiếp. 137 | - **Mediator** tập trung giao tiếp giữa các thành phần của hệ thống. Các thành phần chỉ biết về đối tượng mediator và không giao tiếp trực tiếp. 138 | 139 | Một lớp **Facade** thường có thể được chuyển đổi thành **Singleton** vì một đối tượng facade duy nhất là đủ trong hầu hết các trường hợp. 140 | 141 | **Facade** tương tự như **Proxy** ở chỗ cả hai đều đệm một thực thể phức tạp và tự khởi tạo nó. Không giống như **Facade**, **Proxy** có interface giống với đối tượng dịch vụ của nó, điều này làm cho chúng có thể hoán đổi cho nhau. 142 | 143 | # Nguồn 144 | 145 | [**refactoring**](https://refactoring.guru/design-patterns/facade) 146 | -------------------------------------------------------------------------------- /structural-pattern/facade/assets/analogy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/facade/assets/analogy.png -------------------------------------------------------------------------------- /structural-pattern/facade/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/facade/assets/intent.png -------------------------------------------------------------------------------- /structural-pattern/facade/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/facade/assets/pseudocode.png -------------------------------------------------------------------------------- /structural-pattern/facade/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/facade/assets/structure.png -------------------------------------------------------------------------------- /structural-pattern/flyweight/README.md: -------------------------------------------------------------------------------- 1 | # Flyweight 2 | 3 | ## 📜 Mục đích 4 | 5 | **Flyweight** là design pattern thuộc nhóm structural giúp bạn chỉnh các đối tượng phù hợp với dung lượng RAM bằng cách chia sẻ trạng thái chung giữa các đối tượng thay vì giữ tất cả dữ liệu ở một đối tượng. 6 | 7 | ![intent](./assets/intent.png) 8 | 9 | ## 😟 Vấn đề 10 | 11 | Để tìm điều gì vui vẻ sau thời gian dài làm việc, bạn quyết định tạo một video game đơn giản: người chơi di chuyển vòng quanh bản đồ và bắn hạ các đối thủ. Bạn chọn triển khai hệ thống hạt (hệ thống phổ biến nhất dùng làm game effect trong Unity 3d) và tạo các tính năng đặc biệt cho game. Sẽ có rất nhiều mảnh đạn, tên lửa và mảnh bom từ các vụ nổ văng khắp bản đồ và mang lại trải nghiệm ly kỳ cho người chơi. 12 | 13 | Sau hoàn thành, bạn commit lần cuối, tạo game và gửi nó cho người bạn để kiểm tra thử. Mặc dù game chạy hoàn hảo trên máy bạn, nhưng người bạn của bạn lại không thể chơi trong thời gian dài. Vì nó bị crash sau vài phút trên máy anh ta. Sau nhiều giờ tìm hiểu debug log, bạn nhận ra game bị crash vì nó không đủ RAM. Bởi vì máy tính của anh ta không mạnh như máy bạn, nên các sự cố mới xảy ra. 14 | 15 | Thực ra vấn đề này có liên quan đến hệ thống hạt. Với mỗi hạt, ví dụ như đạn, tên lửa hay mảnh bom sẽ được biểu diễn bởi một đối tượng riêng bao gồm nhiều dữ liệu. Cùng thời điểm đó, nếu người chơi tàn sát lẫn nhau trên màn hình, các hạt mới tạo ra không vừa với dung lượng RAM còn lại, nên chương trình bị crash. 16 | 17 | ![problem](./assets/problem.png) 18 | 19 | ## 😊 Giải pháp 20 | 21 | Khi kiểm tra kỹ hơn các lớp `Particle`, bạn sẽ nhận ra các trường `color` và `sprite`(ảnh biểu diễn hạt) tiêu thụ nhiều bộ nhớ hơn các trường khác. Điều tệ ở đây là hai trường này lưu trữ phần lớn dữ liệu giống hệt nhau ở tất cả hạt. Ví dụ, tất cả đạn đều có cùng màu sắc và sprite. 22 | 23 | ![solution1](./assets/solution1.png) 24 | 25 | Mặt khác các trạng thái hạt như `coords`(toạ độ), `vector`(hướng chuyển động) và `speed`(tốc độ) là không trùng cho mỗi hạt. Đồng thời, giá trị các trường này sẽ thay đổi theo thời gian. Dữ liệu biểu diễn nó luôn thay đổi khi hạt tồn tại, trong khi màu sắc và sprite không đổi cho mỗi hạt. 26 | 27 | Dữ liệu không đổi này của một đối tượng thường được gọi là *intrinsic state(trạng thái nội tại)*. Nó ở bên trong đối tượng; các đối tượng khác chỉ có thể đọc nó, không thể thay đổi nó. Phần còn lại của trạng thái của đối tượng, thường bị thay đổi “từ bên ngoài” bởi các đối tượng khác, được gọi là *extrinsic state(trạng thái bên ngoài)*. 28 | 29 | Ý tưởng của Flyweight là thay vì lưu trữ các trạng thái bên ngoài trong đối tượng. Bạn truyền trạng thái đó vào một phương thức cụ thể. Chỉ giữ lại trạng thái nội tại bên trong đối tượng, sử dụng nó cho các bối cảnh khác nhau. Do đó, bạn sẽ cần ít các đối tượng này hơn vì chúng chỉ khác nhau ở trạng thái nội tại, thứ có ít biến thể hơn nhiều so với trạng thái bên ngoài. 30 | 31 | ![solution2](./assets/solution2.png) 32 | 33 | Quay lại với trò chơi ở trên. Giả sử rằng ta đã trích xuất trạng thái bên ngoài khỏi lớp `Particle`, giờ chỉ còn ba đối tượng khác nhau là đủ để đại diện cho tất cả các hạt trong trò chơi: một viên đạn, một tên lửa và một mảnh bom. Như bạn có thể đã đoán, một đối tượng chỉ lưu trữ trạng thái bên trong được gọi là *flyweight*. 34 | 35 | ### Lưu trữ trạng thái bên ngoài 36 | 37 | Vậy trạng thái bên ngoài được chuyển đi đâu? Một vài lớp sẽ lưu trữ nó? Đúng vậy, đa số trường hợp nó được chuyển đến đối tượng container, thứ tổng hợp các đối tượng trước khi áp dụng pattern. 38 | 39 | Ở trường hợp này, đối tượng chính `Game` là nơi lưu trữ tất cả các hạt ở trường `particles`. Để chuyển trạng thái bên ngoài vào lớp này, bạn cần tạo nhiều trường kiểu mảng cho lưu trữ toạ độ, vector và tốc độ cho từng hạt. Đồng thời, bạn sẽ cần một mảng khác cho lưu trữ tham chiếu đến flyweight riêng để biểu diễn hạt. Các mảng này phải đồng bộ với nhau nên bạn phải truy cập tất cả dữ liệu của hạt với cùng một chỉ số. 40 | 41 | ![solution3](./assets/solution3.png) 42 | 43 | Giải pháp gọn gàng hơn là tạo lớp ngữ cảnh riêng để lưu trữ trạng thái bên ngoài cùng với tham chiếu đến đối tượng flyweight. Cách tiếp cận này chỉ yêu cầu một mảng duy nhất trong lớp container. 44 | 45 | Đợi đã! Chúng ta không cần tất cả đối tượng ngữ cảnh như ban đầu sao? Về mặt kỹ thuật, thì đúng là như vậy. Nhưng hãy nhìn lại, các đối tượng bây giờ đã nhỏ hơn trước rất nhiều. Các trường tiêu thụ nhiều bộ nhớ nhất đã được chuyển vào các đối tượng flyweight. Bây giờ một ngàn đối tượng ngữ cảnh có thể sử dụng lại một đối tượng flyweight thay vì phải tự lưu trữ hàng ngàn bản sao dữ liệu. 46 | 47 | ### Flyweight và tính bất biến 48 | 49 | Vì cùng một đối tượng flyweight có thể dùng trong các bối cảnh khác nhau, bạn phải đảm bảo rằng nó không thể bị sửa đối. Một flyweight nên khởi tạo trạng thái một lần duy nhất, thông qua tham số từ hàm khởi tạo.Nó không được để lộ bấy kỳ setter hay trường công khai nào với đối tượng khác. 50 | 51 | ### Flyweight factory 52 | 53 | Để truy cập các flyweight khác nhau tiện lợi hơn, bạn có thể tạo phương thức factory để quản lý pool của đối tượng flyweight đã tồn tại. Phương thức chấp nhận trạng thái nội tại của flyweight mong muốn từ client, tìm kiếm flyweight đã tồn tại ứng với trạng thái này, và trả về nếu tìm gặp. Nếu không nó sẽ tạo flyweight mới và thêm vào pool. 54 | 55 | Ở đây có khá nhiều lựa chọn để đặt phương thức này. Đa số là ở flyweight container. Một vài trường hợp, bạn có thể tạo lớp factory mới. Hoặc bạn có thể tạo phương thức factory tĩnh và đặt vào trong nó lớp flyweight. 56 | 57 | ## 🏢 Cấu trúc 58 | 59 | ![structure](./assets/structure.png) 60 | 61 | Flyweight chỉ là một sự tối ưu hoá. Trước khi áp dụng nó, hãy đảm bảo chương trình của bạn có vấn đề tiêu thụ RAM liên quan đến số lượng khổng lồ các đối tượng giống nhau trong bộ nhớ cùng lúc. Đồng thời vấn đề này không thể giải quyết bằng bất kỳ cách nào khác. 62 | 63 | 1. **Flyweight** lớp bao gồm phần trạng thái gốc của đối tượng có thể được chia sẻ giữa nhiều đối tượng. Cùng đối tượng flyweight có thể được sử dụng trong nhiều ngữ cảnh khác nhau. Trạng thái lưu trữ trong flyweight được gọi là *intrinsic*(nội tại). Trạng thái được chuyền đến phương thức của flyweight được gọi là *extrinsic*(bên ngoài). 64 | 2. **Context** lớp bao gồm trạng thái bên ngoài, duy nhất trên tất cả đối tượng gốc. Khi context được ghép nối với một đối tượng flyweight, nó biểu diễn tất cả trạng thái của đối tượng gốc. 65 | 3. Thông thường, hành vi của đối tượng gốc tồn tại trong lớp flyweight. Trong trường hợp này, bất cứ ai gọi phương thức của flyweight cũng phải truyền các bit phù hợp của trạng thái bên ngoài vào tham số của phương thức. Mặt khác, hành vi có thể được chuyền đến lớp context, nới sử dụng liên kết flyweight đơn thuần như một đối tượng dữ liệu. 66 | 4. **Client** tính toán hoặc lưu trữ trạng thái bên ngoài của flyweight. Từ góc nhìn client, một flyweight là một đối tượng mẫu thứ có thể được cấu hình khi đang chạy bằng cách truyền một vài dữ liệu ngữ cảnh vào tham số của phương thức của nó. 67 | 68 | ## 👨‍💻 Mã giả 69 | 70 | Trong ví dụ này, Flyweight giúp giảm mức sử dụng bộ nhớ khi hiển thị hàng triệu đối tượng dạng cây trên canvas. 71 | 72 | ![structure](./assets/structure.png) 73 | 74 | Pattern mở rộng trạng thái nội tại bị lặp của lớp `Tree` và chuyển nói vào lớp flyweight `TreeType`. 75 | 76 | Bây giờ thay vì lưu trữ cùng một dữ liệu trong nhiều đối tượng, nó chỉ lưu trong vài đối tượng flyweight và liên kết đến đối tượng `Tree` phù hợp,thứ hành động như một context. Code client tạo đối tượng cây mới sử dụng flyweight factory, thứ gói gọn sự phức tạp của việc tìm kiếm đối tượng phù hợp và sử dụng lại nó nếu cần. 77 | 78 | ```c 79 | // Lớp flyweight bao gồm một phần trạng thái của cây. Các 80 | // trường lưu trữ giá trị đơn nhất cho từng cây riêng biêt. 81 | // Ví dụ, bạn sẽ không tìm thấy ở đây toạ độ cây. Nhưng ở đây 82 | // sẽ có cấu hình và màu sắc được dùng cung giữa nhiều cây. 83 | // Vì dữ liệu này thường rất LỚN, bạn không muốn lãng phí phần 84 | // lớn bộ nhớ để lưu trữ nó cho từng đối tượng cây. Vì thế mà, 85 | // bạn trích xuất kết cấu, màu sắc và các dữ liệu lặp lại khác 86 | // vào một đối tượng riêng biệt mà các cây có thể tham chiếu 87 | // đến. 88 | class TreeType is 89 | field name 90 | field color 91 | field texture 92 | constructor TreeType(name, color, texture) { ... } 93 | method draw(canvas, x, y) is 94 | // 1. Tạo bitmap cho kiểu, màu sắc và kết cấu. 95 | // 2. Vẽ bitmap trên canvas theo toạ độ X và Y. 96 | 97 | // Flyweight factory quyết định xem sử dụng lại flyweight 98 | // hiện có hay tạo đối tượng mới. 99 | class TreeFactory is 100 | static field treeTypes: collection of tree types 101 | static method getTreeType(name, color, texture) is 102 | type = treeTypes.find(name, color, texture) 103 | if (type == null) 104 | type = new TreeType(name, color, texture) 105 | treeTypes.add(type) 106 | return type 107 | 108 | 109 | // Đối tượng ngữ cảnh bao gồm phần trạng thái bên ngoài của cây. 110 | // Ứng dụng có thể tạo hàng tỉ cái vì nó rất nhỏ: chỉ bao gồm hai 111 | // số nguyên cho toạ độ và một trường tham chiếu. 112 | class Tree is 113 | field x,y 114 | field type: TreeType 115 | constructor Tree(x, y, type) { ... } 116 | method draw(canvas) is 117 | type.draw(canvas, this.x, this.y) 118 | 119 | 120 | // Lớp Tree và Forest là client của flyweight. 121 | // Bạn có thể gộp chúng nếu không có ý đinh phát 122 | // triển lớp Tree xa hơn. 123 | class Forest is 124 | field trees: collection of Trees 125 | 126 | method plantTree(x, y, name, color, texture) is 127 | type = TreeFactory.getTreeType(name, color, texture) 128 | tree = new Tree(x, y, type) 129 | trees.add(tree) 130 | 131 | method draw(canvas) is 132 | foreach (tree in trees) do 133 | tree.draw(canvas) 134 | ``` 135 | 136 | ## 💡 Ứng dụng 137 | 138 | **🐞 Sử dụng Flyweight khi chương trình bạn tạo ra một số lượng lớn đối tượng không phù hợp với lượng RAM khả dụng** 139 | 140 | ⚡ Lợi ích của áp dụng pattern này nằm ở việc nó được dùng như thế nào và ở đâu. Nó sẽ hữu ích nhất khi: 141 | - ứng dụng cần tạo một số lượng rất lớn đối tượng giống nhau. 142 | - điều này làm cạn RAM khả dụng của thiết bị 143 | - đối tượng bao gồm các trạng thái trùng lặp có thể trích xuất và chia sẻ giữa nhiều đối tượng. 144 | 145 | ## 📋 Triển khai 146 | 147 | 1. Chia các trường của lớp sẽ trở thành flyweight, thành hai phần: 148 | - trạng thái nội tại: các trường dữ liệu không thay đổi trùng lặp với nhiều đối tượng 149 | - trạng thái bên ngoài: các trường dữ liệu theo ngữ cảnh duy nhất cho mỗi đối tượng. 150 | 2. Chuyển các trường biểu diễn trạng thái nội tại vào một lớp. Đảm bảo rằng nó luôn bất biến. Ta chỉ nên tạo trạng thái ban đầu cho nó trong hàm khởi tạo. 151 | 3. Đi đến phương thức sử dụng trạng thái bên ngoài. Với từng trường sử dụng phương thức thêm tham số mới và dùng nó thay cho trường. 152 | 4. Tuỳ chon, tạo lớp factory để quản lý pool của flyweight. Nó kiểm tra flyweight đã tồn tại trước khi tạo mới. Sau khi factory hoạt động, client nên gửi yêu cầu flyweight thông qua nó. Client có thể mô tả flyweight mong muốn bằng cách truyền trạng thái nội tại của nó đến factory. 153 | 5. Client nên lưu trữ hoặc tính toán giá trị của trạng thái bên ngoài (ngữ cảnh) có thể gọi phương thức của đối tượng flyweight. Vì tiện lợi, trạng thái bên ngoài cùng với trường tham chiếu đến flyweight có thể được chuyển đến lớp ngữ cảnh riêng biệt. 154 | 155 | ## ⚖️ Ưu nhược điểm 156 | 157 | ### Ưu điểm 158 | 159 | ✔️ Bạn có thể tiết kiệm rất nhiều RAM, nếu chương trình của bạn có rất nhiều đối tượng giống nhau. 160 | 161 | ### Nhược điểm 162 | 163 | ❌ Bạn có thể trao đổi RAM quá mức với các chu kỳ CPU khi một số dữ liệu ngữ cảnh cần được tính toán lại mỗi khi ai đó gọi phương thức flyweight. 164 | 165 | ❌ Code trở nên phức tạp hơn nhiều. Các thành viên mới trong nhóm sẽ luôn thắc mắc tại sao trạng thái của một thực thể lại được tách ra theo cách như vậy 166 | 167 | ## 🔁 Quan hệ với các pattern khác 168 | 169 | Bạn có thể triển khai các nút leaf chia sẻ của cây **Composite** dưới dạng **Flyweights** để tiết kiệm dung lượng RAM. 170 | 171 | **Flyweight** cho thấy cách tạo nhiều đối tượng nhỏ, trong khi **Facade** cho thấy cách tạo một đối tượng duy nhất đại diện cho toàn bộ hệ thống con. 172 | 173 | **Flyweight** sẽ giống với **Singleton** nếu bạn bằng cách nào đó giảm được tất cả các trạng thái được chia sẻ của các đối tượng xuống chỉ còn một đối tượng flyweight. Nhưng có hai điểm khác biệt cơ bản giữa các pattern này: 174 | 175 | - Chỉ nên có một thực thể **Singleton**, trong khi lớp **Flyweight** có thể có nhiều thực thể với các trạng thái nội tại khác nhau. 176 | - Đối tượng **Singleton** có thể thay đổi được. Đối tượng **Flyweight** là bất biến. 177 | 178 | # Nguồn 179 | 180 | [**refactoring**](https://refactoring.guru/design-patterns/flyweight) 181 | -------------------------------------------------------------------------------- /structural-pattern/flyweight/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/flyweight/assets/intent.png -------------------------------------------------------------------------------- /structural-pattern/flyweight/assets/problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/flyweight/assets/problem.png -------------------------------------------------------------------------------- /structural-pattern/flyweight/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/flyweight/assets/pseudocode.png -------------------------------------------------------------------------------- /structural-pattern/flyweight/assets/solution1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/flyweight/assets/solution1.png -------------------------------------------------------------------------------- /structural-pattern/flyweight/assets/solution2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/flyweight/assets/solution2.png -------------------------------------------------------------------------------- /structural-pattern/flyweight/assets/solution3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/flyweight/assets/solution3.png -------------------------------------------------------------------------------- /structural-pattern/flyweight/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/flyweight/assets/structure.png -------------------------------------------------------------------------------- /structural-pattern/proxy/README.md: -------------------------------------------------------------------------------- 1 | # Proxy 2 | 3 | ## 📜 Mục đích 4 | 5 | **Proxy** là một design pattern thuộc nhóm structural cho phép bạn cung cấp một vật thay thế hoặc vật giữ chỗ cho một đối tượng khác. Một proxy kiểm soát quyền truy cập đến đối tượng ban đầu, cho phép bạn thực hiện điều gì đó trước hoặc sau khi yêu cầu được chuyển đến đối tượng ban đầu. 6 | 7 | ![intent](./assets/intent.png) 8 | 9 | ## 😟 Vấn đề 10 | 11 | Tại sao bạn muốn kiểm soát truy cập đến một đối tượng? Hãy xem một ví dụ: nếu bạn có một đối tượng khổng lồ tiêu thụ một lượng lớn tài nguyên hệ thống. Bạn sẽ chỉ cần nó trong một vài thời điểm chứ không phải mọi lúc. 12 | 13 | ![problem](./assets/problem.png) 14 | 15 | Thế nên bạn có thể triển khai lazy initialization: tạo đối tượng chỉ khi thực sự cần đến nó. Tất cả đối tượng của client sẽ cần thực thi một số code deferred initialization. Thật không may điều này có thể gây ra code trùng lặp. 16 | 17 | Nếu như đặt code này trực tiếp vào lớp đối tượng thì chỉ có thể thực hiện ở thế giới lý tưởng, vì thực tế lớp này có thể là một phần của một thư viện bên thứ 3. 18 | 19 | ## 😊 Giải pháp 20 | 21 | Proxy đưa ra ý tưởng rằng bạn nên tạo một lớp proxy mới với interface giống như đối tượng dịch vụ ban đầu. Sau đó, bạn cập nhật ứng dụng của mình để ứng dụng chuyển đối tượng proxy đến tất cả các client của đối tượng ban đầu. Khi nhận được yêu cầu từ client, proxy sẽ tạo một đối tượng dịch vụ thực và ủy thác tất cả công việc cho nó. 22 | 23 | ![solution](./assets/solution.png) 24 | 25 | *Proxy cải trang bản thân như một đối tượng cơ sở dữ liệu. Nó có thể thực hiện lazy initialization và lưu kết quả vào bộ đêm mà cả cả client và cơ sở dữ liệu thực không hề biết.* 26 | 27 | Vậy điều này có lợi ích gì? Nếu bạn cần thực thi điều gì đó trước hoặc sau logic chính của lớp, proxy sẽ giúp bạn làm điều đó mà không cần thay đổi lớp. Vì proxy triển khai cùng interface với lớp gốc, nên nó có thể được truyền đến bất kỳ client nào đang mong đợi một đối tượng dịch vụ thực. 28 | 29 | ## 🚗 Thế Giới Thực 30 | 31 | ![analogy](./assets/analogy.png) 32 | 33 | Credit card là một proxy cho một tài khoản khoản ngân hàng, thứ là proxy cho một khoản tiền mặt. Cả hai triển khai cùng interface để bạn có thể sử dụng nó cho thanh toán. Điều này làm khách hàng cảm thấy thoải mái vì họ không cần phải mang theo tiền mặt mọi lúc. Đồng thời chủ shop cũng cảm thấy vui vẻ vì số tiền từ một giao dịch điện tử đến tài khoản ngân hàng của shop sẽ giảm nguy cơ bị mất đặt cọc hay bị cướp trên đường. 34 | 35 | ## 🏢 Cấu trúc 36 | 37 | ![structure](./assets/structure.png) 38 | 39 | 1. **Service Interface** khai báo interface của **Service**. Proxy phải theo sau interface này để có thể cải trang bản thân như một đối tượng dịch vụ. 40 | 2. **Service** lớp cung cấp logic nghiệp vụ hữu ích thực sự. 41 | 3. **Proxy** lớp có trường tham chiếu đến một đối tượng dịch vụ. Sau khi proxy kết thúc tiến trình của nó (lazy initialization, logging, điều khiển truy cập, catching,...) nó truyền yêu cầu đến đối tượng dịch vụ thực. 42 | Thông thường, proxy quản lý chu kỳ hoàn chỉnh của đối tượng dịch vụ. 43 | 4. **Client** nên làm việc với cả service và proxy thông qua cùng interface. Cách này giúp bạn có thể truyền proxy vào bất kỳ đoạn code nào cần một đối tượng dịch vụ. 44 | 45 | ## 👨‍💻 Mã giả 46 | 47 | Ví dụ này, minh họa cách Proxy có thể giúp lưu vào bộ nhớ cache và lazy initialization với thư viện tích hợp YouTube của bên thứ ba. 48 | 49 | ![structure](./assets/structure.png) 50 | 51 | Thư viện cung cấp cho ta một lớp để tải video về. Tuy nhiên, nó rất kém hiệu quả. Nếu ứng dụng client yêu cầu cùng một video nhiều lần, thư viện sẽ tải nó nhiều lần thay vì lưu vào bộ nhớ đệm và sử dụng lại file tải về đầu tiên. 52 | 53 | Lóp proxy triển khai cùng interface như lớp tải về gốc và uỷ thác cho nó tất cả công việc. Tuy nhiên, nó sẽ theo dõi các file đã tải về và lấy kết quả từ bộ đệm khi ứng dụng yêu cầu cùng một video nhiều lần. 54 | 55 | ```c 56 | // Interface của dịch vụ từ xa. 57 | interface ThirdPartyYouTubeLib is 58 | method listVideos() 59 | method getVideoInfo(id) 60 | method downloadVideo(id) 61 | 62 | 63 | // Triển khai cụ thể của dịch vụ kết nối. Phương thức của lớp 64 | // này có thể yêu cầu thông tin từ Youtube. Tốc độ của yêu cầu 65 | // dựa trên kết nối internet của người dùng cũng như của Youtube. 66 | // Ứng dụng sẽ tải chậm nếu có nhiều yêu cầu cùng lức, kể cả khi 67 | // tất cả yêu cầu cùng một thông tin. 68 | class ThirdPartyYouTubeClass implements ThirdPartyYouTubeLib is 69 | method listVideos() is 70 | // Gửi yêu cầu API đến YouTube. 71 | 72 | method getVideoInfo(id) is 73 | // Lấy metadata về một vài video. 74 | 75 | method downloadVideo(id) is 76 | // Tải file video từ YouTube. 77 | 78 | 79 | // Để tiết kiệm brandwidth, bạn có thể lưu kết quả yêu cầu vào 80 | // bộ đệm và giữ nó cho một vài thời điểm. Nhưng không thể 81 | // đặt code trực tiếp vào lớp service. Ví dụ, nó có thể được 82 | // cung cấp như là một phần của thư viện bên thứ ba đã xác 83 | // định là `final`. Đó là lý do tại sao ta đặt code caching vào 84 | // lớp proxy mới, thứ triển khai interface giống như là lớp 85 | // service. Nó uỷ thác cho đối tượng service chỉ khi yêu cầu 86 | // thực sự cần gửi đi. 87 | class CachedYouTubeClass implements ThirdPartyYouTubeLib is 88 | private field service: ThirdPartyYouTubeLib 89 | private field listCache, videoCache 90 | field needReset 91 | 92 | constructor CachedYouTubeClass(service: ThirdPartyYouTubeLib) is 93 | this.service = service 94 | 95 | method listVideos() is 96 | if (listCache == null || needReset) 97 | listCache = service.listVideos() 98 | return listCache 99 | 100 | method getVideoInfo(id) is 101 | if (videoCache == null || needReset) 102 | videoCache = service.getVideoInfo(id) 103 | return videoCache 104 | 105 | method downloadVideo(id) is 106 | if (!downloadExists(id) || needReset) 107 | service.downloadVideo(id) 108 | 109 | 110 | // Lớp GUI, thứ đã từng làm việc trực tiếp với đối tượng service, 111 | // không cần thay đổi gì miễn là nó làm việc với đối tượng service 112 | // thông qua một interface. Ta có thể truyền đối tượng proxy thay 113 | // vì đối tượng service thực vì cả hai triển khai cùng interface. 114 | class YouTubeManager is 115 | protected field service: ThirdPartyYouTubeLib 116 | 117 | constructor YouTubeManager(service: ThirdPartyYouTubeLib) is 118 | this.service = service 119 | 120 | method renderVideoPage(id) is 121 | info = service.getVideoInfo(id) 122 | // Hiển thị trang video. 123 | 124 | method renderListPanel() is 125 | list = service.listVideos() 126 | // Hiển thi danh sách hình ảnh của video. 127 | 128 | method reactOnUserInput() is 129 | renderVideoPage() 130 | renderListPanel() 131 | 132 | // Ứng dụng có thể cấu hình proxy đang chạy. 133 | class Application is 134 | method init() is 135 | aYouTubeService = new ThirdPartyYouTubeClass() 136 | aYouTubeProxy = new CachedYouTubeClass(aYouTubeService) 137 | manager = new YouTubeManager(aYouTubeProxy) 138 | manager.reactOnUserInput() 139 | ``` 140 | 141 | ## 💡 Ứng dụng 142 | 143 | Có rất nhiều cách để sử dụng Proxy. Hãy cùng điểm qua những cách sử dụng phổ biến nhất. 144 | 145 | **🐞 Lazy intialization (virtual proxy). Khi bạn có một đối tượng dịch vụ nặng gây lãng phí tài nguyên hệ thống do luôn hoạt động, mặc dù thỉnh thoảng bạn mới cần nó.** 146 | 147 | ⚡ Thay vì tạo đối tượng khi ứng dụng khởi chạy, bạn có thể trì hoãn việc khởi chạy đối tượng đến thời điểm thực sự cần thiết. 148 | 149 | **🐞 Kiểm soát truy cập (protection proxy). Khi bạn muốn chỉ những client cụ thể mới có thể sử dụng đối tượng dịch vụ.** 150 | 151 | ⚡ Ví dụ: khi các đối tượng của bạn là các phần quan trọng của hệ điều hành và các ứng dụng client là các ứng dụng được khởi chạy khác nhau (bao gồm cả những ứng dụng độc hại). 152 | 153 | Proxy có thể chuyển yêu cầu đến đối tượng dịch vụ nếu thông tin đăng nhập của client phù hợp với một số tiêu chí. 154 | 155 | **🐞 Thực thi cục bộ một dịch vụ từ xa (remote proxy). Khi đối tượng dịch vụ được đặt trên một máy chủ từ xa.** 156 | 157 | ⚡ Trong trường hợp này, proxy chuyển yêu cầu của client qua mạng, xử lý tất cả các chi tiết khó chịu khi làm việc với mạng. 158 | 159 | **🐞 Logging request (logging proxy). Khi bạn muốn giữ lịch sử của các yêu cầu đối với đối tượng dịch vụ.** 160 | 161 | ⚡ Proxy có thể ghi lại từng yêu cầu trước khi chuyển nó đến dịch vụ. 162 | 163 | **🐞 Lưu kết quả yêu cầu vào bộ đệm (caching proxy). Khi bạn cần lưu vào bộ đệm kết quả của các yêu cầu client và quản lý vòng đời của bộ đệm này, đặc biệt nếu kết quả khá lớn.** 164 | 165 | ⚡ Proxy có thể triển khai bộ nhớ đệm cho các yêu cầu định kỳ luôn mang lại kết quả giống nhau. Proxy có thể sử dụng các tham số của yêu cầu làm khóa bộ nhớ cache. 166 | 167 | **🐞 Tham chiếu thông minh. Khi bạn cần loại bỏ một đối tượng nặng khi không có ứng dụng client nào sử dụng nó.** 168 | 169 | ⚡ Proxy có thể theo dõi các client nhận được tham chiếu đến đối tượng dịch vụ hoặc kết quả của nó. Đôi khi, proxy có thể đi qua các client và kiểm tra xem chúng có còn hoạt động hay không. Nếu danh sách client trống, proxy có thể loại bỏ đối tượng dịch vụ và giải phóng tài nguyên hệ thống. 170 | 171 | Proxy cũng có thể theo dõi xem client đã sửa đổi đối tượng dịch vụ hay chưa. Sau đó, các đối tượng không thay đổi có thể được sử dụng lại bởi các client khác. 172 | 173 | ## 📋 Triển khai 174 | 175 | 1. Nếu không có interface dịch vụ nào có sẵn, hãy tạo một interface để làm cho các đối tượng proxy và dịch vụ có thể hoán đổi cho nhau. Không phải lúc nào bạn cũng có thể trích xuất interface khỏi lớp dịch vụ vì bạn cần thay đổi tất cả các client của dịch vụ sử dụng interface đó. Kế hoạch B là làm cho proxy trở thành lớp con của lớp dịch vụ và theo cách này nó sẽ kế thừa interface của dịch vụ. 176 | 2. Tạo lớp proxy. Nó phải có một trường để lưu trữ một tham chiếu đến dịch vụ. Thông thường, proxy tạo và quản lý toàn bộ vòng đời của các dịch vụ của họ. Trong những trường hợp hiếm hoi, một dịch vụ được client chuyển tới proxy thông qua một phương thức khởi tạo. 177 | 3. Thực hiện các phương pháp ủy quyền theo mục đích của chúng. Trong hầu hết các trường hợp, sau khi thực hiện một số công việc, proxy sẽ ủy quyền công việc cho đối tượng dịch vụ. 178 | 4. Xem xét việc thêm một phương pháp tạo quyết định xem client nhận được một proxy hay một dịch vụ thực sự. Đây có thể là một phương thức tĩnh đơn giản trong lớp proxy hoặc một phương thức gốc đầy đủ. 179 | 5. Xem xét việc triển khai lazy initialization cho đối tượng dịch vụ. 180 | 181 | ## ⚖️ Ưu nhược điểm 182 | 183 | ### Ưu điểm 184 | 185 | ✔️ Bạn có thể kiểm soát đối tượng dịch vụ mà client không biết về nó. 186 | 187 | ✔️ Bạn có thể quản lý vòng đời của đối tượng dịch vụ khi client không quan tâm đến nó. 188 | 189 | ✔️ Proxy hoạt động ngay cả khi đối tượng dịch vụ chưa sẵn sàng hoặc không có sẵn. 190 | 191 | ✔️ *Open/Closed Principle*. Bạn có thể thêm proxy mới mà không cần thay đổi dịch vụ hoặc ứng dụng client. 192 | 193 | ### Nhược điểm 194 | 195 | ❌ Code có thể trở nên phức tạp hơn vì bạn cần giới thiệu nhiều lớp mới. 196 | 197 | ❌ Phản hồi từ dịch vụ có thể bị trì hoãn. 198 | 199 | 200 | ## 🔁 Quan hệ với các pattern khác 201 | 202 | **Adapter** cung cấp một interface khác cho đối tượng được bọc, **Proxy** cung cấp cho nó một interface tương tự và **Decorator** cung cấp cho nó một interface nâng cao. 203 | 204 | **Decorator** và **Proxy** có cấu trúc tương tự, nhưng nội dung rất khác nhau. Cả hai pattern đều được xây dựng trên cùng nguyên tắc, trong đó một đối tượng được cho là ủy quyền một số công việc cho đối tượng khác. Sự khác biệt là **Proxy** thường tự quản lý vòng đời của đối tượng dịch vụ của nó, trong khi thành phần của **Decorator** luôn được kiểm soát bởi client. 205 | 206 | **Facade** tương tự như **Proxy** ở chỗ cả hai đều đệm một thực thể phức tạp và tự khởi tạo nó. Không giống như **Facade**, **Proxy** có interface giống với đối tượng dịch vụ của nó, điều này làm cho chúng có thể hoán đổi cho nhau. 207 | 208 | # Nguồn 209 | 210 | [**refactoring**](https://refactoring.guru/design-patterns/proxy) 211 | -------------------------------------------------------------------------------- /structural-pattern/proxy/assets/analogy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/proxy/assets/analogy.png -------------------------------------------------------------------------------- /structural-pattern/proxy/assets/intent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/proxy/assets/intent.png -------------------------------------------------------------------------------- /structural-pattern/proxy/assets/problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/proxy/assets/problem.png -------------------------------------------------------------------------------- /structural-pattern/proxy/assets/pseudocode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/proxy/assets/pseudocode.png -------------------------------------------------------------------------------- /structural-pattern/proxy/assets/solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/proxy/assets/solution.png -------------------------------------------------------------------------------- /structural-pattern/proxy/assets/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ren0503/design-pattern/c87e0416d1fcd4ffae392ece0813dc8b68f7a733/structural-pattern/proxy/assets/structure.png --------------------------------------------------------------------------------