###裝飾器模式 裝飾器模式(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>
}
}
}
- 裝飾器實現表單驗證
<!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>