摘要:裝飾模式描述裝飾模式裝飾模式是在不必改變原類文件和使用繼承的情況下,動態(tài)地擴展一個對象的功能。具體裝飾角色負責給構件對象添加上附加的責任。
裝飾模式 描述
裝飾模式:裝飾模式是在不必改變原類文件和使用繼承的情況下,動態(tài)地擴展一個對象的功能。它是通過創(chuàng)建一個包裝對象,也就是裝飾來包裹真實的對象。適用性-百科
以下情況使用Decorator模式:
需要擴展一個類的功能,或給一個類添加附加職責。
需要動態(tài)的給一個對象添加功能,這些功能可以再動態(tài)的撤銷。
需要增加由一些基本功能的排列組合而產(chǎn)生的非常大量的功能,從而使繼承關系變的不現(xiàn)實。
當不能采用生成子類的方法進行擴充時。一種情況是,可能有大量獨立的擴展,為支持每一種組合將產(chǎn)生大量的子類,使得子類數(shù)目呈爆炸性增長。另一種情況可能是因為類定義被隱藏,或類定義不能用于生成子類。
代碼示例在裝飾模式中的各個角色有:
抽象構件(Component)角色:給出一個抽象接口,以規(guī)范準備接收附加責任的對象。
具體構件(Concrete Component)角色:定義一個將要接收附加責任的類。
裝飾(Decorator)角色:持有一個構件(Component)對象的實例,并實現(xiàn)一個與抽象構件接口一致的接口。
具體裝飾(Concrete Decorator)角色:負責給構件對象添加上附加的責任。
// Component類 定義一個對象接口,可以給這些對象動態(tài)的添加職責 abstract class Component { abstract Operation (): void } // ConcreteComponent 類定義一個具體的對象,也可以給這個對象添加職責 class ConcreteComponent extends Component { Operation () { console.log("具體的對象操作"); } } // Decorator 裝飾抽象類,繼承了Component 從外類來拓展Component的功能,但是對于Component來說,無需知道Decorator的存在 abstract class Decorator extends Component{ protected component: Component | null = null // 裝載Component SetComponent (component: Component) { this.component = component } // 重寫Operation,實際執(zhí)行的是component的Operation Operation () { if (this.component !== null) { this.component.Operation() } } } // ConcreteDecorator 具體的裝飾對象 起到給Component類添加職責 class ConcreteDecoratorA extends Decorator { private addState: string = "" Operation () { // 先運行裝飾對象的Operation,如果有的話 super.Operation() this.addState = "new stateA" console.log("具體裝飾對象A的操作"); } } class ConcreteDecoratorB extends Decorator { Operation () { super.Operation() this.AddedBehavior() console.log("具體裝飾對象b的操作"); } AddedBehavior () { console.log("new state B"); } } // 調(diào)用 const c = new ConcreteComponent() const d1 = new ConcreteDecoratorA() const d2 = new ConcreteDecoratorB() d1.SetComponent(c) // d1裝飾的是c d2.SetComponent(d1) // d2裝飾的是d1 d2.Operation() // d2.Operation中先會調(diào)用d1的Operation,d1.Operation中先會調(diào)用c的Operationjs的裝飾器
js有自帶的裝飾器,可以用來修飾類和方法例子 - 換衣服系統(tǒng)
實現(xiàn)一個換衣服系統(tǒng),一個人可以穿各種服飾,以一定順序輸出穿捉的服裝版本0
class Person0 { private name: string; constructor (name: string) { this.name = name } wearTShirts () { console.log(" T-shirts") } wearBigTrouser () { console.log(" big trouser") } wearSneakers () { console.log("sneakers ") } wearSuit () { console.log("suit") } wearLeatherShoes () { console.log("LeatherShoes") } show () { console.log(this.name) } } const person0 = new Person0("lujs") person0.wearBigTrouser() person0.wearTShirts() person0.wearSneakers() person0.wearSuit() person0.show()版本1
上面的版本0,每次要添加不同的服飾,就需要修改person類,不符合開放-封閉原則,下面會抽離出服飾類,每個服飾子類都有添加服飾的方法,這樣就解耦了person和finery
class Person1 { private name: string; constructor (name: string) { this.name = name } show () { console.log(this.name) } } abstract class Finery { abstract show (): void } class Tshirts extends Finery { show () { console.log(" T-shirts") } } class BigTrouser extends Finery { show () { console.log(" BigTrouser") } } class Sneakers extends Finery { show () { console.log(" Sneakers") } } // 調(diào)用 const person1 = new Person1("lujs") const ts = new Tshirts() const bt = new BigTrouser() const sneakers = new Sneakers() person1.show() ts.show() bt.show() sneakers.show()版本2
上面的版本1,多帶帶抽離了服飾類,這樣就可以隨意添加服飾而不會影響到person了,
但是在上面的調(diào)用代碼中需要按順序去調(diào)用自定服飾的show,最好就是只調(diào)用一次show就顯示出正確的穿衣服順序;需要把功能按正確的順序進行控制,下面用裝飾模式來實現(xiàn)實現(xiàn)
class Person2 { private name: string = "" setName (name: string) { this.name = name } show () { console.log("裝扮", this.name); } } class Finery2 extends Person2 { private component: Person2 | null = null Decorator (component: Person2) { this.component = component } show () { if (this.component !== null) { this.component.show() } } } class Tshirts2 extends Finery2 { show () { super.show() console.log("穿tshirt"); } } class BigTrouser2 extends Finery2 { show () { super.show() console.log("穿BigTrouser"); } } const p2 = new Person2() const t1 = new Tshirts2() const b1 = new BigTrouser2() p2.setName("p2") t1.Decorator(p2) b1.Decorator(t1) b1.show()
當系統(tǒng)需要新功能的時候,是向舊的類中添加新的代碼。這些新加的代碼通常裝飾了原有類的核心職責或主要行為,版本3
比如用服飾裝飾人,但這種做法的問題在于,它們在主類中加入了新的字段,新的方法和新的邏輯,從而增加了主類的復雜度
而這些新加入的東西僅僅是為了滿足一些只在某種特定情況下才會執(zhí)行的特殊行為的需要。
而裝飾模式卻提供了一個非常好的解決方案,它把每個要裝飾的功能放在多帶帶的類中,
并讓這個類包裝它所要裝飾的對象,因此,當需要執(zhí)行特殊行為時,客戶代碼就可以在運行時根據(jù)需要有選擇地、按順序地使用裝飾功能包裝對象了
接下來我們使用js自帶的裝飾器來實現(xiàn)
class Person4 { private name: string = "" SetName (name: string) { this.name = name } show () { console.log("裝備開始", this.name) } } // 裝飾函數(shù) type fn = () => void function beforeShow(fns: fn[]) { return (target:any, name:any, descriptor:any) => { const oldValue = descriptor.value descriptor.value = function () { const value = oldValue.apply(this, arguments); fns.forEach(f:fn => { f() }) return value } } } // 使用函數(shù)來代替服飾子類 const wearTShirts = () => { console.log("wear Tshirts"); } const wearBigTrouser = () => { console.log("wear BigTrouser"); } class Finery4 extends Person4 { private person: Person4 | null = null addPerson (person: Person4) { this.person = person } @beforeShow([wearBigTrouser, wearTShirts]) show () { if (this.person !== null) { this.person.show() } } } // 需要修改服飾順序的時候,可以直接修改服飾類的裝飾函數(shù)順序,或者生成另一個類 class Finery5 extends Person4 { private person: Person4 | null = null addPerson (person: Person4) { this.person = person } @beforeShow([wearTShirts, wearBigTrouser]) show () { if (this.person !== null) { this.person.show() } } } const p6 = new Person4() const f6 = new Finery4() p6.SetName("lll") f6.addPerson(p6) f6.show() console.log("換一種服飾"); const p7 = new Person4() const f7 = new Finery4() p7.SetName("lll") f7.addPerson(p7) f7.show()表單例子 版本0
一般我們寫表單提交都會想下面那樣先驗證,后提交,
但是submit函數(shù)承擔了兩個責任,驗證和提交。
我們可以通過裝飾器把驗證的方法剝離出來
const ajax = (url:string, data: any) => {console.log("ajax", url, data)} class Form { state = { username: "lujs", password: "lujs" } validata = ():boolean => { if (this.state.username === "") { return false } if (this.state.password === "") { return false } return true } submit = () => { if (!this.validata()) { return } ajax("url", this.state) } }版本1
先把驗證函數(shù)多帶帶寫成插件
現(xiàn)在submit函數(shù)只有提交數(shù)據(jù)這個功能,而驗證功能寫成了裝飾器
interface RequestData { username: string password: string } type Vality = (data: RequestData) => boolean type ValityFail = (data: RequestData) => void const validata = (data: RequestData):boolean => { if (data.username === "") { return false } if (data.password === "") { return false } console.log("驗證通過") return true } function verify(vality:Vality, valityFail: ValityFail) { return (target:any, name:string, descriptor:any) => { const oldValue = descriptor.value descriptor.value = function (requestData: RequestData) { // 驗證處理 if (!vality(requestData)) { // 驗證失敗處理 valityFail(requestData) return } // console.log(this, " == this") return oldValue.apply(this, arguments) } return descriptor } } class Form1 { state = { username: "", password: "password" } @verify(validata, () => console.log("驗證失敗")) submit(requestData: RequestData) { ajax("url", requestData) } } console.log("表單驗證例子1開始---") const f1 = new Form1 f1.submit(f1.state) f1.state.username = "lujs" f1.submit(f1.state) console.log("表單驗證例子1結束---")
#### 版本2
把驗證器寫成多帶帶的插件
/** * 一個使用裝飾功能的表單驗證插件 */ // 先定義一下希望插件的調(diào)用方式, 輸入一個數(shù)組,內(nèi)容可以是字符串或者對象 state = { username: "lujs", myEmail: "123@qq.com", custom: "custom" } @validate([ "username", // fail: () =>console.log("username wrong") { key: "myEmail", method: "email" }, { key: "myEmail", method: (val) => val === "custom", fail: () => alert("fail") } ]) submit(requestData: RequestData) { ajax("url", requestData) }
./validator.ts
export interface Validator { notEmpty (val: string):boolean notEmail (val: string):boolean } export const validator: Validator = { notEmpty: (val: string) => val !== "", notEmail: (val: string) => !/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/.test(val) } export interface V { key: string method: keyof Validator | ((val: any) => boolean) fail?: (val: any) => void } export function verify( vality: Array, ) { return (target:any, propertyKey:string, descriptor:PropertyDescriptor) => { const oldValue = descriptor.value descriptor.value = function (requestData: {[p: string]: any}) { // 驗證處理 const flag = vality.every((v) => { console.log(typeof v, v, " == this") if (typeof v === "string") { const val = requestData[v] // 默認進行empty判斷 if (!validator.notEmpty(val)) { return false } } else { // 對象的情況 const val = requestData[v.key] console.log(val, " => val") console.log(v, " => v") if (typeof v.method === "string") { if (!validator[v.method](val)) { if (v.fail) { v.fail.apply(this, requestData) } return false } } else { console.log(v.method(val), val) if (!v.method(val)) { if (v.fail) { v.fail.apply(this, requestData) } return false } } } return true }) if (!flag) { return } return oldValue.apply(this, arguments) } return descriptor } }
./form
import {verify} from "./validator" const ajax = (url:string, data: any) => {console.log("ajax", url, data)} class Form2 { state = { username: "lujs", myEmail: "123@qq.com", custom: "custom" } @verify([ "username", // fail: () =>console.log("username wrong") { key: "myEmail", method: "notEmail" }, { key: "myEmail", method: (val) => val !== "custom", fail: (val) => console.log(val) } ]) submit(requestData: {[p: string]: any}) { ajax("url", requestData) } } const f2 = new Form2 f2.submit(f2.state)
例子來自《大話設計模式》《javascript設計模式與開發(fā)實踐》
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/98372.html
摘要:裝飾器我們?yōu)樯兑懻撛刈⑷肫鞫皇茄b飾器這是因為會把元素注入器依賴解析過程限制在當前組件視圖內(nèi)。但是一旦使用了裝飾器,整個依賴解析過程就會在第一階段完成后停止解析,也就是說,元素注入器只在組件視圖內(nèi)解析依賴,然后就停止解析工作。 原文鏈接:A curious case of the @Host decorator and Element Injectors in Angular 我...
摘要:什么是裝飾器模式向一個現(xiàn)有的對象添加新的功能,同時又不改變其結構的設計模式被稱為裝飾器模式,它是作為現(xiàn)有的類的一個包裝。中的裝飾器模式中有一個的提案,使用一個以開頭的函數(shù)對中的及其屬性方法進行修飾。 1 什么是裝飾器模式 showImg(https://segmentfault.com/img/remote/1460000015970102?w=1127&h=563); 向一個現(xiàn)有的對...
摘要:但是,這樣做的后果就是,我們會不斷的改變本體,就像把鳳姐送去做整形手術一樣。在中,我們叫做引用裝飾。所以,這里引入的裝飾模式裝飾親切,熟悉,完美。實例講解裝飾上面那個例子,只能算是裝飾模式的一個不起眼的角落。 裝飾者,英文名叫decorator. 所謂的裝飾,從字面可以很容易的理解出,就是給 土肥圓,化個妝,華麗的轉(zhuǎn)身為白富美,但本體還是土肥圓。 說人話.咳咳~ 在js里面一切都是對...
摘要:用戶名不能為空密碼不能為空校驗未通過使用優(yōu)化代碼返回的情況直接,不再執(zhí)行后面的原函數(shù)用戶名不能為空密碼不能為空 本文是《JavaScript設計模式與開發(fā)實踐》的學習筆記,例子來源于書中,對于設計模式的看法,推薦看看本書作者的建議。 什么是裝飾者模式? 給對象動態(tài)增加職責的方式成為裝飾者模式。 裝飾者模式能夠在不改變對象自身的基礎上,在運行程序期間給對象動態(tài)地添加職責。這是一種輕便靈活...
閱讀 2679·2023-04-25 20:28
閱讀 1849·2021-11-22 09:34
閱讀 3687·2021-09-26 10:20
閱讀 1834·2021-09-22 16:05
閱讀 3085·2021-09-09 09:32
閱讀 2502·2021-08-31 09:40
閱讀 2101·2019-08-30 13:56
閱讀 3320·2019-08-29 17:01