Design Pattern 學習筆記
本篇文章為筆者在學習 design pattern 的一些筆記與摘要,並非完全原創,文章內容大多截取自維基百科與 Learning Design Pattern in 30 real-case practices系列(已取得作者同意)。
GoF 設計模式裡面的各種 pattern 主要會以下列形式介紹:
Pattern 名稱
- 情境
(擷取自 ithome 系列文)
- 設計
(擷取自 ithome 系列文、wiki,自己的想法)
- From wiki
(擷取自 wiki)
- wiki 上面的 UML
- Reference 來源
S.O.L.I.D.
Single responsibility principle (SRP)
- A class should have only one reason to change.
- __每個物件,不管是類別、函數,負責的功能,都應該只做一件事__。對函數而言,一個函數內,同時做了兩件以上的事情。當發生錯誤時,很難快速定位錯誤的原因。另外,也容易間接導至程式碼的可閱讀性降低。
Reference:
SOLID 之 單一職責原則(Single responsibility principle)
09. 物件導向設計原則—SOLID
The open closed principle (OCP)
- Software entities (class, modules, functions, etc.) should be open for extension, but closed for modification.
- 優點:降低修改風險;只有新增程式碼,舊有程式因為沒修改,所以理論上問題當然會比較少。
- 潛在問題:擴展的情境並不一定在設計階段就會發現,常常要到了需求調整才會知道
Reference:
SOLID 之 開關原則(Open-Close principle)
Liskov Substitution Principle (LSP)
- Subtypes must be substitutable for their base types.
- 假設繼承實作並且 override 的時候,必須先思考修改內容的行為方向是否符合 parent class,如果行為不符合就意味著其實一開始就不屬於該 parent class
- Guide:
- child class 必須完全實作 parent class 的方法
- child class 可以有屬於自己的屬性和方法
- 覆寫或實作 parent class 的 method 時,參數要與 parent class 定義的一樣,或是更寬鬆。比方說:parent class 的定義是 List,child class 則可以是 List 或 Collection
- 覆寫或實作 parent class 的 method 時,回傳結果要跟 parent class 定義的一樣,或是縮小。比方說:parent class 是回傳 List ,child class 則可以回傳 List 或 ArrayList
- Param type: child > parent
- Return type: parent > child
- LSP 的目的:提高強健性
Reference:
SOLID 之 里氏替換原則(Liskov substitution principle)
里氏替換原則 Liskov Substitution Principle (LSP)
Interface Segregation Principle (ISP)
- Clients should not be forced to depend on methods that they do not use.
直譯:「客戶不應該被強迫依賴他們不使用的方法」
- 優點:在需要多型時,會比較容易為 class 實作對應的 method
Dependency inversion principle (DIP)
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend on details. Details should depend on abstractions.
from wiki
SOLID 之外的 Least Knowledge Principle
- Each unit should have only limited knowledge about other units: only units “closely” related to the current unit.
Reference
[不在 SOLID 裡的 最小知識原則(Least Knowledge Principle)(https://ithelp.ithome.com.tw/articles/10193110)
GoF 設計模式
Creational design patterns
Abstract Factory
情境(from ithome)
As a 開發人員
I want 建立一個 Interface 來讓開發人員建立各資料庫連線物件
So that 避免在多個程式碼區塊放入建立資料庫連線的細節,降低耦合度
設計
- 讓工廠類別(Factory Class)去做建立物件的細節,至於使用者只要使用 Factory 去建立需要的物件,不需要知道物件怎麼建立的,或是細節
- 常用的工廠模式:
From wiki
The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes. In normal usage, the client software creates a concrete implementation of the abstract factory and then uses the generic interface of the factory to create the concrete objects that are part of the theme. The client doesn’t know (or care) which concrete objects it gets from each of these internal factories, since it uses only the generic interfaces of their products. This pattern separates the details of implementation of a set of objects from their general usage and relies on object composition, as object creation is implemented in methods exposed in the factory interface.
from wiki
Builder
情境(from ithome)
As a 公司入口網站產品經理
I want 各BU在公司入口網站首頁看到屬於部門之資訊
So that 讓主管及同仁能迅速掌握資訊,並達到各部門之差異化。
設計
- 建造者模式包含了以下元素:
- Builder(建造者):負責建造
- Director(總監):請建造者依序執行建造的動作
- 假設入口網站包含了”報表”及”員工請假資訊”,而各 BU 看到的報表類型或哪些員工(介於多少職等)請假要列出來的條件皆不一樣。(參考範例)
- 和 Factory 的差別
- 初看 Builder 會很難抓到使用的時機,因為 Factory 可以解決大部分的 Creational 需求。
From wiki
The intent of the Builder design pattern is to separate the construction of a complex object from its representation. By doing so the same construction process can create different representations.
from wiki
Reference:
為什麼裝潢師傅做出來的不是我想要的? 你需要… (Builder 建造者模式)
Factory Method
From wiki
In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. This is done by creating objects by calling a factory method—either specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes—rather than by calling a constructor.
from wiki
Reference:
參考範例
Prototype
情境(from ithome)
As a 系統使用者
I want 降低查詢線上交易報表的回應時間
So that 提高作業效率
設計
- 原型模式有更深層的意義在於
- 儲放原型
- 方便的調用原型複製(Clone)功能
From wiki
The prototype pattern is a creational design pattern in software development. It is used when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects. This pattern is used to:
- avoid subclasses of an object creator in the client application, like the factory method pattern does.
- avoid the inherent cost of creating a new object in the standard way (e.g., using the ‘new’ keyword) when it is prohibitively expensive for a given application.
from wiki
Reference:
收到一筆要建立複製人軍隊的訂單怎麼辦? (Prototype 原型模式)
Singleton
情境(from ithome)
As a 業務副總
I want 在明天的新產品線上預購活動,讓預購客戶取得一個唯一的預購號碼,而且要在產品發表會螢幕上打上線上預購的數目。
So that 由這個成功的產品發表會來增加產品曝光率
From wiki
In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one “single” instance. This is useful when exactly one object is needed to coordinate actions across the system.
Reference:
別再因為發號碼牌重複被客訴! (Singleton 單例模式)
Structural design patterns
Adapter
情境(from ithome)
As a 資料分析者
I want 系統可以介接 XX 店家二代卡機傳回來的 EDI 並整理資料後存放在資料庫
So that 我們可以在資料庫做進一步客戶分析
設計
- 通過轉換已經存在類別的接口以適應而不改變它
- Adapter 分成兩種:
- 繼承(Class way)
- 組合(Object way)
- __Adapter 就像出國旅行用的萬用轉接頭__,負責將電器的插頭(來源)轉換成插座(Adaptee)可以用的模式。 來源只統一用 Adapter 這個接口,而不管細節
From wiki
In software engineering, the adapter pattern is a software design pattern (also known as wrapper, an alternative naming shared with the decorator pattern) that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.
from wiki
Reference:
江湖走跳,轉接頭很重要! (Adapter 適配器模式)
Bridge
情境(from ithome)
As a 建立訂單的秘書
I want 可以在同一介面上依據各供應商、產品和是否急件列印不同格式的訂單
So that 秘書可以不必因為上述之差異而到處 KEY 重複的資料
設計
- 分離 Abstraction 及 Implementor,使兩者可獨立變化
- 在 run time 設定 Abstraction 裡的 Implementor
- Strategy 屬於行為模式(Behavioral design patterns)
- __Bridge 屬於結構型模式(Structural design patterns)__,它將抽象和實做解耦合,使兩者可獨立的變化
From wiki
The bridge pattern is a design pattern used in software engineering that is meant to decouple an abstraction from its implementation so that the two can vary independently, introduced by the Gang of Four. The bridge uses encapsulation, aggregation, and can use inheritance to separate responsibilities into different classes.
from wiki
Reference:
橋來橋去! 以需求大化小,小化無(抽象)為目標! (Bridge 橋接模式)
Composite
情境(from ithome)
As a 人資經理
I want 系統可以帶出任一單位的組織圖
So that 可以讓老闆容易了解目前組織架構
From wiki
In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes a group of objects that are treated the same way as a single instance of the same type of object. The intent of a composite is to “compose” objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.
Composite should be used when clients ignore the difference between compositions of objects and individual objects. If programmers find that they are using multiple objects in the same way, and often have nearly identical code to handle each of them, then composite is a good choice; it is less complex in this situation to treat primitives and composites as homogeneous.
from wiki
Decorator
情境(from ithome)
As a 物流部秘書
I want 報價單系統可以在標準運費上加上其他服務費:加點/假日運送/延遲費”並費用最大化
So that 配合成本轉嫁客戶之策略,節首公司之支出
設計
- 在不繼承原有類別情況下,對原類別加強或新增功能
From wiki
In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern. The decorator pattern is structurally nearly identical to the chain of responsibility pattern, the difference being that in a chain of responsibility, exactly one of the classes handles the request, while for the decorator, all classes handle the request.
from wiki
Facade
情境(from ithome)
As a 資料分析者
I want 使用者在執行任一系統功能時,系統可以同時記錄使用記錄在文字檔和資料庫
So that 我們可以知道使用者何時使用了該功能和次數
設計
- 封裝商業邏輯並提供簡單介面,以達到易使用、可讀性、減少依賴的目的
From wiki
The facade pattern (also spelled façade) is a software-design pattern commonly used in object-oriented programming. Analogous to a facade in architecture, a facade is an object that serves as a front-facing interface masking more complex underlying or structural code.
A facade can:
- improve the readability and usability of a software library by masking interaction with more complex components behind a single (and often simplified) API
- provide a context-specific interface to more generic functionality (complete with context-specific input validation)
- serve as a launching point for a broader refactor of monolithic or tightly-coupled systems in favor of more loosely-coupled code
Developers often use the facade design pattern when a system is very complex or difficult to understand because the system has a large number of interdependent classes or because its source code is unavailable. This pattern hides the complexities of the larger system and provides a simpler interface to the client.
from wiki
from wiki
ReferenceL
不用看書就會,但不一定會唸的… (Facade 外觀模式)
Flyweight
情境(from ithome)
As a 公司官網管理者
I want 產品頁面可以更快速的顯示(<=2.0Sec)
So that 瀏覽者有好的使用者體驗
設計
- Flyweight 使用的時機如下:
- __共享對像會頻繁的建立__,而且每次建立需耗費可觀的資源
- 共享對像適合為獨立且輕量的的小元素,且不會由本身來改變其狀態和資料
- 提供介面讓外部程式改變其狀態和資料
- 網站的暫存資料很適合將其作為 Flyweight 物件存放並共享給整個網站使用,因為暫存資料一般有以下特性:
- 變動性小
- 因資料共用性,一般習慣將暫存以小範圍管理之方式建立
- 注意 Flyweight 為 Structural design pattern 而非 Creational design pattern,它通常會搭配 Factory,由工廠提供資料,而 Flyweight 提供一個共享的結構。
From wiki
A flyweight is an object that minimizes memory usage by sharing as much data as possible with other similar objects; it is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory. Often some parts of the object state can be shared, and it is common practice to hold them in external data structures and pass them to the objects temporarily when they are used.
from wiki
Reference:
程式碼也需要瘦身! (Flyweight 享元模式)
Proxy
情境(from ithome)
As a 物流部秘書
I want 報價單系統可以在其他服務費加上更多彈性:
- 加點: 若單趟載超過兩個點,第三個點開始每加一點加收總價*20%
- 延遲費: 超過兩小時後的時間,改為每小時NTD$1,000
So that 業務可在大單採原來之計價,但小單則採新制
設計
- What problems can the Proxy design pattern solve?
- The access to an object should be controlled.
- Additional functionality should be provided when accessing an object.
Proxy 和 Adapter 的差異(from ithome)
- Adapter
- 特性:
- 提供不同的介面,而主程式依賴於實體 Adapter 類別
- 當套用 Adapter 時,主程式使用的介面會改變
- 使用時機:
- __在不改變原始介面下__,提供不同的介面讓主程式可以使用原介面的功能
- 目的在於__適應__,不改變原功能的目的
- 特性:
- Proxy
- 特性:
- 提供相同的介面,讓主程式依賴於抽象
- 主程式使用的介面和方式不會改變
- 使用時機:
- 在不改變主程式的行為下,讓代理完成其它實作類別的功能
- 目的在於__控制__,可__加強或改變原功能的目的__
- 特性:
From wiki
A proxy, in its most general form, is a class functioning as an interface to something else. The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate. In short, a proxy is a wrapper or agent object that is being called by the client to access the real serving object behind the scenes. Use of the proxy can simply be forwarding to the real object, or can provide additional logic. In the proxy, extra functionality can be provided, for example caching when operations on the real object are resource intensive, or checking preconditions before operations on the real object are invoked. For the client, usage of a proxy object is similar to using the real object, because both implement the same interface.
from wiki
Reference:
老闆說給客戶的報價要有彈性! 但是只能多算不能少算! (Proxy 代理模式)
Behavioral design patterns
Chain of responsibility
情境(from ithome)
As a 產品經理
I want 多國語系介面
So that 顯示使用者對應的語系內容
設計
- 根據條件來定義一個有責任的接收者,以處理一個請求或將其轉發給鏈上的下一個接收者(如果有的話)
- chain 上的接收者可以選擇直接丟給下一個適合的接收者,或是選擇做事再判斷適合的接收者後往後丟
From wiki
In object-oriented design, the chain-of-responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain.
from wiki
Reference:
職場第一條規則:事情不要接了傻傻做,而是要交給負責的人! (Chain of Responsibility 職責鍊模式)
Command
設計
- 何時適合用 Command 呢?
- 當需要執行多個對象
- 且對象需要抽換策略和抽換執行策略的邏輯
From wiki
Four terms always associated with the command pattern are command, receiver, invoker and client. A command object knows about receiver and invokes a method of the receiver. Values for parameters of the receiver method are stored in the command. The receiver object to execute these methods is also stored in the command object by aggregation. The receiver then does the work when the execute() method in command is called. An invoker object knows how to execute a command, and optionally does bookkeeping about the command execution. The invoker does not know anything about a concrete command, it knows only about the command interface. Invoker object(s), command objects and receiver objects are held by a client object, the client decides which receiver objects it assigns to the command objects, and which commands it assigns to the invoker. The client decides which commands to execute at which points. To execute a command, it passes the command object to the invoker object.
from wiki
Reference:
讓你當一天艾森豪,來指揮諾曼地登陸作戰! (Command 命令模式)
Interpreter
情境(from ithome)
As a 資料分析者
I want 系統可以介接店家傳回來的 EDI 並整理資料後存放在資料庫
So that 我們可以在資料庫做進一步客戶分析
設計
- 定義一個表示法,使用表示法來解釋(翻譯)語言中的句子
From wiki
In computer programming, the interpreter pattern is a design pattern that specifies how to evaluate sentences in a language. The basic idea is to have a class for each symbol (terminal or nonterminal) in a specialized computer language. The syntax tree of a sentence in the language is an instance of the composite pattern and is used to evaluate (interpret) the sentence for a client.
from wiki
感覺跟 strategy 的概念有點相似,對於同一個介面個別實作細節
Reference:
雞同鴨講也可以通! (Interpreter 解譯器模式)
Iterator
情境(from ithome)
As a 電商老闆
I want 舉辦行銷活動,購物車結帳時:
- 書籍雜誌:會員相同類別10本以上八折優惠
- 生活用品:會員相同品項$1,000以上九折優惠
So that 提高網站轉換率及營收
From wiki
In object-oriented programming, the iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container’s elements. The iterator pattern decouples algorithms from containers; in some cases, algorithms are necessarily container-specific and thus cannot be decoupled.
from wiki
Reference:
你每天在用,但是可能不知道的… (Iterator 迭代器模式)
Mediator
情境(from ithome)
As a 銀行行員
I want 計算客戶評分時,可採用
- 各金融商品之評分模型但分別給與權重
- 各金融商品之評分模型,加總後作平均
So that 參考各模組之評分,達到KYC客戶風險評估之目的
設計
- 在 Mediator 中定義兩種角色(各自需建立抽象及實作類別)
- Colleague:合作的對象,並且透過 Mediator 與其他對像溝通
- Mediator:中介者,他知道所有合作的對象,並且協調這些對像協同作業
From wiki
In software engineering, the mediator pattern defines an object that encapsulates how a set of objects interact. This pattern is considered to be a behavioral pattern due to the way it can alter the program’s running behavior.
In object-oriented programming, programs often consist of many classes. Business logic and computation are distributed among these classes. However, as more classes are added to a program, especially during maintenance and/or refactoring, the problem of communication between these classes may become more complex. This makes the program harder to read and maintain. Furthermore, it can become difficult to change the program, since any change may affect code in several other classes.
With the mediator pattern, communication between objects is encapsulated within a mediator object. Objects no longer communicate directly with each other, but instead communicate through the mediator. This reduces the dependencies between communicating objects, thereby reducing coupling.
from wiki
Reference:
別再孤軍奮戰! 做好做滿第一件事情先找到能調用資源的人! (Mediator 中介者模式)
Python實現設計模式–08.中介者模式(Mediator Pattern)
Memento
情境(from ithome)
As a 電子表單使用者
I want 系統可以在我填寫表單時,提供記錄草稿的功能,儲存該張表單後,即刪除該單所有草稿,但若未儲存,則須保留草稿
So that 避免臨時無法完成表單而造成已填寫之資料遺失情況
設計
- 在 Memento 中,定義了以下角色
- Originator:擁有要被儲存的資料
- Memento:State
- Caretaker(管理人):管理與儲存 Memento(由 Originator 提供),提供存取的介面
From wiki
The memento pattern is a software design pattern that provides the ability to restore an object to its previous state (undo via rollback).
The memento pattern is implemented with three objects: the originator, a caretaker and a memento. The originator is some object that has an internal state. The caretaker is going to do something to the originator, but wants to be able to undo the change. The caretaker first asks the originator for a memento object. Then it does whatever operation (or sequence of operations) it was going to do. To roll back to the state before the operations, it returns the memento object to the originator. The memento object itself is an opaque object (one which the caretaker cannot, or should not, change). When using this pattern, care should be taken if the originator may change other objects or resources - the memento pattern operates on a single object.
Classic examples of the memento pattern include the seed of a pseudorandom number generator (it will always produce the same sequence thereafter when initialized with the seed state) and the state in a finite state machine.
from wiki
Observer
From wiki
The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.
It is mainly used to implement distributed event handling systems, in “event driven” software. In those systems, the subject is usually called a “stream of events” or “stream source of events”, while the observers are called “sink of events”. The stream nomenclature simulates or adapts to a physical setup where the observers are physically separated and have no control over the emitted events of the subject/stream-source. This pattern then perfectly suits any process where data arrives through I/O, that is, where data is not available to the CPU at startup, but can arrive “randomly” (HTTP requests, GPIO data, user input from keyboard/mouse/…, distributed databases and blockchains, …). Most modern languages have built-in “event” constructs which implement the observer pattern components. While not mandatory most ‘observers’ implementations will use background threads listening for subject events and other support mechanism from the kernel (Linux epoll, …)
from wiki
Reference:
察言觀色! 敵不動,我不動! (Observer 觀察者模式)
State
情境(from ithome)
As a 提需求單的使用者
I want 需求單管理系統支援在某個需求的狀態改變時,記錄時間並以Email通知同仁
So that 任何人可以輕易的識別目前需求的狀態
設計
- 實作 State 的方式
- 建立每一種狀態的類別,它們定義了該狀態下要做那些事,以及決定下一個狀態
- 建立存放狀態的對象(Context),它具有改變狀態的權力
- State v.s. Chain of Responsibility
- State 和 Chain of Responsibility 都可以解決有順序、多重判斷、執行邏輯的問題,e.g. IF ELSE,SWITCH CASE
- 兩者的差別
- State
- 由 Context 控制何時轉換及執行狀態
- 對像透過__已定義好的順序__轉換狀態
- Chain of Responsibility
- 對象只能決定何時發起鏈上的第一個點(Handler),鏈上其他的點接續完成作業
- 對像能在開始作業前,改變職責鏈的順序
- State
From wiki
The state pattern is a behavioral software design pattern that allows an object to alter its behavior when its internal state changes. This pattern is close to the concept of finite-state machines. The state pattern can be interpreted as a strategy pattern, which is able to switch a strategy through invocations of methods defined in the pattern’s interface.
The state pattern is used in computer programming to encapsulate varying behavior for the same object, based on its internal state. This can be a cleaner way for an object to change its behavior at runtime without resorting to conditional statements and thus improve maintainability.
from wiki
Reference:
別讓老闆和USER再問你好了沒? 隨時更新工作狀態吧! (State 狀態模式)
Strategy
情境(from ithome)
As a 資料分析者
I want 使用者在執行任一系統功能時,系統可以記錄使用記錄在文字檔
So that 我們可以知道使用者何時使用了該功能和次數
設計
- 定義多個演算法,各別封裝這些演算法,並讓它們可以互換
From wiki
In computer programming, the strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly.
from wiki
Reference:
謀略設計模式! 學習首重策略! (Strategy 策略模式)
Template
情境(from ithome)
As a 銀行交易員
I want 在衍生性金融商品管理系統可以自動比價
So that 提供獲利及損失模型,讓客戶理解投資風險
設計
- 就宣告一個 template class 然後繼承它,並實作其 abstract method
From wiki
The template method is a method in a superclass, usually an abstract superclass, and defines the skeleton of an operation in terms of a number of high-level steps. These steps are themselves implemented by additional helper methods in the same class as the template method.
from wiki
Reference:
一樣米養百種人! (Template Method 樣板方法)
Visitor
情境(from ithome)
As a 電商老闆
I want 舉辦行銷活動,購物車結帳時:
- 書籍雜誌:會員相同類別10本以上八折優惠
- 生活用品:會員相同品項$1,000以上九折優惠
So that 提高網站轉換率及營收
設計
- Visitor 是 Strategy 的延伸
- 在 Strategy 中,我們__只讓一個對象__執行注入的策略
- 在 Visitor 中,我們可以__讓很多對象__依序執行注入的策略
- 使用時機:
- Strategy 是設計來對__一個物件__注入不同處理邏輯
- Visitor 是設計來對__多個物件__注入處理邏輯(當然也可以對單一個物件注入不同處理邏輯)
- Strategy 簡單,適用於多數場合
- Visitor 使用 Strategy 的概念,適用於有多個實作類別或是子類別,而且每個類別需要特別的處理邏輯
From wiki
In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying the structures. It is one way to follow the open/closed principle.
In essence, the visitor allows adding new virtual functions to a family of classes, without modifying the classes. Instead, a visitor class is created that implements all of the appropriate specializations of the virtual function. The visitor takes the instance reference as input, and implements the goal through double dispatch.
from wiki