Kimn
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- ###### tags: `Design Pattern` --- # 11/01 ## 適配器模式 ### 概述 - 如果去歐洲國家旅遊的話,他們的插座如下圖最左邊,是歐洲標準,而我們使用的插頭如下圖最右邊,很顯然,我們的插頭是不能直接插到歐標的插座上面的。 - 因此我們的筆記本電腦、手機在當地都不能直接充電,所以此時我們就需要一個==插座轉換器==,轉換器第1面插入當地的插座,第2面供我們充電,這樣使得我們的插頭在當地就能使用了,也就是說我們的筆記本電腦、手機等在當地就可以正常的進行充電操作了。 ![](https://i.imgur.com/SugqFVt.png) ### 定義 - ==適配器模式指的是將一個類的接口轉換成客戶希望的另外一個接口,使得原本由於接口不兼容而不能一起工作的那些類能一起工作。== - 適配器模式分為類適配器模式和對象適配器模式,前者類之間的耦合度比後者高(這是因為類適配器模式使用的是繼承的方式,而對象適配器模式使用的是聚合或者組合的方式),且類適配器模式要求程序員了解現有組件庫中的相關組件的內部結構,所以應用相對較少些,用的更多的還是對象適配器模式。 ### 結構 適配器模式裡面總共擁有三個角色,它們分別是: - 目標(Target)接口:當前系統業務所期待的接口,它可以是抽像類或接口。==(歐式三孔插座)== - 適配者(Adaptee)類:它是被訪問和適配的現存組件庫中的組件接口。==(插頭)== - 適配器(Adapter)類:它是一個轉換器,通過==繼承==(類適配器模式)或==引用適配者的物件==(物件適配器模式),把適配者接口轉換成目標接口(也就是使用轉換器將三頭的歐標插座轉換成適合我們使用的兩頭插座),讓客戶按目標接口的格式訪問適配者。 很顯然,它對應上面案例中的==插座轉換器== ### 類適配器模式 - 實現方式:定義一個適配器類來實現當前系統的業務接口,同時又繼承現有組件庫中已經存在的組件。 :::info 例:讀卡機 現有一台電腦只能讀取SD卡,而要讀取TF卡中的內容就需要使用適配器模式。創建一個讀卡器,將TF卡中的內容讀取中來。 ![](https://i.imgur.com/1vd6YPU.png) ::: ```typescript= // 適配者類的接口 interface TFCard { readTF(): string; writeTF(msg: string): void; } class TFCardImpl implements TFCard { readTF(): string { const msg = "TFCard 讀取訊息 : Hello World TF"; return msg; } writeTF(msg: string): void { console.log(`TFCard 寫入訊息 : ${msg}`); } } // 目標接口 interface SDCard { readSD(): string; writeSD(msg: string): void; } class SDCardImpl implements SDCard { readSD(): string { const msg = "SDCard 讀取訊息 : Hello World SD"; return msg; } writeSD(msg: string): void { console.log(`SDCard 寫入訊息 : ${msg}`); } } // 電腦類 class Computer { // 從 SD 卡中讀取數據 readSD(sdCard: SDCard) { if (sdCard == null) { throw new Error("sd 卡不能為空"); } return sdCard.readSD(); } } // 適配器類 - 實做 SD 卡 class SDAdapterTF extends TFCardImpl implements SDCard { readSD(): string { console.log("適配器讀取 TF 卡"); return this.readTF(); } writeSD(msg: string): void { console.log("適配器寫入 TF 卡"); this.writeTF(msg); } } class Client { static main(): void { const computer = new Computer(); const msg = computer.readSD(new SDCardImpl()); console.log(msg); console.log("-------------"); // 使用該電腦讀取 TF 中的數據 // 定義適配器類 const msgTF = computer.readSD(new SDAdapterTF()); console.log(msgTF); } } Client.main(); ``` - ==類適配器模式違背了合成復用原則。== - 類適配器是客戶類只有一個接口規範的情況下可用,反之不可用。(要有SD介面) ### 物件適配器模式 - 物件適配器模式可採用將現有組件庫中已經實現的組件引入適配器類中,該類同時實現當前系統的業務接口。 - 我們使用物件適配器模式將讀卡機案例進行改寫。類圖如下: ![](https://i.imgur.com/LNohy0K.png) ```typescript= // 適配器類 - 實做 SD 卡 class SDAdapterTF implements SDCard { // 聲明適配者類 private _tfCard: TFCard; constructor(tfCard: TFCard) { this._tfCard = tfCard; } readSD(): string { console.log("適配器讀取 TF 卡"); return this._tfCard.readTF(); } writeSD(msg: string): void { console.log("適配器寫入 TF 卡"); this._tfCard.writeTF(msg); } } class Client { static main(): void { const computer = new Computer(); const msg = computer.readSD(new SDCardImpl()); console.log(msg); console.log("-------------"); // 使用該電腦讀取 TF 中的數據 // 定義適配器類 const adapter = new SDAdapterTF(new TFCardImpl()); const msgTF = computer.readSD(adapter); console.log(msgTF); } } Client.main(); ``` > 注意:還有一個適配器模式是接口適配器模式。 > 當不希望實現一個接口中所有的方法時,可以創建一個抽象類 Adapter,實現所有方法。 > 而我們只需要繼承該抽象類即可。 ### 應用場景 - 以前開發的系統存在滿足新系統功能的類,但其接口與新系統的接口不一致。 - 使用第三方提供的組件,但組件接口定義和自己要求的接口定義不同。 ## 裝飾者模式 ### 概述 - 例:快餐店有炒麵、炒飯這些快餐,可以額外附加雞蛋、火腿、培根這些配菜,當然加配菜需要額外加錢,每個配菜的價錢通常不太一樣,那麼這樣計算總價就會顯得比較麻煩。 ![](https://i.imgur.com/oAR4lgW.png) - 使用繼承的方式存在的問題: - 擴產性不好: - 如果要再加一種配料(火腿),我們就會發現需要給 FriedRice、FriedNoodlrs 分別定義一個子類。如果要新增一個快餐品類(炒米粉)的話,就需要定義更多的子類。 - 產生過多的子類 - 定義: 只在==不改變現有物件結構==的情況下,動態的給物件==增加一些職責==(即增加其額外功能)的模式。 ### 結構 裝飾(Decorator)模式中的角色: - 抽象構件(Component)角色:定義一個抽象接口以規範準備接收附加責任的對象。注意,此處的抽象接口既可以是接口也可以是抽像類。該角色對應以上快餐店案例中的快餐類 - 具體構件(ConcreteComponent)角色:實現抽象構件,通過裝飾角色為其添加一些職責。例如,以上快餐店案例中的炒飯類、炒麵類都屬於具體構建角色 - 抽象裝飾(Decorator)角色:繼承或實現抽象構件,並包含具體構件的實例(也就是說將其聚合進來了),可以通過其子類擴展具體構件的功能。所以,裝飾者模式巧妙就巧妙在這個位置 - 具體裝飾(ConcreteDecorator)角色:實現抽象裝飾的相關方法,並給具體構件對象添加附加的責任 ### 案例 - 我們使用裝飾者模式對快餐店案例進行改造,體會裝飾者模式的精髓。 ![](https://i.imgur.com/vBriChn.png) ```typescript= // 抽象構建角色 abstract class FastFood { private _price: number; private _desc: string; constructor(price: number, desc: string) { this._price = price; this._desc = desc; } abstract cost(): number; // get set .... } // 具體構建角色 class FriedRice extends FastFood { constructor() { super(10, "炒飯"); } public cost(): number { return this.price; } } // 裝飾者類(抽象裝飾者角色) abstract class Garnish extends FastFood { // 聲明快餐類的變量 private _fastFood: FastFood; constructor(price: number, desc: string, fastFood: FastFood) { super(price, desc); this._fastFood = fastFood; } // get set .... } //具體裝飾者角色 class Egg extends Garnish { constructor(fastFood: FastFood) { super(1, "雞蛋", fastFood); } // 計算價格 public cost(): number { return this.price + this.fastFood.cost(); } public override get desc(): string { return `${super.desc}${this.fastFood.desc}`; } } class Client { static main(): void { // 點一份炒飯 let food: FastFood = new FriedRice(); console.log(`${food.desc} ${food.cost()}元`); // 在上面的炒飯中 加雞蛋 food = new Egg(food); console.log(`${food.desc} ${food.cost()}元`); // 再加一個雞蛋 food = new Egg(food); console.log(`${food.desc} ${food.cost()}元`); // 再加一個培根 food = new Bacon(food); console.log(`${food.desc} ${food.cost()}元`); } } Client.main(); ``` ### 好處 - 裝飾者模式可以帶來比繼承更加靈活性的擴展功能,使用更加方便,可以通過組合不同的裝飾者物件來獲取具有不同行為狀態的多樣化結果。 - 裝飾者模式比繼承更具良好的擴展性,完美地遵循開閉原則,==繼承是靜態的附加責任,裝飾者則是動態的附加責任。== - 裝飾類和被裝飾類可以獨立發展,不會互相耦合,裝飾模式是繼承的一個替代模式,裝飾模式可以動態擴展一個實現類的功能。 ### 使用場景 - 當不能採用繼承的方式對系統進行擴充或者採用繼承不利於系統擴展和維護時,我們就可以使用裝飾者模式了。 - 不能採用繼承的情況主要有兩類: - 第一類是系統中存在大量獨立的擴展(比如說配料或者快餐的品種),為支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增長。而定義太多的子類會讓系統變得更加的複雜 - 第二類是因為類定義不能繼承(如 final 類) - 在不影響其他對象的情況下,以動態、透明的方式給單個對象添加職責 - 當對象的功能要求可以動態地添加,也可以再動態地撤銷時。 動態地撤銷又該怎麼去理解呢?我用上面的快餐店案例再來給大家詳細解釋一下。快餐店裡面的雞蛋賣完了之後,我們只需要動態地把 Egg 這個子類移除掉就可以了;又買了一些雞蛋之後,再將該子類添加上就行,這就是所謂的動態地添加和撤銷 ### 代理和裝飾者的區別 靜態代理和裝飾者模式的區別: - 相同點: - 都要實現與目標類相同的業務接口 - 在兩個類都要聲明目標物件 - 都可以在不修改目標類的前提下增強目標方法 - 不同點: - 目的不同:==裝飾者是為了增強目標物件、靜態代理是為了保護隱藏目標物件== - 獲取目標物件構建的地方不同:==裝飾者是由外界傳遞進來,可以通過構造方法傳遞、靜態代理是在代理內部創建,以此來隱藏目標物件== ## 橋接模式 ### 概述 - 現在有一個需求,需要創建不同的圖形,並且每個圖形都可能會有不同的顏色。我們可以利用繼承的方式來設計類的關係。 ![](https://i.imgur.com/ihupTBr.png) - 我們可以發現有很多的類,假如我們再增加一個形狀或再增加一種顏色的話,你會發現需要創建更多的子類,極有可能會出現類爆炸的現象。 - 試想,在一個有多種可能會變化的維度的系統中,用繼承方式勢必就會造成類爆炸的現象,擴展起來也不靈活,即使它滿足了開閉原則。每次在一個維度上新增一個具體實現都要增加多個子類。此時,為了更加靈活的設計系統,我們便可以考慮使用橋接模式了。 ### 定義 - 橋接模式是指將抽象與實現分離,使它們可以獨立變化。它是用==組合關係代替繼承關係來實現==,從而降低了抽象和實現這兩個可變維度的耦合度。 ### 結構 橋接(Bridge)模式包含以下主要角色: - 抽象化(Abstraction)角色:定義抽像類,並包含了一個對實現化物件的引用。 - 擴展抽象化(Refined Abstraction)角色:是抽象化角色的子類,實現父類中的業務方法,並通過組合關係調用實現化角色中的業務方法。 - 實現化(Implementor)角色:定義實現化角色的接口(注意,這裡可以是接口也可以是抽像類),供擴展抽象化角色調用。 - 具體實現化(Concrete Implementor)角色:給出實現化角色接口的具體實現。 ### 案例 :::info - 影片播放器: - 需要開發一個跨平台的影片播放器,可以在不同操作系統(如 window、mac、linux 等)上播放多種格式的影片,常見的影片格式包括 RMVB、AVI、WMV 等。 - 該播放器包==含了兩個維度,適合橋接模式。== ::: - 類圖如下: ![](https://i.imgur.com/IssPvo6.png) ```typescript= // 影片檔案(實現化角色) interface VideoFile { // 解碼功能 decode(fileName: string): void; } // 具體的實現化角色 class AviFile implements VideoFile { decode(fileName: string): void { console.log(`avi 影片檔案 ${fileName}`); } } // 抽象的操作系統類(抽象化角色) abstract class OperatingSystem { // 宣告 videoFile 變數 protected _videofile: VideoFile; constructor(videofile: VideoFile) { this._videofile = videofile; } abstract play(fileName: string): void; } // mac 操作系統(擴展抽象化角色) class Mac extends OperatingSystem { constructor(videofile: VideoFile) { super(videofile); } play(fileName: string): void { this._videofile.decode(fileName); } } class Client { static main(): void { // 創建 mac 系統物件 const system: OperatingSystem = new Mac(new AviFile()); // 使用操作系統播放影片 system.play("猛毒2"); } } Client.main(); // avi 影片檔案 猛毒2 ``` ### 好處 - 橋接模式提高了系統的可擴充性,在兩個變化維度中任意擴展一個維度,都不需要修改原有系統。 - 如:現在有新的格式 wmv,我們只需要再定義一個類實現 VideoFile 接口即可,其他類不需要發生變化。 - 實現細節對客戶透明。 ### 使用場景 - 當一個類存在兩個獨立變化的維度,且這兩個維度都需要進行擴展時。 - 當一個系統不希望使用繼承或因為多層次繼承導致系統類的個數急遽增加時。 - 當一個系統需要再構建的抽象話角色和具體化角色之間增加更多的靈活性時。避免在兩個層次之間建立靜態的繼承聯繫,通過橋接模式可以使他們在抽象層建立一個關聯關係。

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully