結合ES6?編寫?JavaScript?設計模式中的結構型模式
前言
本文將對 20 多種 JavaScript 設計模式進行簡單概述,然后結合 ES6 類的方式來編寫實例代碼展示其使用方式。
JavaScript 在現(xiàn)代前端中扮演重要的角色,相比過去能夠做的事情已經(jīng)不在一個級別上了。JavaScript 最大的特征是其靈活性,一般只要敢想敢寫,可以把程序寫得很簡單,有可以寫得很復雜。其靈活性導致編寫 JavaScript 的時候能夠不斷的優(yōu)化,能夠不斷質疑寫的質量。而設計模式,是不分編程語言的,是軟件工程的內容,JavaScript 強大的表現(xiàn)力賦予了開發(fā)者運用設計模式編寫代碼時創(chuàng)造性。
設計模式系列文章:
什么是設計模式?
設計模式是軟件設計中常見問題的解決方案,這些模式很容易重復使用并且富有表現(xiàn)力。
在軟件工程中,設計模式(design pattern)是對軟件設計中普遍存在(反復出現(xiàn))的各種問題,所提出的解決方案。它并不直接用來完成代碼的編寫,而是描述在各種不同情況下,要怎么解決問題的一種方案。面向對象設計模式通常以類別或對象來描述其中的關系和相互作用,但不涉及用來完成應用程序的特定類別或對象。—— 維基百科
在 JavaScript 中使用設計模式主要有以下原因:
- 可維護性:設計模式有助于降低模塊間的耦合程度。
- 溝通:設計模式為處理不同類型的對象提供了一套通用的術語。
- 性能:對代碼起到優(yōu)化作用,提高程序的運行速度。當然不是所有的設計模式都能夠改善性能。
有三種模式:創(chuàng)建型模式,結構型模式、行為型模式。
- 創(chuàng)建型模式:解決與創(chuàng)建對象相關的問題。
- 結構型模式:處理實體之間的關系,以及它們如何共同組成一個更大的結構。
- 行為型模式:處理對象如何相互通信和交互。
結構型設計模式
結構型設計模式涉及類和對象組合,使用繼承來組合接口。
在軟件工程中,結構設計模式是通過識別實現(xiàn)實體之間關系的簡單方法來簡化設計的設計模式。
- 適配器模式
- 橋接模式
- 組合模式
- 裝飾者模式
- 門面模式
- 享元模式
- 代理模式
適配器模式
適配器模式允許具有不兼容接口的類通過將它們自己的接口封裝在現(xiàn)有類周圍來一起工作。適配器也稱為包裝器(wrapper),用來把不匹配的接口替換為一個可用于現(xiàn)有系統(tǒng)中的接口。
在軟件工程中,適配器模式是一種軟件設計模式,它允許將現(xiàn)有類的接口用作另一個接口。它通常用于使現(xiàn)有類在不修改其源代碼的情況下與其他類一起使用。
實例
實例將通過一個計算器的例子進行改造。 Calculator1 是舊接口,Calculator2 是新接口。將構建一個適配器,它將包裝新接口并使用其新方法為其提供結果。
class Calculator1 {
constructor() {
this.operations = function (value1, value2, operation) {
const operationHandler = {
add: () => value1 + value2,
sub: () => value1 - value2,
};
return (
typeof (operationHandler[operation] === "function") &&
operationHandler[operation]()
);
};
}
}
class Calculator2 {
constructor() {
this.add = function (value1, value2) {
return value1 + value2;
};
this.sub = function (value1, value2) {
return value1 - value2;
};
}
}
// 使用適配器模式構建類
class CalcAdapter {
constructor() {
const cal2 = new Calculator2();
this.operations = function (value1, value2, operation) {
return (
typeof (cal2[operation] === "function") &&
cal2[operation](value1, value2)
);
};
}
}
const adaptedCalc = new CalcAdapter();
console.log(adaptedCalc.operations(30, 25, "sub")); // 5橋接模式
橋接模式將抽象與實現(xiàn)分開,以便兩者可以獨立變化。是一種既能把兩個對象連接在一起,又能避免二者間的強耦合的方法。
橋接模式是一種結構化設計模式,它允許將一個大類或一組密切相關的類拆分為兩個獨立的層次結構:抽象和實現(xiàn),它們可以相互獨立地開發(fā)。—— 維基百科
實例
將創(chuàng)建渲染器類來渲染多個形狀。
class VectorRenderer {
renderCircle(radius) {
console.log(`渲染一個半徑為 ${radius} 的圓`);
}
}
class RasterRenderer {
renderCircle(radius) {
console.log(`繪制一個半徑為 ${radius} 的像素圓`);
}
}
class Shape {
constructor(renderer) {
this.renderer = renderer;
}
}
class Circle extends Shape {
constructor(renderer, radius) {
super(renderer);
this.radius = radius;
}
draw() {
this.renderer.renderCircle(this.radius);
}
resize(factor) {
this.radius *= factor;
}
}
const raster = new RasterRenderer();
const vector = new VectorRenderer();
const circle = new Circle(vector, 5);
const rasterCircle = new Circle(raster, 5);
circle.draw();
circle.resize(2);
circle.draw();
rasterCircle.draw();
rasterCircle.resize(2);
rasterCircle.draw();組合模式
組合模式組合對象,以便可以將它們作為單個對象進行操作,非常適合用于創(chuàng)建WEB上的動態(tài)用戶界面。
組合模式描述了一組對象,這些對象的處理方式與相同類型對象的單個實例相同。—— 維基百科
實例
將使用工作示例:
class Employer {
constructor(name, role) {
this.name = name;
this.role = role;
}
print() {
const { name } = this;
console.log(`姓名:${name}`);
}
}
class EmplyerGroup {
constructor(name, composite = []) {
console.log(name);
this.name = name;
this.composite = composite;
}
print() {
console.log(this.name);
this.composite.forEach((emp) => {
emp.print();
});
}
}
const ravi = new Employer("ravi", "developer");
const bhavy = new Employer("bhavy", "developer");
const groupDevelopers = new EmplyerGroup("Developer", [ravi, bhavy]);
groupDevelopers.print();裝飾者模式
裝飾者模式動態(tài)地添加或覆蓋對象的行為,用于把對象透明的包裝到另一個種具有相同接口的對象中。
裝飾器模式是一種設計模式,它允許將行為動態(tài)地添加到單個對象,而不會影響同一類中其他對象的行為。
實例
將以顏色和形狀為例,如果必須畫一個圓,將創(chuàng)建方法并畫一個圓。如果要畫一個紅色的圓圈,將行為被添加到一個對象中,裝飾器模式將幫助實現(xiàn)。
class Shape {
constructor(color = "") {
this.color = color;
}
}
class Circle extends Shape {
constructor(radius = 0) {
super();
this.radius = radius;
}
resize(factor) {
this.radius *= factor;
}
toString() {
return `一個直徑為 ${this.radius} 的園`;
}
}
class ColoredShape extends Shape {
constructor(shape, color) {
super();
this.shape = shape;
this.color = color;
}
toString() {
return `${this.shape.toString()},顏色為${this.color}`;
}
}
const circle = new Circle(2);
console.log(circle); // Circle { color: '', radius: 2 }
const redCircle = new ColoredShape(circle, "紅色");
console.log(redCircle.toString()); // 一個直徑為 2 的園,顏色為紅色門面模式
門面模式為復雜代碼提供了一個簡化的接口,可以用來把現(xiàn)有接口轉換為一個更便于使用的接口。
門面模式是面向對象編程中常用的一種軟件設計模式。與架構中的外觀類似,外觀是一個對象,它充當前端接口,掩蓋更復雜的底層或結構代碼。 —— 維基百科
實例
舉一個客戶端與計算機交互的例子:
class CPU {
freeze() {
console.log("凍結…");
}
jump() {
console.log("跳過…");
}
execute() {
console.log("執(zhí)行…");
}
}
class Memory {
load(position, data) {
console.log("加載中…");
}
}
class HardDrive {
read(lba, size) {
console.log("讀取中…");
}
}
class ComputerFacade {
constructor() {
this.processor = new CPU();
this.ram = new Memory();
this.hd = new HardDrive();
}
start() {
this.processor.freeze();
this.ram.load("", this.hd.read("lba", "size"));
this.processor.jump("");
this.processor.execute();
}
}
const computer = new ComputerFacade();
computer.start();享元模式
享元模式降低了創(chuàng)建類似對象的內存成本,是一種可以用于優(yōu)化目的的設計模式。最適合用于解決因創(chuàng)建大量類似對象而累及性能的問題。
享元是一種通過與其他類似對象共享盡可能多的數(shù)據(jù)來最小化內存使用的對象。—— 維基百科
實例
以用戶為例,讓有多個同名的用戶,可以通過存儲一個名稱來節(jié)省內存,并將其引用給具有相同名稱的用戶。
class User {
constructor(fullname) {
this.fullname = fullname;
}
}
class User2 {
constructor(fullname) {
const getOrAdd = (s) => {
const idx = User2.strings.indexOf(s);
if (idx !== -1) {
return idx;
} else {
User2.strings.push(s);
return User2.strings.length - 1;
}
};
this.names = fullname.split(" ").map(getOrAdd);
}
}
User2.strings = [];
const getRadmon = (max) => Math.floor(Math.random() * Math.floor(max));
const randomString = () => {
const result = [];
for (let x = 0; x < 10; ++x) {
result.push(String.fromCharCode(65 + getRadmon(26)));
}
return result.join("");
};現(xiàn)在將通過創(chuàng)建 10k 個用戶來進行不使用享元模式和使用享元模式的占用字符大小來類比內存大小。
const users = [];
const users2 = [];
const firstnames = [];
const lastnames = [];
for (let i = 0; i < 100; ++i) {
firstnames.push(randomString());
lastnames.push(randomString());
}
for (const first of firstnames) {
for (const last of lastnames) {
users.push(new User(`${first} ${last}`));
users2.push(new User2(`${first} ${last}`));
}
}
console.log(`10k用戶占用了大約 ${JSON.stringify(users).length} 字符`); // 10k用戶占用了大約 370001 字符
const user2length = [users2, User2.strings]
.map((x) => JSON.stringify(x).length)
.reduce((x, y) => x + y);
console.log(`10k用戶在享元模式占用約 ${user2length} 字符`); // 10k用戶在享元模式占用約 191602 字符代理模式
通過使用代理,一個類可以代表另一個類的功能。更加詳細的介紹,可以參閱《JavaScript 設計模式之代理模式》
代理模式是一種軟件設計模式。代理,在其最一般的形式中,是一個作為與其他事物的接口的類。—— 維基百科
實例
以價值代理作為實例。
class Percentage {
constructor(percent) {
this.percent = percent;
}
toString() {
return `${this.percent}%`;
}
valueOf() {
return this.percent / 100;
}
}
const fivePercent = new Percentage(5);
console.log(fivePercent.toString()); // 5%
console.log(`5% 的50倍是${50 * fivePercent}`); // 5% 的50倍是2.5到此這篇關于結合ES6 編寫 JavaScript 設計模式中的結構型模式的文章就介紹到這了,更多相關JS 結構型模式內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Javascript下IE與Firefox下的差異兼容寫法總結
總結一部分IE和Firefox的javascript差異寫法,對于像書寫多瀏覽器兼容性更好的代碼,可以參考下。2010-06-06
ymPrompt的doHandler方法來實現(xiàn)獲取子窗口返回值的方法
今天在寫頁面時用到了ymPrompt的win方法來彈出一個窗口。由于要用到獲取子窗口返回來的值判斷是否刷新父窗口,在ymPrompt的組件Demo中一直沒有找到合適的方法實現(xiàn)2010-06-06

