模型圖
用例圖
- 用例建模最主要的功能是表達(dá)系統(tǒng)的功能性需求或行為
- 參互者: 參與者指的是與系統(tǒng)交互的角色咏删,可以是人,也可以是事物或其他系統(tǒng)
- 用例是系統(tǒng)為參與者提供的功能募逞,用戶名稱一般是一個(gè)帶動(dòng)作性的詞語
用例描述
項(xiàng)目 | 內(nèi)容 |
---|---|
用例名稱 | 登錄 |
用例ID | login |
角色 | 用戶 |
用例說明 | 描述用戶的登錄過程 |
前置條件 | 打開網(wǎng)站頁面 |
基本事件流 | 1. 點(diǎn)擊登錄 2.輸入用戶名和密碼 3. 點(diǎn)擊登錄 4.服務(wù)器會(huì)使用會(huì)話登錄保存用戶登錄狀態(tài) |
其它事件流 | 1. 用戶名為空提示用戶名不能為空 2. 密碼為空提示密碼不能為空 |
異常事件流 | 登錄超時(shí)則返回登錄頁 |
后置條件 | 登錄成功弓摘,進(jìn)入個(gè)人中心 |
類圖
- 類圖描述類的靜態(tài)結(jié)構(gòu),定義類以及描述類之間的聯(lián)系腕够,如關(guān)聯(lián),依賴舌劳,聚合等帚湘,還包括類的內(nèi)部結(jié)構(gòu)(類的屬性和操作)
- 類圖是一種靜態(tài)模型類型,一個(gè)類圖根據(jù)結(jié)構(gòu)系統(tǒng)中的類以及各個(gè)類之間的關(guān)系描述系統(tǒng)的靜態(tài)結(jié)構(gòu)
- 類圖包含7個(gè)元素
- 類
- 接口
- 協(xié)作
- 泛化關(guān)系
- 依賴關(guān)系
- 關(guān)聯(lián)關(guān)系
- 實(shí)現(xiàn)關(guān)系
- 在UML中甚淡,類用矩形來表示大诸,分為3個(gè)部分,名稱部分贯卦,屬性部分和操作部分
依賴關(guān)系
只要在類中使用了對(duì)方资柔,那么它們之間就存在依賴關(guān)系,如果沒有對(duì)方撵割,連編譯都通過不了
泛化關(guān)系
泛化關(guān)系實(shí)際上就是繼承關(guān)系贿堰,它就是依賴關(guān)系的特例
實(shí)現(xiàn)關(guān)系
實(shí)現(xiàn)關(guān)系實(shí)際上就是A類實(shí)現(xiàn)B類(接口),他就是依賴關(guān)系的特例
關(guān)聯(lián)關(guān)系
- 關(guān)聯(lián)關(guān)系實(shí)際上就是類與類之間的聯(lián)系啡彬,它是依賴關(guān)系的特例
- 關(guān)聯(lián)關(guān)系比依賴的關(guān)系更強(qiáng)
- 關(guān)聯(lián)具有導(dǎo)航性羹与。即雙向關(guān)系或單向關(guān)系故硅,表示關(guān)系在哪一方維護(hù)
- 關(guān)聯(lián)具有多重性,如
- 1 表示有且僅有一個(gè)
- 0... 表示0或者多個(gè)
- 0,1 表示0或者1個(gè)
- n,m 表示n到m個(gè)都可以
- m... 表示至少m個(gè)
聚合關(guān)系
- 聚合關(guān)系表示的是整體和部分的關(guān)系纵搁,整體與部分可以分開
- 聚合關(guān)系是關(guān)聯(lián)關(guān)系的特例吃衅,所以它具有關(guān)聯(lián)的導(dǎo)向性和多重性
-
聚合的雙方聲明周期是獨(dú)立的
組合關(guān)系
- 也是整體和部分的關(guān)系,但是整體和部分不可分開
- 整體和部分生命周期一致
對(duì)象圖(不常用)
- 對(duì)象圖描述一組對(duì)象和它們之間的關(guān)系诡渴,它是系統(tǒng)狀態(tài)的某一時(shí)刻的快照捐晶,它的使用相當(dāng)有限,它主要用于了解系統(tǒng)在某個(gè)特定時(shí)刻的具體情況和數(shù)據(jù)結(jié)構(gòu)
-
對(duì)象圖表示方法和類圖大致相同妄辩,對(duì)象圖中的對(duì)象屬性可以有具體值惑灵,類圖中的一個(gè)類可以對(duì)應(yīng)成對(duì)象圖中的多個(gè)對(duì)象,例如:部門類的自關(guān)聯(lián)就可以對(duì)應(yīng)成多個(gè)部門之間的關(guān)聯(lián)
活動(dòng)圖
- 在UML里眼耀,活動(dòng)圖本質(zhì)上就是流程圖
- 它描述系統(tǒng)的活動(dòng)英支,判斷系統(tǒng)的活動(dòng),判斷點(diǎn)和分支等
四角圓滑的矩形---終端框(起止框)
矩形----處理框(執(zhí)行框)
普通的平行四邊形---輸入\輸出框
菱形---判斷框
時(shí)序圖
- 時(shí)序圖強(qiáng)調(diào)消息時(shí)間順序的交互圖
- 時(shí)序圖描述類系統(tǒng)中類與類之間的交互哮伟,它將這些交互建模換成消息交換
- 時(shí)序圖用于描述對(duì)象之間如何隨著時(shí)間進(jìn)行協(xié)作
- 時(shí)序圖由活動(dòng)者干花、對(duì)象、消息楞黄、生命線和控制焦點(diǎn)組成
- 不同元素由不同表示
- 對(duì)象是一個(gè)矩形池凄,對(duì)象名稱下由下劃線
- 消息用有方向的箭頭表示,調(diào)用是實(shí)線鬼廓,返回消息是虛線
- 生命線由縱向的虛線表示
-
控制焦點(diǎn)是縱向的矩形肿仑,也就是活動(dòng)條
協(xié)作圖
時(shí)序圖可一鍵轉(zhuǎn)換成協(xié)作圖
- 協(xié)作圖是時(shí)序圖的一種變種
- 協(xié)作圖強(qiáng)調(diào)的是發(fā)送和接受消息的對(duì)象之間的組織結(jié)構(gòu)
- 協(xié)作圖顯示了一系列對(duì)象和在這些對(duì)象之前的聯(lián)系以及對(duì)象間發(fā)送和接受的消息
-
時(shí)序圖主要側(cè)重于對(duì)象間消息傳遞在時(shí)間上的先后關(guān)系,而協(xié)作圖則側(cè)重與對(duì)象以及對(duì)象和角色交互的靜態(tài)關(guān)系
組件圖
- 組件圖用例建立系統(tǒng)的各個(gè)組件之間的關(guān)系碎税,它們是通過軟件或者文檔組織在一起尤慰,使用組件圖可以幫助讀者了解某個(gè)功能位于軟件包中的哪一個(gè)位置,以及各個(gè)版本組件包含哪些功能
- 組件圖可以用來幫助設(shè)計(jì)系統(tǒng)的整體架構(gòu)
部署圖
- 部署圖是來幫助讀者了解軟件中的各個(gè)組件運(yùn)行硬件什么位置雷蹂,以及這些硬件之間的交互關(guān)系
- 節(jié)點(diǎn):用來表示一種硬件伟端,它可以是服務(wù)器,計(jì)算機(jī)等匪煌。節(jié)點(diǎn)的符號(hào)是一個(gè)三位盒子责蝠,在左上角包含節(jié)點(diǎn)的名稱
- 通信關(guān)聯(lián):節(jié)點(diǎn)通過通信關(guān)聯(lián)建立彼此的關(guān)系,采用從節(jié)點(diǎn)到節(jié)點(diǎn)繪制實(shí)現(xiàn)來表示關(guān)聯(lián)
設(shè)計(jì)原則
SOLID五大設(shè)計(jì)原則
首字母 | 指代 | 概念 |
---|---|---|
S | 單一職責(zé)原則 | 單一功能原則認(rèn)為對(duì)象應(yīng)該僅具有一種單一功能的概念 |
O | 開放封閉原則 | 開閉原則認(rèn)為 軟件應(yīng)該是對(duì)于擴(kuò)展開放的虐杯,但是對(duì)于修改關(guān)閉的概念 |
L | 里氏替換原則 | 里氏替換原則認(rèn)為程序中的對(duì)象應(yīng)該是可以在不改變程序正確性的前提下玛歌,被子類所替代的 的概念 |
I | 接口隔離原則 | 接口隔離原則認(rèn)為 多個(gè)特定客戶端接口要好于一個(gè)寬泛用途的接口的概念 |
D | 依賴反轉(zhuǎn)原則 | 依賴反轉(zhuǎn)原則認(rèn)為一個(gè)方法應(yīng)該遵從依賴與抽象而不是一個(gè)實(shí)例 的概念,依賴注入是該原則的一種實(shí)現(xiàn)方式 |
開放封閉原則
- 對(duì)擴(kuò)展開放擎椰,對(duì)修改關(guān)閉
- 增加需求時(shí),擴(kuò)展新代碼创肥,而非修改已有的代碼
- 開閉原則是設(shè)計(jì)模式的總原則
- 對(duì)近期可能會(huì)變化并且如果有變化但改動(dòng)量巨大的地方要增加擴(kuò)展點(diǎn)达舒,擴(kuò)展點(diǎn)過多會(huì)降低可讀性
/**
* 開閉原則-失敗案例值朋,每次新增客戶類型都修改
*/
class Customer {
constructor(public rank:string) {
}
}
class Product {
constructor(public name:string,public price:number) {
}
cost(customer:Customer){
switch (customer.rank) {
case 'member':
return this.price*.8;
case 'vip':
return this.price*.6;
default:
return this.price;
}
}
}
let product=new Product('筆記本電腦',1000);
let c1=new Customer('member');
let c2=new Customer('guest');
console.log(product.cost(c1));
console.log(product.cost(c2));
/**
* 開閉原則
*/
export {}
class Customer {
constructor(public rank:string,public discount:number=1) {}
}
class Product {
constructor(public name:string,public price:number) {
}
cost(customer:Customer){
return this.price*customer.discount;
}
}
let product=new Product('筆記本電腦',1000);
let c1=new Customer('member',0.8);
let c2=new Customer('guest');
console.log(product.cost(c1));
console.log(product.cost(c2));
單一職責(zé)原則
- 一個(gè)類或者模塊只負(fù)責(zé)完成一個(gè)職責(zé),如果功能特別復(fù)雜就進(jìn)行拆分
- 單一職責(zé)可以降低類的復(fù)雜性巩搏,提高代碼可讀性昨登,可維護(hù)性
- 當(dāng)類的代碼行數(shù)過多,方法過多贯底,功能太多丰辣,職責(zé)太雜的時(shí)候就要對(duì)類進(jìn)行拆分了
- 拆分不能過去,如果拆分過度會(huì)損失內(nèi)聚性和維護(hù)性
里氏替換原則
- 所有引用基類的地方必須能透明的使用其子類的對(duì)象
- 子類能替換掉父類禽捆,使用者可能根本不需要知道是父類還是子類笙什,反之則不行
- 里氏替換原則是開閉原則的實(shí)現(xiàn)基礎(chǔ),程序設(shè)計(jì)的時(shí)候盡量使用基類定義及引用胚想,運(yùn)行時(shí)再?zèng)Q定使用哪個(gè)子類
- 里氏替換原則可以提高代碼的復(fù)用性琐凭,提高代碼的可擴(kuò)展性,也增加了耦合性
- 相對(duì)于多態(tài)浊服,這個(gè)原則是講的是類如何設(shè)計(jì)统屈,子類如果違反了父類的功能則表示違反了里氏替換原則
依賴倒置原則
- 面向接口編程,依賴于抽象而不依賴于具體實(shí)現(xiàn)
- 要求我們?cè)诔绦虼a中傳遞參數(shù)或者再關(guān)聯(lián)關(guān)系中牙躺,盡量引用層級(jí)高得抽象層類
- 使用方只關(guān)注接口而不關(guān)注具體類得實(shí)現(xiàn)
接口隔離原則
- 保持接口得單一獨(dú)立愁憔,避免出現(xiàn)胖接口
- 客戶端不應(yīng)該依賴它不需要得接口,類間得依賴關(guān)系應(yīng)該建立在最小得接口上
- 接口盡量細(xì)化孽拷,而且接口中得方法盡量得少
- 類似于單一職責(zé)原則吨掌,更關(guān)注接口
迪米特法則
- 也叫做最少知識(shí)原則
- 一個(gè)軟件實(shí)體應(yīng)該盡可能少的與其他實(shí)體發(fā)生相互作用
- 迪米特法則得初衷在于降低類之間得耦合
- 類定義時(shí)盡量要實(shí)現(xiàn)內(nèi)聚,少使用public乓搬,盡量使用private思犁,protected等
合成復(fù)用原則
類得原則
- 類之間有三種基本關(guān)系,分別是關(guān)聯(lián)(聚合和組合)进肯、泛化和依賴
- 如果一個(gè)類單向依賴另一個(gè)類激蹲,那么他們之間就是單向關(guān)聯(lián)。如果彼此依賴則為相互依賴江掩,即雙向關(guān)聯(lián)
- 關(guān)聯(lián)關(guān)系包括兩種特例:聚合和組合
- 聚合学辱,用來表示整體與部分得關(guān)系或者擁有關(guān)系,代表部分得對(duì)象可能會(huì)被整體擁有环形,但并不一定會(huì)隨著整體的消亡而銷毀策泣。比如班級(jí)和學(xué)生
- 合成或者說組合要比聚合關(guān)系強(qiáng)的多,部分和整體得生命周期是一致得抬吟,比如人和器官之間
從弱到強(qiáng):依賴>關(guān)聯(lián)>聚合>組合
合成復(fù)用原則
- 合成復(fù)用原則是通過將已有得對(duì)象納入新對(duì)象中萨咕,作為新對(duì)象得成員來實(shí)現(xiàn)的
- 新對(duì)象可以調(diào)用已有對(duì)象得功能,從而達(dá)到復(fù)用
- 原則是盡量首先使用組合/聚合得方式火本,而不是使用繼承
工廠模式
簡單工廠
由一個(gè)工廠對(duì)象決定創(chuàng)建出哪一種產(chǎn)品類得實(shí)例
/**
* 簡單工廠模式
*/
abstract class Coffee {
constructor(public name: string) {}
}
class AmericanCoffee extends Coffee {}
class LatteCoffee extends Coffee {}
//簡單工廠
class CafeFactory {
static order(name: string) {
switch (name) {
case 'American':
return new AmericanCoffee('美式');
case 'Latte':
return new LatteCoffee('拿鐵');
default:
throw new Error("咖啡類型沒有");
}
}
}
缺點(diǎn):不符合開閉原則危队,每次增加或者刪除都要修改switch代碼
工廠方法
- 又稱為多態(tài)性工廠模式
- 在工廠方法模式中聪建,核心得工廠類不再負(fù)責(zé)所有得產(chǎn)品得創(chuàng)建,而是將具體創(chuàng)建得工作交給工廠子類去做
/**
* 工廠方法模式
*/
abstract class Coffee {
constructor(public name: string) {}
}
class AmericanCoffee extends Coffee {}
class LatteCoffee extends Coffee {}
abstract class CafeFactory {
abstract createCoffee():Coffee;
}
class AmericanCoffeeFactory extends CafeFactory{
createCoffee(): Coffee {
return new AmericanCoffee('美式');
}
}
class LatteCoffeeFactory extends CafeFactory{
createCoffee(): Coffee {
return new AmericanCoffee('拿鐵');
}
}
//在工廠方法里茫陆,不再由Factory來創(chuàng)建產(chǎn)品金麸,而是先創(chuàng)建具體得工廠,然后具體得工廠來創(chuàng)建產(chǎn)品
class Factory {
static order(name: string) {
switch (name) {
case 'American':
return new AmericanCoffeeFactory().createCoffee();
case 'Latte':
return new LatteCoffeeFactory().createCoffee();
default:
throw new Error("咖啡類型沒有");
}
}
}
抽象工廠模式
- 抽象工廠模式可以向客戶端提供一個(gè)接口簿盅,使客戶端在不必指定產(chǎn)品得具體得情況下挥下,創(chuàng)建多個(gè)產(chǎn)品族中得產(chǎn)品對(duì)象
- 工廠方法模式針對(duì)得是同一類或同等級(jí)產(chǎn)品,而抽象工廠模式針對(duì)得是多種類得產(chǎn)品設(shè)計(jì)
- 系統(tǒng)中有多個(gè)產(chǎn)品族桨醋,每個(gè)具體工廠負(fù)責(zé)創(chuàng)建同一族但屬于不同產(chǎn)品等級(jí)(產(chǎn)品種類)得產(chǎn)品
- 產(chǎn)品族是一組相關(guān)或相互依賴得對(duì)象
- 系統(tǒng)一次只能消費(fèi)某一族產(chǎn)品棚瘟,即相同產(chǎn)品族得產(chǎn)品是一起被使用得
- 當(dāng)系統(tǒng)需要新增一個(gè)產(chǎn)品族時(shí),只需要增加新的工廠類即可,無需修改源代碼讨盒;但是如果需要產(chǎn)品族中增加一個(gè)新種類得產(chǎn)品時(shí)候解取,則所有得工廠類都需要修改
/**
* 抽象工廠模式
*/
abstract class AmericanCoffee { }
abstract class LatteCoffee { }
class StarBucksAmericanCoffee extends AmericanCoffee {
}
class LuckinAmericanCoffee extends AmericanCoffee {
}
class StarBucksLatteCoffee extends LatteCoffee {
}
class LuckinLatteCoffee extends LatteCoffee {
}
abstract class CafeFacroty {
abstract createAmericanCoffee():AmericanCoffee;
abstract createLatteCoffee():LatteCoffee;
}
class LuckinCafeFactory extends CafeFacroty {
createAmericanCoffee(): AmericanCoffee {
return new LuckinAmericanCoffee()
}
createLatteCoffee(): LatteCoffee {
return new LuckinLatteCoffee()
}
}
class StarBucksCafeFactory extends CafeFacroty {
createAmericanCoffee(): AmericanCoffee {
return new StarBucksAmericanCoffee()
}
createLatteCoffee(): LatteCoffee {
return new StarBucksLatteCoffee()
}
}
單例模式
class Windows {
private static instance:Windows;
private constructor() {
}
public static getInstance(){
if (!Windows.instance) {
Windows.instance=new Windows();
}
return Windows.instance;
}
}
let w1=Windows.getInstance();
let w2=Windows.getInstance();
console.log(w1===w2);//true
透明單例
- 使用者并不需要知道按單例使用
let Window=(function() {
let window:Window;
let Window=function(this:Window) {
if (window) {
return window;
}else{
//this變量賦值給window并返回
return (window=this);
}
}
return Window;
})();
//還是正常new著使用
let w1=new (Window as any)();
let w2=new (Window as any)();
console.log(w1===w2);
單例與構(gòu)建過程的分離
function Window() {
}
Window.prototype.hello=function() {
console.log('hello');
}
let createInstance=(function() {
let instance:Window;
return function() {
if (!instance) {
instance=new (Window as any);
}
return instance;
}
})();
let w1=createInstance();
let w2=createInstance();
console.log(w1===w2);
封裝變化
function Window() {
}
Window.prototype.hello = function () {
console.log('hello');
}
//希望可以創(chuàng)建任意類型的實(shí)例
let createInstance = function (Constructor: any) {
let instance: any;
return function (this: any) {
if (!instance) {
Constructor.apply(this, arguments);
//因?yàn)檎G闆r下,this的__proto__是當(dāng)前函數(shù)返顺,即匿名函數(shù)的禀苦,但是此時(shí)需要的是外部指向,因?yàn)槭莿?dòng)態(tài)的
//this.__proto__=Constructor.prototype
Object.setPrototypeOf(this,Constructor.prototype)
instance = this;
}
return instance;
}
};
/*let createWindow = createInstance(Window);
let w1= new (createWindow as any)();
let w2= new (createWindow as any)();*/
let createWindow:any = createInstance(Window);
let w1= new createWindow();
let w2= new createWindow();
console.log(w1 === w2);
適配器模式
- 適配器模式又稱包裝器模式遂鹊,將一個(gè)類得接口轉(zhuǎn)化成用戶需要得另一個(gè)接口振乏,解決類(對(duì)象)之間接口不兼容得問題
- 舊得接口和使用者不兼容
- 中間加一個(gè)適配器轉(zhuǎn)換接口
//需要被適配得類
class Socket {
output(){
return '220V';
}
}
abstract class Power{
abstract charge():string;
}
//適配器類
class PowerAdapter extends Power {
constructor(public socket:Socket) {
super();
}
charge(): string {
return this.socket.output()+'轉(zhuǎn)換成24V';
}
}
let adapter=new PowerAdapter(new Socket());
console.log(adapter.charge()); //220V轉(zhuǎn)換成24V
axios適配簡化邏輯
function getDefaultAdapter() {
let adapter;
if (typeof XMLHtttpRequest !='undefined') {
adapter=xhr;//xhr另外函數(shù),其實(shí)就是瀏覽器環(huán)境邏輯
}else{
adapter=http;//http另外函數(shù)秉扑,其實(shí)就是node環(huán)境邏輯
}
return adapter;
}
其他例如node得promisify也是適配器模式得使用慧邮,回調(diào)轉(zhuǎn)promise;還有vue中得computed中可以把字符串轉(zhuǎn)大寫
裝飾器模式
- 在不改變其原有結(jié)構(gòu)和功能為對(duì)象添加新功能得模式其實(shí)就叫做裝飾器模式
- 最直觀得就是買房后得裝修
- 裝飾比繼承更加靈活,可以實(shí)現(xiàn)裝飾著和被裝飾者之間松耦合
- 被裝飾者可以使用裝飾者動(dòng)態(tài)得增加和撤銷功能
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(): void {
this.shape.draw();
console.log('邊框紅色');
}
}
let redColorfulShape=new RedColorfulShape(new Circle());
redColorfulShape.draw();
namespace c{
interface Animal{
swings:number;
fly:Function;
}
function flyable(target: any) {
target.prototype.swings=2;
target.prototype.fly=function() {
console.log('飛');
}
}
@flyable
class Animal {
constructor() {
}
}
let animal:Animal=new Animal();
animal.fly();
}
裝飾器模式擴(kuò)展(重點(diǎn))
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
</style>
<body>
<input type="text" id="username">
<button id="register">提交</button>
</body>
</html>
<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;
if (!username) {
return alert('用戶名不能為空!')
}
return true;
})
let registerBnt = document.getElementById('register');
registerBnt.addEventListener('click', registerFn);
</script>
代理模式
- 由于一個(gè)對(duì)象不能直接引用另一個(gè)對(duì)象舟陆,所以需要通過代理對(duì)象在兩個(gè)對(duì)象之間起到中介作用
- 代理模式就算為目標(biāo)對(duì)象創(chuàng)造一個(gè)代理對(duì)象误澳,以實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象的訪問
- 這樣就可以在代理對(duì)象里增加一些邏輯判斷,調(diào)用前或調(diào)用后執(zhí)行一些操作秦躯,從而實(shí)現(xiàn)擴(kuò)展目標(biāo)的功能
- 火車票代購忆谓,房產(chǎn)中介,律師等
類描述
- Target目標(biāo)對(duì)象踱承,也就是被代理的對(duì)象倡缠,是具體業(yè)務(wù)的執(zhí)行者
- Proxy代理對(duì)象,里面會(huì)包含一個(gè)目標(biāo)對(duì)象的引用茎活,可以實(shí)現(xiàn)對(duì)訪問的擴(kuò)展和額外處理
interface Star{
answerPhone():void;//接電話
}
class Anglebaby implements Star {
//是否有空的意思
available:boolean=true;
answerPhone(): void {
console.log('你好,我是Anglebaby');
}
}
//Anglebaby經(jīng)紀(jì)人
class AnglebabyAgent implements Star {
anglebaby
constructor(){
this.anglebaby= new Anglebaby();
}
answerPhone(): void {
console.log('你好,我是Anglebaby經(jīng)紀(jì)人');
if (this.anglebaby.available) {
this.anglebaby.answerPhone();
}else{
console.log('Anglebaby沒空');
}
}
}
let anglebabyAgent=new AnglebabyAgent();
anglebabyAgent.answerPhone();
代理模式使用場(chǎng)景
事件委托代理
- 事件捕獲指的是從document到觸發(fā)事件的那個(gè)節(jié)點(diǎn)昙沦,即自上而下的去觸發(fā)事件
- 事件冒泡是自下而上的去觸發(fā)事件
- 綁定事件的方法的第三個(gè)參數(shù),就算控制事件觸發(fā)順序是否為事件捕獲载荔。true為事件捕獲盾饮,false為事件冒泡,默認(rèn)false
圖片預(yù)加載
- 本質(zhì)就是大圖先加載loading圖,等實(shí)際圖片加載完畢時(shí)候才顯示
- 通過data-set設(shè)置真實(shí)圖片地址丐谋,然后通過new Image()的Onload事件回調(diào)設(shè)置真實(shí)路徑
虛擬代理(圖片懶加載) - 即判斷圖片距離頂端的距離來確認(rèn)是否在可視區(qū)域芍碧,在的話就從dataset中取出來圖片地址做顯示煌珊,否則只顯示占位(可能是圖片)
- 當(dāng)前可視區(qū)域的高度
window.innerHeight||document.documentElement.clientHeight
- 元素距離可視區(qū)域頂部的高度
getBoundingClientRect().top
緩存代理 - 有些時(shí)候可以用空間換時(shí)間
- 一個(gè)正整數(shù)的階乘是所有小于及等于該數(shù)的正整數(shù)的積号俐,并且0的階乘為1
const factorial=function f(num) {
if (num==1) {
return 1;
} else {
return (num*f(num-1))
}
}
const proxy=function(fn) {
const cache={};//緩存對(duì)象
return function(num) {
if (num in cache) {
return cache[num];//使用緩存代理
}
return cache[num]=fn.call(this,num);
}
}
const proxyFactorial=proxy(factorial);
防抖代理
- 通過防抖代理優(yōu)化可以把多次請(qǐng)求合并為一次,提高性能
- 節(jié)流和防抖都是為了減少頻繁觸發(fā)事件回調(diào)
- 節(jié)流是在某段時(shí)間內(nèi)不管觸發(fā)了多少次回調(diào)都只認(rèn)第一個(gè)定庵,并在第一次結(jié)束后指定回調(diào)
- 防抖就是在某段時(shí)間不管觸發(fā)了多少回調(diào)都只看最后一個(gè)
代理跨域 - 正向代理和反向代理
- 正向代理的對(duì)象是客戶端吏饿,服務(wù)器端看不到真正的客戶端
- 通過公司代理服務(wù)器上網(wǎng)
- 反向代理的對(duì)象是服務(wù)端,客戶端看不到真正的服務(wù)端
- nginx代理應(yīng)用服務(wù)器
let http=require('http');
const httpProxy=require('http-proxy');
const proxy=httpProxy.createProxyServer();
//把當(dāng)前請(qǐng)求代理到9999端口對(duì)應(yīng)的服務(wù)-反向代理
let server=http.createServer((req,res)=>{
proxy.web(req,res,{
target:'http://127.0.0.1:9999'
});
})
server.listen(8888,()=>console.log('8888'))
- 代理跨域
Proxy
- Proxy用于修改某些操作的默認(rèn)行為
- Proxy可以理解成蔬浙,在目標(biāo)對(duì)象之前架設(shè)一層攔截猪落,外界對(duì)該對(duì)象的訪問,都必須先通過這層攔截畴博,因此提供了一種機(jī)制笨忌,可以對(duì)外界的訪問進(jìn)行過濾和改寫
- Proxy這個(gè)次的愿意就是代理,用在這里表示由它來代理某些操作
let wang={
name:'wanglaoshi',
age:31,
height:160
}
let wangMama=new Proxy(wang,{
get(target,key){
//target其實(shí)就是wang
if (key==='age') {
return wang.age-5;
} else {
return wang.height+5;
}
return target[key];
},
set(target,key,value){
if (key==='boyfriend') {
if (value.age>40) {
throw new Error('太大')
}else if (value.salary<20000) {
throw new Error('賺的太少')
}else{
target[key]=value;
return true;
}
}
return true;
}
})
對(duì)比
- 代理模式VS適配器模式 適配器提供不同接口俱病,代理模式提供一摸一樣的接口
- 代理模式VS裝飾器模式 裝飾器模式原來的功能不變還可以使用官疲,代理模式改變?cè)瓉淼墓δ?/li>
外觀模式
該模式就是把一些復(fù)雜的流程封裝成一個(gè)接口供給外部用戶更簡單的使用
- 門面角色:外觀模式的核心绑改。它被客戶角色調(diào)用癞揉,它熟悉子系統(tǒng)的功能久妆。內(nèi)部根據(jù)客戶角色的需求預(yù)定義
- 子系統(tǒng)角色:實(shí)現(xiàn)了子系統(tǒng)的功能榆苞。它對(duì)客戶角色和Facade時(shí)未知的
- 客戶角色:通過調(diào)用Facede來完成要實(shí)現(xiàn)的功能
class Sum{
sum(a,b){return a+b;}
}
class Minus{
minus(a,b){return a-b;}
}
class Multiply{
multiply(a,b){return a*b;}
}
//門面
class Calculator{
constructor(){
this.sum1=new Sum();
this.minus1=new Minus();
this.multiply1=new Multiply();
}
sum(a,b){
return this.sum1.sum(a,b);
}
minus(a,b){
return this.minus1.minus(a,b);
}
}
let calculator=new Calculator();
console.log(calculator.sum(1,2));
觀察者模式(發(fā)布訂閱模式)
例如node中的事件總線庫也是這么實(shí)現(xiàn)
- 被觀察者供維護(hù)觀察者的一系列方法
- 觀察者提供更新接口
- 觀察者把自己注冊(cè)到被觀察者里面
- 在被觀察者發(fā)生變化時(shí)候成箫,調(diào)用觀察者的更新方法
class Star {
constructor(name) {
this.name = name;
this.state = '';
this.observers = [];//粉絲
}
getState() {
return this.state
}
setState(state) {
this.state = state;
this.notifyAllObservers();
}
//增加新的觀察者
attach(observer) {
this.observers.push(observer);
}
notifyAllObservers() {
if (this.observers.length > 0) {
this.observers.forEach(observer => {
observer.update();
})
}
}
}
class Fan {
constructor(name, star) {
this.name = name;
this.star = star;
this.star.attach(this);
}
update() {
console.log(this.name, this.star.state);
}
}
let star = new Star('dsa');
let f1 = new Fan('張三', star);
let f2 = new Fan('李四', star);
star.setState('綠色');
當(dāng)然觀察者模式很多種寫法铆铆,不見得就兩者關(guān)系种蝶,也可以N者员萍,例如:租客促王,房東犀盟,中介,那么中介就處于監(jiān)聽二者消息并觸發(fā)對(duì)應(yīng)的回調(diào)邏輯
狀態(tài)模式
例如一些
有限狀態(tài)機(jī)(例如:冰到水水到氣蝇狼,逆向也是阅畴,狀態(tài)有限,狀態(tài)切換順序執(zhí)行不能跳躍切換题翰,稱作狀態(tài)機(jī)恶阴,)
就是利用了狀態(tài)模式
- 當(dāng)一個(gè)對(duì)象的內(nèi)部狀態(tài)發(fā)生改變時(shí),回導(dǎo)致其行為的改變豹障,這看起來就像是改變了對(duì)象
- 對(duì)象有自己的狀態(tài)
- 不同狀態(tài)下執(zhí)行的邏輯不一樣
- 明確狀態(tài)和每個(gè)狀態(tài)下執(zhí)行的動(dòng)作
- 用來減少
if else
語句
class Battery{
constructor(){
this.amount='high';
this.state=new SuccessState();//綠色狀態(tài)
}
show(){
this.state.show();//把顯示的邏輯委托給狀態(tài)對(duì)象
//雖然也有if else但是具體邏輯時(shí)候很復(fù)雜則避免了多層嵌套冯事,因?yàn)檫壿嬏崛×? if (this.amount=='high') {
this.amount='middle';
this.state=new ErrorState();
} else {
this.amount='high';
this.state=new SuccessState();
}
}
}
class SuccessState{
show(){
console.log('綠色');
}
}
class ErrorState{
show(){
console.log('紅色');
}
}
let b=new Battery();
b.show();
b.show();
b.show();
b.show();
有限狀態(tài)機(jī):https://github.com/jakesgordon/javascript-state-machine
策略模式
- 將定義的一組算法封裝起來,使其相互之間可以替換血公。封裝的算法具有一定的獨(dú)立性昵仅,不會(huì)隨著客戶端變化而變化
- 避免大量的
swith case
class Customer{
constructor(kind){
this.kind=kind;
}
pay(amount){
//此處本來是通過kind判斷類型,然后switch 或者if else
return this.kind.pay(amount);
}
}
class Normal{
pay(amount){return amount};
}
class Vip{
pay(amount){return amount*0.8};
}
let c=new Customer(new Normal());
console.log(c.pay(100));
c.kind=new Vip();
console.log(c.pay(100));
- 策略抽取
class Customer{
constructor(){
this.kinds={
normal:function(amount) {
return amount;
},
memeber:function(amount) {
return amount*.9;
},
vip:function(amount) {
return amount*.8;
}
}
}
pay(kind,amount){
return this.kinds[kind](amount);
}
}
let c=new Customer();
console.log(c.pay('normal',100));
策略模式在emementui中也有,例如登錄框校驗(yàn)策略
策略模式和狀態(tài)模式的區(qū)別
- 策略模式和狀態(tài)模式都有上下文摔笤,有策略或者狀態(tài)類够滑,上下文把這些請(qǐng)求委托給這些類來執(zhí)行
- 策略模式中各個(gè)類是平等的,沒有關(guān)系吕世,客戶端需要知道算法主動(dòng)切換彰触,狀態(tài)模式中,狀態(tài)的切換被封裝好了客戶端不需要了解細(xì)節(jié)
原型模式
- 基于原有的一個(gè)對(duì)象創(chuàng)建一個(gè)新對(duì)象
- 原型模式是一個(gè)創(chuàng)建型的模式
- 創(chuàng)建基類的時(shí)候命辖,簡單差異化的屬性放在構(gòu)造函數(shù)中况毅,消耗資源相同的功能放在基類原型中
不是原型模式
function Person(name) {
this.name=name;
this.getName=function() {
console.log(this.name);
}
}
let p1=new Person('zs');
let p2=new Person('ls');
console.log(p1.getName===p2.getName);//false
- 函數(shù)復(fù)用
function Person(name) {
this.name=name;
}
Person.prototype.getName=function() {
console.log(this.name);
}
let p1=new Person('zs');
let p2=new Person('ls');
console.log(p1.getName===p2.getName);//true
橋接模式
- 將抽象部分與它的實(shí)現(xiàn)部分分離,這樣抽象化與實(shí)現(xiàn)化解耦尔艇,使他們可以獨(dú)立的變化
- 應(yīng)用場(chǎng)景是實(shí)現(xiàn)系統(tǒng)可能有多個(gè)角度分類尔许,每一種角度都可能變化
- 橋方可以通過實(shí)現(xiàn)橋接口進(jìn)行單方面擴(kuò)展,而另一方可以繼承抽象類而單方面擴(kuò)展终娃,而之間的調(diào)用就從橋接口來作為突破口味廊,不會(huì)受到雙方擴(kuò)展的任何影響
class A {
constructor(bridge) {
this.position = 'A地點(diǎn)';
this.bridge = bridge;
}
go() {
console.log(`從${this.from()}到達(dá)${this.bridge.to()}`);
}
from() {
throw new Error('子類必須實(shí)現(xiàn)此方法')
}
}
class A1 extends A {
from() {
return 'A1';
}
}
class A2 extends A {
from() {
return 'A2';
}
}
class B {
to() {
throw new Error('子類必須實(shí)現(xiàn)此方法')
}
}
class B1 extends B {
to() { return 'B1' }
}
class B2 extends B {
to() { return 'B2' }
}
let b2 = new B2();
let a1 = new A1(b2);
a1.go();//從A1到達(dá)B2
// 此時(shí)B就是一座橋,A .B都是抽象棠耕,符合橋接模式定義余佛,然后B是橋連接A1 B2
//重點(diǎn):其他場(chǎng)景,例如:事件綁定中的callback昧辽,把callback提取出去衙熔,
//在node和瀏覽器環(huán)境傳入不同回調(diào)適應(yīng)不同環(huán)境同時(shí)降低了代碼耦合
//項(xiàng)目中:例如運(yùn)行態(tài)注冊(cè)的事件循環(huán),也是橋接模式的實(shí)現(xiàn)
/**
* 至于解耦實(shí)現(xiàn):
* 例如:在canvas繪畫中搅荞,畫一個(gè)形狀红氯,那么內(nèi)部可以傳入不同的位置和顏色等
* new Position() new Color()當(dāng)作參數(shù)傳入,而不是通過不同判斷實(shí)現(xiàn)不同邏輯復(fù)雜糾纏在一起
* /
組合模式
- 又稱整體-部分模式
- 將對(duì)象組合成樹形結(jié)構(gòu)以表示部分-整體的層次結(jié)構(gòu)
- 客戶可以使用統(tǒng)一的方式對(duì)待組合對(duì)象和葉子對(duì)象
- 優(yōu)點(diǎn):可以方便地構(gòu)造一棵樹來表示對(duì)象的部分-整體 結(jié)構(gòu)咕痛。在樹的構(gòu)造最終 完成之后痢甘,只需要通過請(qǐng)求樹的最頂層對(duì) 象,便能對(duì)整棵樹做統(tǒng)一一致的操作
- 缺點(diǎn):創(chuàng)建出來的對(duì)象長得都差不多茉贡,可能會(huì)使代碼不好理解塞栅,創(chuàng)建太多的對(duì)象對(duì)性能也會(huì)有一些影響
// 創(chuàng)建一個(gè)宏命令
const MacroCommand = function () {
return {
// 宏命令的子命令列表
commandsList: [],
// 添加命令到子命令列表
add: function (command) {
this.commandsList.push(command)
},
// 依次執(zhí)行子命令列表里面的命令
execute: function () {
for (var i = 0, command; command = this.commandsList[i++];) {
command.execute()
}
}
}
}
// 打開空調(diào)命令
const openAcCommand = {
execute: function () {
console.log('打開空調(diào)')
}
}
//打開電視和音響
const openTvCommand = {
execute: function () {
console.log('打開電視')
}
}
var openSoundCommand = {
execute: function () {
console.log('打開音響')
}
}
// 創(chuàng)建一個(gè)宏命令
const macroCommand1 = MacroCommand()
// 把打開電視裝進(jìn)這個(gè)宏命令里
macroCommand1.add(openTvCommand)
// 把打開音響裝進(jìn)這個(gè)宏命令里
macroCommand1.add(openSoundCommand)
//關(guān)門、打開電腦和打登錄QQ的命令
const closeDoorCommand = {
execute: function () {
console.log('關(guān)門')
}
}
const openPcCommand = {
execute: function () {
console.log('開電腦')
}
}
const openQQCommand = {
execute: function () {
console.log('登錄QQ')
}
};
//創(chuàng)建一個(gè)宏命令
const macroCommand2 = MacroCommand()
//把關(guān)門命令裝進(jìn)這個(gè)宏命令里
macroCommand2.add(closeDoorCommand)
//把開電腦命令裝進(jìn)這個(gè)宏命令里
macroCommand2.add(openPcCommand)
//把登錄QQ命令裝進(jìn)這個(gè)宏命令里
macroCommand2.add(openQQCommand)
//把各宏命令裝進(jìn)一個(gè)超級(jí)命令中去
const macroCommand = MacroCommand()
macroCommand.add(openAcCommand)//子命令
macroCommand.add(macroCommand1)//宏命令
macroCommand.add(macroCommand2)//宏命令
//重點(diǎn):從上可知腔丧,不論是什么類型的命令對(duì)調(diào)用者來說使用方式相同放椰,沒有區(qū)別
命令模式
- 執(zhí)行命令時(shí),發(fā)布者和執(zhí)行者分開
- 中間加入命令對(duì)象愉粤,作為中轉(zhuǎn)站
- 三種角色
-
Receiver
接受者角色:該角色就是干活的角色砾医,命令傳遞到這里是應(yīng)該被執(zhí)行的 -
Command
命令角色:需要執(zhí)行的所有命令都在這里聲明 -
Invoker
調(diào)用者角色:接收到命令,并執(zhí)行命令
-
class Cooker {
cook() {
console.log('做飯');
}
}
class Cleaner {
clean() {
console.log('保潔');
}
}
class CookCommand {
constructor(receiver) {
this.receiver = receiver;
}
execute() {
this.receiver.cook();
}
}
class CleanCommand {
constructor(receiver) {
this.receiver = receiver;
}
execute() {
this.receiver.clean();
}
}
class Customer {
constructor(command) {
this.command = command;
}
setCommand(command){
this.command = command;
}
clean() {
this.command.execute();
}
cook() {
this.command.execute();
}
}
//接收者
let cooker=new Cooker();
let cleaner=new Cleaner();
//命令角色
let cookCommand=new CookCommand(cooker);
let cleanCommand=new CleanCommand(cleaner);
//調(diào)用者
let customer=new Customer(cookCommand);
customer.cook();//做飯
customer.setCommand(cleanCommand);
customer.clean();//保潔
享元模式
- 共享內(nèi)存衣厘,節(jié)約內(nèi)存空間
- 相同的數(shù)據(jù)共享使用
- 主要還是對(duì)數(shù)據(jù)如蚜、方法共享分離压恒,將數(shù)據(jù)的方法分為內(nèi)部數(shù)據(jù)、內(nèi)部方法和外部數(shù)據(jù)错邦、外部方法
- 內(nèi)部狀態(tài)保存在對(duì)象內(nèi)部探赫,通常不會(huì)改變,可以共享
- 外部狀態(tài)保存在對(duì)象外部撬呢,可以隨場(chǎng)景改變伦吠,不可以共享
function Person(name,age) {
this.name=name;
this.age=age;
}
Person.prototype.getName=function() {
return this.name;
}
Person.prototype.getAge=function() {
return this.age;
}
let p1=new Person();
let p2=new Person();
方式演進(jìn)
- 正常無享元模式寫法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="radio" value="red" name="color" checked>紅色
<input type="radio" value="yellow" name="color">黃色
<input type="radio" value="blue" name="color">藍(lán)色
<button onclick="draw()">繪制</button>
<div id="container"></div>
</body>
</html>
<script>
function draw() {
let btns=Array.from(document.getElementsByName('color'));
let btn=btns.find(item=>item.checked);
let color=btn?btn.value:'red';
let div=document.createElement('div');
div.style=`width:100px;height:100px;background-color:${color}`;
document.getElementById('container').appendChild(div);
}
</script>
- 享元模式優(yōu)化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="radio" value="red" name="color" checked>紅色
<input type="radio" value="yellow" name="color">黃色
<input type="radio" value="blue" name="color">藍(lán)色
<button onclick="draw()">繪制</button>
<div id="container">
</div>
</body>
</html>
<script>
class MyDiv{
constructor(){
this.element=document.createElement('div');
}
setColor(color){
this.element.style=`width:100px;height:100px;background-color:${color}`;
}
}
//div對(duì)象是同一個(gè),則下面即使是appendChild也不會(huì)追加而是修改
let myDiv=new MyDiv();
function draw() {
let btns=Array.from(document.getElementsByName('color'));
let btn=btns.find(item=>item.checked);
let color=btn?btn.value:'red';
myDiv.setColor(color);
document.getElementById('container').appendChild(myDiv.element);
}
</script>
模板方法模式
- 模板方法模式在一個(gè)方法種定義一個(gè)算法的骨架倾芝,而將一些步驟的實(shí)現(xiàn)延遲到子類中
- 模板方法使得子類可以在不改變算法結(jié)構(gòu)的情況下讨勤,重新定義算法中某些步驟的具體實(shí)現(xiàn)
- 一般有兩部分組成,第一部分是抽象父類晨另,第二部分是具體的實(shí)現(xiàn)子類
- 好萊塢原則,子類放棄了控制權(quán)谱姓,改由父類來調(diào)用
- 發(fā)布訂閱
- 回調(diào)函數(shù)
class Person {
//此處就是不改變的算法結(jié)構(gòu)
dinner() {
this.buy();
this.cook();
this.eat();
}
buy() {
//相當(dāng)于抽象
throw new Error('必須由子類去實(shí)現(xiàn)')
}
cook() {
throw new Error('必須由子類去實(shí)現(xiàn)')
}
eat() {
throw new Error('必須由子類去實(shí)現(xiàn)')
}
}
class Teacher extends Person {
//子類重新定義算法的實(shí)現(xiàn)
buy() {
console.log('買菜');
}
cook() {
console.log('做菜');
}
eat() {
console.log('吃飯');
}
}
let t=new Teacher();
t.dinner();
例如:elementui的警告框借尿,其實(shí)內(nèi)部不同顏色顯示久利用了模板方法模式