設計模式篇(6) 裝飾器

来源:https://www.cnblogs.com/guozhiqiang/archive/2022/04/22/16179666.html
-Advertisement-
Play Games

###裝飾器模式 裝飾器模式(Decorator Pattern)允許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬於結構型模式,它是作為現有的類的一個包裝。 這種模式創建了一個裝飾類,用來包裝原有的類,併在保持類方法簽名完整性的前提下,提供了額外的功能。 我們通過下麵的實例 ...


裝飾器模式

裝飾器模式(Decorator Pattern)允許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬於結構型模式,它是作為現有的類的一個包裝。

這種模式創建了一個裝飾類,用來包裝原有的類,併在保持類方法簽名完整性的前提下,提供了額外的功能。

我們通過下麵的實例來演示裝飾器模式的用法。其中,我們將把一個形狀裝飾上不同的顏色,同時又不改變形狀類。

介紹

意圖:動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾器模式相比生成子類更為靈活。

主要解決:一般的,我們為了擴展一個類經常使用繼承方式實現,由於繼承為類引入靜態特征,並且隨著擴展功能的增多,子類會很膨脹。

何時使用:在不想增加很多子類的情況下擴展類。

如何解決:將具體功能職責劃分,同時繼承裝飾者模式。

關鍵代碼: 1、Component 類充當抽象角色,不應該具體實現。 2、修飾類引用和繼承 Component 類,具體擴展類重寫父類方法。

應用實例: 1、孫悟空有 72 變,當他變成"廟宇"後,他的根本還是一隻猴子,但是他又有了廟宇的功能。 2、不論一幅畫有沒有畫框都可以掛在牆上,但是通常都是有畫框的,並且實際上是畫框被掛在牆上。在掛在牆上之前,畫可以被蒙上玻璃,裝到框子里;這時畫、玻璃和畫框形成了一個物體。

優點:裝飾類和被裝飾類可以獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式可以動態擴展一個實現類的功能。

缺點:多層裝飾比較複雜。

使用場景: 1、擴展一個類的功能。 2、動態增加功能,動態撤銷。

註意事項:可代替繼承。

實現

1,繪製形狀,並給形狀加一個顏色裝飾器

abstract class Shape {
    abstract draw():void
}

class Circle extends Shape {
    draw(): void {
        console.log('繪製圓形')
    }
}

class Rectangle extends Shape {
    draw(): void {
        console.log('繪製矩形');
    }
}

abstract class ColorfulShape extends Shape {
    constructor(public shape: Shape) {
        super();
    }
    abstract draw():void
}

class RedColorfulShape extends ColorfulShape {
    draw() {
        this.shape.draw();
        console.log('把邊框塗成紅色');
    }
}

let redColorfulShape = new RedColorfulShape(new Circle());
redColorfulShape.draw();

2,類裝飾器

namespace a {
    interface Animal {
        swings: number;
        fly: Function
    }

    function flyable(swings:number) {
        return function(target: any) {
            console.log(target);
            target.prototype.swings = swings;
            target.prototype.fly = function () {
                console.log('我能飛');
            }
        }
    }


    @flyable(2)
    class Animal {
        constructor() { }
    }

    let animal: Animal = new Animal();
    console.log(animal.swings);
    animal.fly();
}

namespace b {
    interface Person {
        protoName: string
    }
    //實例屬性上的target就是類的原型對象 ,key是屬性的名字
    function instancePropertyDecorator(target:any,key:any) {
        target.protoName = '我是類的原型上的屬性';
        console.log('instancePropertyDecorator', target, key)
    }
    //實例方法上的target就是類的原型對象 ,key是屬性的名字
    function instanceMethodDecorator(target:any,key:any) {
        console.log('classPropertyDecorator', target, key);
    }

    //靜態屬性時候target指的是類的構造函數,key方法名 descriptor屬性描述符
    function classPropertyDecorator(target:any,key:any) {
        console.log('classPropertyDecorator',target,key);
    }
    //靜態方法時候target指的是類的構造函數,key方法名 descriptor屬性描述符
    function classMethodDecorator(target:any,key:any) {
        console.log('classMethodDecorator',target,key);
    }


    class Person {
        @instancePropertyDecorator
        instanceProperty: string;//實例屬性
        constructor() {
            this.instanceProperty = '';
        }
        @classPropertyDecorator
        static classProperty: string;//類的靜態屬性
        @instanceMethodDecorator
        instanceMethod() {//實例的方法

        }
        @classMethodDecorator
        static classMethod() {//類的靜態方法

        }
    }
}

3,利用裝飾器實現埋點

import React from 'react';
import ReactDom from 'react-dom';

function before(beforeFn) {
    return function (target:any, methodName: any, descriptor: any) {
        let oldMethod = descriptor.value;
        descriptor.value = function () {
            beforeFn.apply(this, arguments);
            return oldMethod.apply(this, arguments);
        }
    }
}

function after(afterFn) {
    return function (target, methodName, descriptor) {
        let oldMethod = descriptor.value;
        descriptor.value = function() {
            let result = oldMethod.apply(this,arguments);
            afterFn.apply(this, arguments);
            return result;
        }
    }
}
//埋點函數
class App extends React.component {
    @before(() => {console.log('before')})
    onClickBefore() {
        console.log('beforeClick');
    }
    @after(() => {console.log('after')})
    onClickAfter() {
        console.log('afterClick'); 
    }
    @after(() => fetch('/api/report'))
    ajaxClick() {
        console.log('ajaxClick');
    }
    render() {
        return {
            <div>
                <button onClick={this.onClickBefore}>beforeClick</button>
                <button onClick={this.onClickAfter}>afterClick</button>
                <button onClick={this.ajaxClick}>ajaxClick</button>
            </div>
        }
    }
}
  1. 裝飾器實現表單驗證
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
      用戶名:<input type="text" id="username" /> 密碼:<input
        type="password"
        id="password"
      />
      <button id="sub">提交</button>

    <script>
      Function.prototype.before = function (beforeFn) {
        let thisFn = this;
        return function () {
          let pass = beforeFn();
          if (pass) {
            thisFn.apply(this, arguments);
          }
        };
      };
      function registerFn(event) {
        console.log("提交表單");
      }
      registerFn = registerFn.before(function () {
        let username = document.getElementById("username").value;
        console.log(username);
        if (!username) {
          return alert("用戶名不能為空");
        }
        return true;
      });
      registerFn = registerFn.before(function () {
        let passowrd = document.getElementById("password");
        console.log(password.value)
        if (!password.value) {
          return alert("密碼不能為空");
        }
        return true;
      });
      let btn = document.getElementById("sub");
      btn.addEventListener("click", registerFn);
    </script>
  </body>
</html>

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • HMS Core Discovery第14期直播《縱享絲滑剪輯,釋放視頻創作力》,已於4月21日圓滿結束,本期直播我們同HMS Core視頻編輯服務(Video Editor Kit)的產品經理和高級專家一起探討了視頻編輯服務的應用場景和圖像處理演算法、AI處理能力、高清低碼等技術細節,下麵我們一起來 ...
  • 1. 前言 站長以前介紹過這個開源項目,最近又有人在問,索性掛在Dotnet9網站上,方便大家線上瀏覽,先聲明,模板來自下麵的倉庫: 倉庫名: 大屏數據展示模板 作者:lvyeyou 開源協議:MIT 倉庫地址:https://gitee.com/lvyeyou/DaShuJuZhiDaPingZh ...
  • 一 、通過雲開發平臺快速創建初始化應用 1.創建相關應用模版請參考鏈接:徹底學會快速部署vue框架,一篇就夠了 2.完成創建後就可以在github中查看到新增的Vue.js 倉庫 二 、 本地編寫《開發跨平臺桌面應用》項目 1.將應用模版克隆到本地 ● 首先假定你已經安裝了Git、node,沒有安裝 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 1. new操作符的實現原理 new操作符的執行過程: (1)首先創建了一個新的空對象 (2)設置原型,將對象的原型設置為函數的 prototype 對象。 (3)讓函數的 this 指向這個對象,執行構造函數的代碼(為這個新對象添加屬性 ...
  • 參考資料 https://www.zhangxinxu.com/wordpress/2017/12/understand-svg-fedisplacementmap-filter/ 該文章已經講的特別細緻了,該篇僅以此記錄動效過程中各點的計算。 feDisplacementMap feDisplac ...
  • 一.同源策略問題(本地調試解決方案) firefox瀏覽器 地址欄輸入:about:config 搜索security.fileuri.strict_origin_policy(這個是安全文件同源策略限制),設置為false 重啟瀏覽器 chome瀏覽器 C盤下創建一個文件夾,名稱隨意(chrome ...
  • 大家好,我是半夏👴,一個剛剛開始寫文的沙雕程式員.如果喜歡我的文章,可以關註➕ 點贊 👍 加我微信:frontendpicker,一起學習交流前端,成為更優秀的工程師~關註公眾號:搞前端的半夏,瞭解更多前端知識! 點我探索新世界! 原文鏈接 ==>http://sylblog.xin/archi ...
  • 之前的文章已經介紹瞭如何實現Web端的即時通訊IM,為了讓大家全面的體驗通信互動的快樂。 本文介紹如何使用 ZIM SDK 快速實現實現小程式端的基本的消息收發功能,在微信中實現一個mini版微信,也就是常見的聊天功能。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...