kyle shanks
    • 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
      • Invitee
    • Publish Note

      Publish Note

      Everyone on the web can find and read all notes of this public team.
      Once published, notes can be searched and viewed by anyone online.
      See published notes
      Please check the box to agree to the Community Guidelines.
    • 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
    • Engagement control
    • 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 Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control 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
Invitee
Publish Note

Publish Note

Everyone on the web can find and read all notes of this public team.
Once published, notes can be searched and viewed by anyone online.
See published notes
Please check the box to agree to the Community Guidelines.
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
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
--- title: 'Decorate 裝飾模式' disqus: kyleAlien --- Decorate 裝飾模式 === ## Overview of Content 如有引用參考請詳註出處,感謝 :smile: > 裝飾模式,在不改變其繼承類別動態拓展功能的方法,**==繼承的替代方案之一==**,**裝飾模式比繼承生成子類更靈活** 它的實現可參考 Java's IO 流、Android 的第三方庫 RxJava :::success * 如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/) 本篇文章對應的是 [**Decorate 裝飾模式 | 解說實現 | Android Framework Context**](https://devtechascendancy.com/object-oriented_design_decorate/) ::: [TOC] ## Decorate 使用場景 1. 當不能使用繼承或是使用了繼承後不利於維護時可以使用 :::warning 當繼承超過 2 層的時候,其實程式會難以維護,這時其實就可以重新檢視程式,並考慮轉換設計 ::: 2. 以不影響其他類別的方式 **動態的使用(動態新增、移除功能)、撤銷其類別,而不必依靠覆寫** (Override) :::success 繼承是靜態的給類新增功能、裝飾則是動態的給類新增功能 ::: 3. 對於原先的類做加裝、改裝功能(並不影響原先的類) ### Decorate 定義 & Decorate UML * **Decorate 定義** **動態的給一個物件添加(減少)額外功能,裝飾模式相比繼承更加靈活** * **Decorate 角色介紹** | 角色 | 功能說明 | | -------- | -------- | | **Component** | 可以是一個抽象,或是接口,它是最原始的物件(有最基礎、最核心的方法) | | **ConcreateComponent** | 具體實現類,也是等待被裝飾(被代理)者,它實現了最基礎的方法 | | **Decorator** | **它^1.^ 繼承 Component 類 並 ^2.^ 依賴抽象(Component 類)取代繼承**,內部持有一個抽象 Component 物件 | | **ConcreateDecorator** | 具體裝飾者,你要它組裝結合 ConcreateComponent 的實做一起表現出來 | > **依靠`注入抽象模組`(高層模組),使用別類(低層模組)實現功能** > ![](https://hackmd.io/_uploads/rJt6WgYSh.png) ### Decorate 設計 - 優缺點 * **Decorate 設計優點** : 1. 可以**減少里式原則帶來的,繼承時帶來的負荷** (一定要複寫該類 or 類別方法增多),子類無限擴張等問題 > 當然你需要切細每個類的方法 2. 用戶可依據需求使用,而不改變該類的方法,符合開閉原則 :::info 子類可以單獨發展,而不必受到繼承約束 ::: 3. 裝飾、被裝飾類都可以獨立發展!不會相互依賴~ (裝飾類依賴的是抽象) * **Decorate 設計缺點** : 1. 高層模組發生變化時勢必會影響到繼承的所有子類別 2. Debug 會更困難,因為**層層嵌套,邏輯變複雜** (可讀性降低) 3. 多層裝飾會導致效率變差 (仍是層層傳遞的問題) ## Decorate 實現 ### Decorate 標準實現 1. **`Component` 類**:IWear 擁有最基礎、核心的方法抽象 ```kotlin= interface IWear { fun wearClothes() } ``` 2. **`ConcreateComponent` 類**:待被裝飾的類(也可以說待被代理) ```kotlin= class NoWorkPerson : IWear { override fun wearClothes() { println("Wear Underwear") } } ``` 3. **`Decorator` 類**:繼承於 `IWear` 類,並持有一個 `IWear` 物件 ```kotlin= abstract class DecorClothes constructor(val realWear: IWear) : IWear { override fun wearClothes() { realWear.wearClothes() } } ``` 4. **`ConcreateDecorator` 類**:這個類的重點是在於實現你自己的邏輯,你要如何裝飾傳入的實際類 (這裡創建兩個裝飾類) ```kotlin= class WorkPerson constructor(realWear: IWear): DecorClothes(realWear) { // 裝飾方法 private fun wearLittle() { println("Wear shoes") } override fun wearLittle() { wearLittle() println("--- WorkPerson finish decor") super.wearClothes() } } class WorkHardPerson constructor(realWear: IWear): DecorClothes(realWear) { // 裝飾方法 private fun wearMore() { println("Wear T-short") println("Wear paint") } override fun wearClothes() { wearMore() println("--- WorkHardPerson finish decor") super.wearClothes() } } ``` * 作為使用者方,使用裝飾模式: ```kotlin= fun main() { // 一層一層裝飾,最終返回最後一個裝飾者 val finalMan = NoWorkPerson().let { noWorkPerson -> WorkPerson(noWorkPerson) }.let { workPerson -> WorkHardPerson(workPerson) } // 其實最終 finalMan 類型是 WorkHardPerson finalMan.wearClothes() } ``` > ![](https://i.imgur.com/31FdiiQ.png) ## Android Source Context 分析 Context 物件以說是 Android 開發者最熟悉,也是最不熟悉的物件了,它應用於 Android 4 大組件中(`Activity`、`Service`、`ContentProvider`、`Broadcast`),而與 Context 最直接關聯、也最常見的就是 Activity 跟 Application 而 Context 本身是個抽象類,在使用時其實也用到了裝飾模式 ```java= // Context.java public abstract class Context { ... 省略 } ``` :::success 以下分析的是 [**Android 10**](https://cs.android.com/android/platform/superproject/+/android-10.0.0_r40:) 的源碼 ::: ### Context & ContextImpl 的裝飾角色 * 我們這邊先直接揭露 [**Context**](https://cs.android.com/android/platform/superproject/+/android-10.0.0_r40:frameworks/base/core/java/android/content/Context.java) & [**ContextImpl**](https://cs.android.com/android/platform/superproject/+/android-10.0.0_r40:frameworks/base/core/java/android/app/ContextImpl.java) 在裝飾模式下的角色,再進行分析 | 裝飾模式 Prototype 角色 | Android 實做類 | 功能說明 | | ------- | - | -------- | | **Component** | `Context` | 可以是一個抽象,或是接口,它是最原始的物件(有最基礎、最核心的方法) | | **ConcreateComponent** | `ContextImpl` | 具體實現類,也是等待被裝飾(被代理)者,它實現了最基礎的方法 | | **Decorator** | ``ContextImpl``、`ContextWrapper` | **它^1.^ 繼承 Component 類 並 ^2.^ 依賴抽象(Component 類)取代繼承**,內部持有一個抽象 Component 物件 | Android Context 實現裝置模式的 UML 概念圖如下 > ![image](https://hackmd.io/_uploads/S1AFBbp0a.png) ### Context & ContextImpl 裝飾結構分析 * Context 類作為裝飾模式中抽象的「原始物件」,它並不實做,但擁有許多核心的抽象方法,其中就包括 **資源管理**、**文件管理**、**包管理**、**類加載**、**權限管理**、**提供系統服務**… 等等功能,我們依照 4 大組件的方向去看看 Context 與其的重要性 1. **裝飾模式下 Context 是作為 `Component` 角色** * 最常見 Context 提供「**Activity**」的相關功能如下 ```java= // Context.java public abstract class Context { ... 省略 // 取得應用的訊息 public abstract ApplicationInfo getApplicationInfo(); // 取得 Application 的 Context public abstract Context getApplicationContext(); // 啟動 Activity public abstract void startActivity(@RequiresPermission Intent intent); public abstract void startActivity(@RequiresPermission Intent intent, @Nullable Bundle options); // 啟動多個 Activity public abstract void startActivities(@RequiresPermission Intent[] intents); public abstract void startActivities(@RequiresPermission Intent[] intents, Bundle options); } ``` * 最常見 Context 提供「**Service**」的相關功能如下 ```java= // Context.java public abstract class Context { ... 省略 // 啟動 Service @Nullable public abstract ComponentName startService(Intent service); // 停止 Service public abstract boolean stopService(Intent service); // 應用綁定 Service public abstract boolean bindService(@RequiresPermission Intent service, @NonNull ServiceConnection conn, @BindServiceFlags int flags); // 解綁定 Service public abstract void unbindService(@NonNull ServiceConnection conn); } ``` * 最常見 Context 提供「**Broadcast**」的相關功能如下 ```java= // Context.java public abstract class Context { ... 省略 // 註冊一個廣播接收者 @Nullable public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter); @Nullable public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter, @RegisterReceiverFlags int flags); // 解註冊廣播接收者 public abstract void unregisterReceiver(BroadcastReceiver receiver); // 發送廣播 public abstract void sendBroadcast(@RequiresPermission Intent intent); // 發送帶有權限的廣播 public abstract void sendBroadcast(@RequiresPermission Intent intent, @Nullable String receiverPermission); public abstract void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions); // 發送順序廣播 public abstract void sendOrderedBroadcast(@RequiresPermission Intent intent, @Nullable String receiverPermission); // 發送黏性廣播,這意味著 Intent 會保留在廣播內 public abstract void sendStickyBroadcast(@RequiresPermission Intent intent); public abstract void sendStickyOrderedBroadcast(@RequiresPermission Intent intent, BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras); } ``` * 最常見 Context 提供「**ContentProvider**」的相關功能如下 ```java= // Context.java public abstract class Context { ... 省略 // 為該應用取得一個 ContentResolver 物件 public abstract ContentResolver getContentResolver(); } ``` * 最常見 Context 提供的重要功能: ```java= // Context.java public abstract class Context { ... 省略 public abstract Looper getMainLooper(); public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name); public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name); public abstract SharedPreferences getSharedPreferences(String name, @PreferencesMode int mode); @Nullable public abstract File getExternalFilesDir(@Nullable String type); } ``` 2. **裝飾模式下 ContextImpl 類,既可作為作為 `ConcreateComponent` 也可作為 `Decorator` 角色**,也就是它本身可以是個沒有裝飾功能的「實做」,也可以「裝飾類」 以下是 [**ContextImpl 的建構函數**](https://cs.android.com/android/platform/superproject/+/android-10.0.0_r40:frameworks/base/core/java/android/app/ContextImpl.java;l=2516):我們可以看到,它是可以傳入 `ContextImpl` 類,而這個參數其實也就透漏個它是一個裝飾設計(也就是說可以加強、減弱、使用… 傳入的 ContextImpl 物件) ```java= // ContextImpl.java private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread, ...) { ... 忽略部份 if (container != null) { // 取外部 ContextImpl 物件來使用 mBasePackageName = container.mBasePackageName; opPackageName = container.mOpPackageName; setResources(container.mResources); mDisplay = container.mDisplay; } else { ... 忽略部份 } ... 忽略部份 } ``` * **ContextImpl 作為 `Decorator` 「實做」角色**:創建 System Context 時會傳入 `null`,代表的是 **不去裝飾**(也就是 **最外層的 Context**) ```java= // ContextImpl.java @UnsupportedAppUsage static ContextImpl createSystemContext(ActivityThread mainThread) { LoadedApk packageInfo = new LoadedApk(mainThread); ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, null, null); ... 忽略 return context; } ``` * **ContextImpl 作為 `ConcreateComponent` 「裝飾類」角色**:創建 Applicatoin Context 時會傳入 `this`,代表 **可以做裝飾**(也就是某一層的 Context) ```java= // ContextImpl.java @Override public Context createApplicationContext(ApplicationInfo application, int flags) throws NameNotFoundException { LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE); if (pi != null) { ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null ... 忽略 } throw new PackageManager.NameNotFoundException( "Application package " + application.packageName + " not found"); } ``` ### 最明顯的裝飾 ContextWrapper * 如果你還是沒辦法看出 Context 裝飾模式的使用的話,我們可以看看 [**`ContextWrapper` 類**](https://cs.android.com/android/platform/superproject/+/android-10.0.0_r40:frameworks/base/core/java/android/content/ContextWrapper.java),從這個類中我們可以很清晰的看到 ContextWrapper 其實沒做甚麽事情,單純的是把外部傳入的 Context 保存起來,並在需要的時候呼叫使用 我們看看最常使用的 `startActivity` 類就可以發現,源碼如下 ```java= // ContextWrapper.java public class ContextWrapper extends Context { // 外部傳入的 Context @UnsupportedAppUsage Context mBase; public ContextWrapper(Context base) { // 保存外部傳入的 Context mBase = base; } @Override public void startActivity(Intent intent) { // 直接呼叫外部傳入的 Context mBase.startActivity(intent); } ... 省略部份 } ``` ### Activity 綁定 Context 的重點 * 這邊我們進入 `performLaunchActivity` 函數: :::warning * 這裡我們忽略 Android 系統是如何啟動的細節,直接看創建 Activity 的關鍵代碼,也就是從 [**ActivityThread**](https://cs.android.com/android/platform/superproject/+/android-10.0.0_r40:frameworks/base/core/java/android/app/ActivityThread.java;l=3347)#`performLaunchActivity` 開始看 ::: ```java= // ActivityThread.java private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ... 省略部份 // 創建 ContextImpl 類 ContextImpl appContext = createBaseContextForActivity(r); Activity activity = null; Activity activity = null; try { java.lang.ClassLoader cl = appContext.getClassLoader(); // 創建目標 Activity activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); } ... 省略 catch try { // 如果沒有 Application 的話,就創建 Application instacne Application app = r.packageInfo.makeApplication(false, mInstrumentation); ... 省略部份 if (activity != null) { ... 省略部份 // 連接 Context & Activity 的關係 appContext.setOuterContext(activity); // 連接 Activity & Context & Application 類 activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback, r.assistToken); ... 省略部份 r.activity = activity; } } ... 省略 catch } ``` 可以主要的相關源碼中,我們可以看到並發現以下事情… 1. 先創建 `ContextImpl` 物件實例,再創建 `Activity` 實例 2. 之後透過 `ContextImpl`#`setOuterContext` 方法,來連接 Context 與 Activity 的關係(有就是在 Context 內放置 Activity 物件) ```mermaid graph LR subgraph Context 實例 a1(Activity) end ``` 3. **最後使用 `Activity`#`attach` 方法來連接 Application、Context 兩者之間的關係**,到了這裡我們就可以看到 Activity 是如何與 Context 產生關係的 ```mermaid graph LR Activity --> |產生關聯| Application Activity --> |產生關聯| Context ``` ## 更多的物件導向設計 物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)! :::info * [**設計建模 2 大概念- UML 分類、使用**](https://devtechascendancy.com/introduction-to-uml-and-diagrams/) * [**物件導向設計原則 – 6 大原則(一)**](https://devtechascendancy.com/object-oriented-design-principles_1/) * [**物件導向設計原則 – 6 大原則(二)**](https://devtechascendancy.com/object-oriented-design-principles_2/) ::: ### 創建模式 - Creation Patterns * [**創建模式 PK**](https://devtechascendancy.com/pk-design-patterns-factory-builder-best/) * **創建模式 - `Creation Patterns`**: 創建模式用於「**物件的創建**」,它關注於如何更靈活、更有效地創建對象。這些模式可以隱藏創建對象的細節,並提供創建對象的機制,例如單例模式、工廠模式… 等等,詳細解說請點擊以下連結 :::success * [**Singleton 單例模式 | 解說實現 | Android Framework Context Service**](https://devtechascendancy.com/object-oriented_design_singleton/) * [**Abstract Factory 設計模式 | 實現解說 | Android MediaPlayer**](https://devtechascendancy.com/object-oriented_design_abstract-factory/) * [**Factory 工廠方法模式 | 解說實現 | Java 集合設計**](https://devtechascendancy.com/object-oriented_design_factory_framework/) * [**Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗**](https://devtechascendancy.com/object-oriented_design_builder_dialog/) * [**Clone 原型模式 | 解說實現 | Android Framework Intent**](https://devtechascendancy.com/object-oriented_design_clone_framework/) * [**Object Pool 設計模式 | 實現與解說 | 利用 JVM**](https://devtechascendancy.com/object-oriented_design_object-pool/) * [**Flyweight 享元模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_flyweight/) ::: ### 行為模式 - Behavioral Patterns * [**行為模式 PK**](https://devtechascendancy.com/pk-design-patterns-cmd-strat-state-obs-chain/) * **行為模式 - `Behavioral Patterns`**: 行為模式關注物件之間的「**通信**」和「**職責分配**」。它們描述了一系列對象如何協作,以完成特定任務。這些模式專注於改進物件之間的通信,從而提高系統的靈活性。例如,策略模式、觀察者模式… 等等,詳細解說請點擊以下連結 :::warning * [**Stragety 策略模式 | 解說實現 | Android Framework 動畫**](https://devtechascendancy.com/object-oriented_design_stragety_framework/) * [**Interpreter 解譯器模式 | 解說實現 | Android Framework PackageManagerService**](https://devtechascendancy.com/object-oriented_design_interpreter_framework/) * [**Chain 責任鏈模式 | 解說實現 | Android Framework View 事件傳遞**](https://devtechascendancy.com/object-oriented_design_chain_framework/) * [**State 狀態模式 | 實現解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_state/) * [**Specification 規格模式 | 解說實現 | Query 語句實做**](https://devtechascendancy.com/object-oriented_design_specification-query/) * [**Command 命令、Servant 雇工模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_command_servant/) * [**Memo 備忘錄模式 | 實現與解說 | Android Framwrok Activity 保存**](https://devtechascendancy.com/object-oriented_design_memo_framework/) * [**Visitor 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_visitor_dispatch/) * [**Template 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_template/) * [**Mediator 模式設計 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_mediator/) * [**Composite 組合模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_composite/) ::: ### 結構模式 - Structural Patterns * [**結構模式 PK**](https://devtechascendancy.com/pk-design-patterns-proxy-decorate-adapter/) * **結構模式 - `Structural Patterns`**: 結構模式專注於「物件之間的組成」,以形成更大的結構。這些模式可以幫助你確保當系統進行擴展或修改時,不會破壞其整體結構。例如,外觀模式、代理模式… 等等,詳細解說請點擊以下連結 :::danger * [**Bridge 橋接模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_bridge/) * [**Decorate 裝飾模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_decorate/) * [**Proxy 代理模式 | 解說實現 | 分析動態代理**](https://devtechascendancy.com/object-oriented_design_proxy_dynamic-proxy/) * [**Iterator 迭代設計 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_iterator/) * [**Facade 外觀、門面模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_facade/) * [**Adapter 設計模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_adapter/) ::: ## Appendix & FAQ :::info ::: ###### tags: `Java 設計模式` `基礎進階`

Import from clipboard

Paste your webpage below. It will be converted to Markdown.

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 is not available.
Upgrade
All
  • All
  • Team
No template found.

Create custom 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

How to use Slide mode

API Docs

Edit in VSCode

Install browser extension

Get in Touch

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

No updates to save
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