做者:Fernando Doglio
譯者:前端小智
來源:medium
點贊再看,養成習慣本文
GitHub
https://github.com/qq44924588... 上已經收錄,更多往期高贊文章的分類,也整理了不少個人文檔,和教程資料。歡迎Star和完善,你們面試能夠參照考點複習,但願咱們一塊兒有點東西。前端
最近開源了一個 Vue 組件,還不夠完善,歡迎你們來一塊兒完善它,也但願你們能給個 star 支持一下,謝謝各位了。vue
github 地址:https://github.com/qq44924588...git
設計模式是能夠幫助開發人員解決問題的模板。在本中涉及的模式太多了,並且它們每每針對不一樣的需求。可是,它們能夠被分爲三個不一樣的組:github
Composite
、Adapter
和Decorator
。觀察者模式
。單例模式多是最著名的設計模式之一。它是一種建立模式,由於它確保不管咱們嘗試實例化一個類多少次,咱們都只有一個可用的實例。面試
處理數據庫鏈接之類的能夠單例模式,由於咱們但願一次只處理一個,而沒必要在每一個用戶請求時從新鏈接。typescript
class MyDBConn { protected static instance: MyDBConn | null = null private id:number = 0 constructor() { this.id = Math.random() } public getID():number { return this.id } public static getInstance():MyDBConn { if (!MyDBConn.instance) { MyDBConn.instance = new MyDBConn() } return MyDBConn.instance } } const connections = [ MyDBConn.getInstance(), MyDBConn.getInstance(), MyDBConn.getInstance(), MyDBConn.getInstance(), MyDBConn.getInstance() ] connections.forEach( c => { console.log(c.getID()) })
如今,雖然不能直接實例化類,可是使用getInstance
方法,能夠確保不會有多個實例。在上面的示例中,能夠看到包裝數據庫鏈接的僞類如何從該模式中獲益。數據庫
這個事例展現了不管咱們調用getInstance
方法多少次,這個鏈接老是相同的。設計模式
上面的運行結果:微信
0.4047087250990713 0.4047087250990713 0.4047087250990713 0.4047087250990713 0.4047087250990713
工廠模式
是一種建立模式,就像單例模式
同樣。可是,這個模式並不直接在咱們關心的對象上工做,而是隻負責管理它的建立。dom
解釋一下:假設咱們經過編寫代碼來模擬移動車輛,車有不少類型,例如汽車、自行車和飛機,移動代碼應該封裝在每一個vehicle
類中,可是調用它們的move
方法的代碼能夠是通用的。
這裏的問題是如何處理對象建立?能夠有一個具備3個方法的單一creator
類,或者一個接收參數的方法。在任何一種狀況下,擴展該邏輯以支持建立更多vehices
都須要不斷增加相同的類。
可是,若是決定使用工廠方法模式,則能夠執行如下操做:
如今,建立新對象所需的代碼被封裝到一個新類中,每一個類對應一個車輛類型。這確保了若是未來須要添加車輛,只須要添加一個新類,而不須要修改任何已經存在的東西。
接着來看看,咱們如何使用TypeScript
來實現這一點:
interface Vehicle { move(): void } class Car implements Vehicle { public move(): void { console.log("Moving the car!") } } class Bicycle implements Vehicle { public move(): void { console.log("Moving the bicycle!") } } class Plane implements Vehicle { public move(): void { console.log("Flying the plane!") } } // VehicleHandler 是「抽象的」,由於沒有人會實例化它instantiate it // 咱們要擴展它並實現抽象方法 abstract class VehicleHandler { // 這是真正的處理程序須要實現的方法 public abstract createVehicle(): Vehicle public moveVehicle(): void { const myVehicle = this.createVehicle() myVehicle.move() } } class PlaneHandler extends VehicleHandler{ public createVehicle(): Vehicle { return new Plane() } } class CarHandler extends VehicleHandler{ public createVehicle(): Vehicle { return new Car() } } class BicycleHandler extends VehicleHandler{ public createVehicle(): Vehicle { return new Bicycle() } } /// User code... const planes = new PlaneHandler() const cars = new CarHandler() planes.moveVehicle() cars.moveVehicle()
上面的代碼不少,但咱們可使用上面的圖表來理解它。本質上最後,咱們關心的是自定義處理程序,這裏稱它爲處理程序,而不是創造者,由於他們不僅是建立的對象,他們也有邏輯,使用它們(moveVehicle方法)。
這個模式的美妙之處在於,若是您你要添加一個新的vehicle
類型,所要作的就是添加它的vehicle
類和它的處理程序類,而不增長任何其餘類的LOC。
在全部的模式,我最喜歡的是觀察者模式
,由於類型的行爲咱們能夠實現它。
它是如何工做的呢?本質上,該模式代表你擁有一組觀察者對象,這些對象將對被觀察實體狀態的變化作出反應。爲了實現這一點,一旦在被觀察端接收到一個更改,它就負責經過調用它的一個方法來通知它的觀察者。
在實踐中,此模式的實現相對簡單,讓咱們快速查看一下代碼,而後回顧一下
type InternalState = { event: String } abstract class Observer { abstract update(state:InternalState): void } abstract class Observable { protected observers: Observer[] = [] protected state:InternalState = { event: ""} public addObserver(o: Observer):void { this.observers.push(o) } protected notify () { this.observers.forEach(o => o.update(this.state)) } } class ConsoleLogger extends Observer { public update(newState: InternalState) { console.log("New internal state update: ", newState) } } class InputElement extends Observable { public click():void { this.state = { event: "click" } this.notify() } } const input = new InputElement() input.addObserver(new ConsoleLogger()) input.click()
正如你所看到的,經過兩個抽象類,咱們能夠定義Observer
,該觀察者將表示對Observable
實體上的更改作出反應的對象。 在上面的示例中,咱們假設具備一個被單擊的InputElement
實體(相似於在前端具備HTML輸入字段的方式),以及一個ConsoleLogger
,用於記錄控制檯發生的全部事情。
這種模式的優勢在於,它使咱們可以瞭解Observable
的內部狀態並對其作出反應,而沒必要弄亂其內部代碼。 咱們能夠繼續添加執行其餘操做的觀察者,甚至包括對特定事件作出反應的觀察者,而後讓它們的代碼決定對每一個通知執行的操做。
裝飾模式試圖在運行時向現有對象添加行爲。 從某種意義上說,咱們能夠將其視爲動態繼承,由於即便沒有建立新類來添加行爲,咱們也正在建立具備擴展功能的新對象。
這樣考慮:假設咱們擁有一個帶有move
方法的Dog
類,如今您想擴展其行爲,由於咱們想要一隻超級狗和一隻能夠游泳的狗。
一般,咱們須要在 Dog 類中添加move
行爲,而後以兩種方式擴展該類,即SuperDog
和SwimmingDog
類。 可是,若是咱們想將二者混合在一塊兒,則必須再次建立一個新類來擴展它們的行爲,可是,有更好的方法。
組合讓咱們能夠將自定義行爲封裝在不一樣的類中,而後使用該模式經過將原始對象傳遞給它們的構造函數來建立這些類的新實例。 讓咱們看一下代碼:
abstract class Animal { abstract move(): void } abstract class SuperDecorator extends Animal { protected comp: Animal constructor(decoratedAnimal: Animal) { super() this.comp = decoratedAnimal } abstract move(): void } class Dog extends Animal { public move():void { console.log("Moving the dog...") } } class SuperAnimal extends SuperDecorator { public move():void { console.log("Starts flying...") this.comp.move() console.log("Landing...") } } class SwimmingAnimal extends SuperDecorator { public move():void { console.log("Jumps into the water...") this.comp.move() } } const dog = new Dog() console.log("--- Non-decorated attempt: ") dog.move() console.log("--- Flying decorator --- ") const superDog = new SuperAnimal(dog) superDog.move() console.log("--- Now let's go swimming --- ") const swimmingDog = new SwimmingAnimal(dog) swimmingDog.move()
注意幾個細節:
SuperDecorator
類擴展了Animal
類,與Dog
類擴展了相同的類。 這是由於裝飾器須要提供與其嘗試裝飾的類相同的公共接口。SuperDecorator
類是abstract
,這意味着並無使用它,只是使用它來定義構造函數,該構造函數會將原始對象的副本保留在受保護的屬性中。 公共接口的覆蓋是在自定義裝飾器內部完成的。SuperAnimal
和SwimmingAnimal
是實際的裝飾器,它們是添加額外行爲的裝飾器。進行此設置的好處是,因爲全部裝飾器也間接擴展了Animal
類,所以若是你要將兩種行爲混合在一塊兒,則能夠執行如下操做:
const superSwimmingDog = new SwimmingAnimal(superDog) superSwimmingDog.move()
關於Composite模式,其實就是組合模式,又叫部分總體模式,這個模式在咱們的生活中也常用。
好比編寫過前端的頁面,確定使用過<div>
等標籤訂義一些格式,而後格式之間互相組合,經過一種遞歸的方式組織成相應的結構,這種方式其實就是組合,將部分的組件鑲嵌到總體之中。
關於此模式的有趣之處在於,它不是一個簡單的對象組,它能夠包含實體或實體組,每一個組能夠同時包含更多組,這就是咱們所說的樹。
看一個例子:
interface IProduct { getName(): string getPrice(): number } class Product implements IProduct { private price:number private name:string constructor(name:string, price:number) { this.name = name this.price = price } public getPrice():number { return this.price } public getName(): string { return this.name } } class Box implements IProduct { private products: IProduct[] = [] contructor() { this.products = [] } public getName(): string { return "A box with " + this.products.length + " products" } add(p: IProduct):void { console.log("Adding a ", p.getName(), "to the box") this.products.push(p) } getPrice(): number { return this.products.reduce( (curr: number, b: IProduct) => (curr + b.getPrice()), 0) } } //Using the code... const box1 = new Box() box1.add(new Product("Bubble gum", 0.5)) box1.add(new Product("Samsung Note 20", 1005)) const box2 = new Box() box2.add( new Product("Samsung TV 20in", 300)) box2.add( new Product("Samsung TV 50in", 800)) box1.add(box2) console.log("Total price: ", box1.getPrice())
在上面的示例中,咱們能夠將product
放入Box
中,也能夠將Box
放入其餘Box
中,這是組合的經典示例。由於咱們要實現的是得到完整的交付價格,所以須要在大box
裏添加每一個元素的價格(包括每一個小box
的價格)。
上面運行的結果:
Adding a Bubble gum to the box Adding a Samsung Note 20 to the box Adding a Samsung TV 20in to the box Adding a Samsung TV 50in to the box Adding a A box with 2 products to the box Total price: 2105.5
所以,在處理遵循同一接口的多個對象時,請考慮使用此模式。 經過將複雜性隱藏在單個實體(組合自己)中,您會發現它有助於簡化與小組的互動方式。
今天的分享就到這裏了,感謝你們的觀看,咱們下期再見。
原文:https://blog.bitsrc.io/design...
代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug。
文章每週持續更新,能夠微信搜索「 大遷世界 」第一時間閱讀和催更(比博客早一到兩篇喲),本文 GitHub https://github.com/qq449245884/xiaozhi 已經收錄,整理了不少個人文檔,歡迎Star和完善,你們面試能夠參照考點複習,另外關注公衆號,後臺回覆福利,便可看到福利,你懂的。