成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

常用的JavaScript設(shè)計(jì)模式你都知道嗎?

Jochen / 1608人閱讀

摘要:構(gòu)造函數(shù)模式是中最常用的模式之一,用于創(chuàng)建給定類型的新對象。原型模式這是基于對象的創(chuàng)造型設(shè)計(jì)模式。它通常用于目標(biāo)對象受到約束且可能無法有效地處理其所有職責(zé)的情況。它不是構(gòu)造函數(shù)其靜態(tài)方法用于可攔截的操作。

原文:https://medium.com/better-programming...  
譯者:前端技術(shù)小哥
簡介

我們編寫代碼是為了解決問題。 這些問題通常有很多相似之處,在嘗試解決這些問題時(shí),我們會(huì)注意到幾種常見的模式。 這就是設(shè)計(jì)模式的用武之地。
設(shè)計(jì)模式這個(gè)術(shù)語在軟件工程中用來表示軟件設(shè)計(jì)中經(jīng)常出現(xiàn)的問題的通用的、可重用的解決方案。
設(shè)計(jì)模式的基本概念從一開始就存在于軟件工程行業(yè),但它們并沒有真正正式化。由Erich Gamma、Richard Helm、Ralph Johnson和John Vlisides,著名的四人幫(GoF),編寫的《設(shè)計(jì)模式:可復(fù)用面對對象軟件的基礎(chǔ)》一書有助于推動(dòng)軟件工程中設(shè)計(jì)模式的形式化概念?,F(xiàn)在,設(shè)計(jì)模式是軟件開發(fā)的重要組成部分,并且已經(jīng)存在很長時(shí)間了。
原著介紹了23種設(shè)計(jì)模式。


設(shè)計(jì)模式的好處有很多。它們是業(yè)界資深人士已經(jīng)嘗試和測試過的經(jīng)過驗(yàn)證的解決方案。它們是可靠的方法,能夠以廣泛接受的方式解決問題,并反映出幫助定義問題的行業(yè)領(lǐng)先開發(fā)人員的經(jīng)驗(yàn)和見解。模式還使我們的代碼更具可重用性和可讀性,同時(shí)大大加快了開發(fā)過程。
設(shè)計(jì)模式絕不是最終的解決方案。它們只是為我們提供了解決問題的方法或方案。
注意:在本文中,我們將主要從面向?qū)ο蟮慕嵌纫约八鼈冊诂F(xiàn)代JavaScript中的可用性來討論設(shè)計(jì)模式。這就是為什么許多來自GoF的經(jīng)典模式不會(huì)被提及,而來自AddyOsmani的《JavaScript設(shè)計(jì)模式》中的一些現(xiàn)代模式將被囊括。為了便于理解,這些示例都保持簡單,因此不是各自設(shè)計(jì)模式的最佳實(shí)現(xiàn)。

設(shè)計(jì)模式的類別

設(shè)計(jì)模式通常分為三大類。

創(chuàng)建型模式

顧名思義,這些模式用于處理對象創(chuàng)建機(jī)制。創(chuàng)造性設(shè)計(jì)模式基本上通過控制對象的創(chuàng)建過程來解決問題。
我們將詳細(xì)討論以下模式: 建造者模式、工廠模式、原型模式和單例模式。

結(jié)構(gòu)型模式

這些模式與類和對象組合有關(guān)。它們幫助構(gòu)造或重組一個(gè)或多個(gè)部分,而不影響整個(gè)系統(tǒng)。換句話說,它們幫助獲得新的功能,而不改變現(xiàn)有的功能。
我們將詳細(xì)討論以下模式:適配器模式、復(fù)合模式、裝飾模式、外觀模式、享元模式和代理模式。

行為型模式

這些模式涉及改善不同對象之間的通信。
我們將詳細(xì)討論以下模式:責(zé)任鏈模式、命令模式、迭代器模式、中介者模式、觀察者模式、狀態(tài)模式、策略模式和模板方法模式。

建造者模式

這是一個(gè)基于類的創(chuàng)造性設(shè)計(jì)模式。構(gòu)造函數(shù)是一種特殊的函數(shù),可用于用該函數(shù)定義的方法和屬性實(shí)例化新對象。
它不是經(jīng)典的設(shè)計(jì)模式之一。事實(shí)上,在大多數(shù)面向?qū)ο蟮恼Z言中,它更像是一種基本的語言構(gòu)造,而不是模式。但是在JavaScript中,對象可以在沒有任何構(gòu)造函數(shù)或“類”定義的情況下動(dòng)態(tài)創(chuàng)建。因此,我認(rèn)為以這個(gè)簡單模式為其他模式打下基礎(chǔ)是很重要的。
構(gòu)造函數(shù)模式是JavaScript中最常用的模式之一,用于創(chuàng)建給定類型的新對象。
在下面的例子中,我們定義了一個(gè)類Hero,它具有name和specialAbility等屬性,以及getDetails等方法。然后,我們通過調(diào)用構(gòu)造函數(shù)方法實(shí)例化一個(gè)對象IronMan,該方法使用new關(guān)鍵字作為參數(shù)傳入相應(yīng)屬性的值。

// traditional Function-based syntax
function Hero(name, specialAbility) {
  // setting property values
  this.name = name;
  this.specialAbility = specialAbility;

  // declaring a method on the object
  this.getDetails = function() {
    return this.name + " can " + this.specialAbility;
  };
}

// ES6 Class syntax
class Hero {
  constructor(name, specialAbility) {
    // setting property values
    this._name = name;
    this._specialAbility = specialAbility;

    // declaring a method on the object
    this.getDetails = function() {
      return `${this._name} can ${this._specialAbility}`;
    };
  }
}
// creating new instances of Hero
const IronMan = new Hero("Iron Man", "fly");
console.log(IronMan.getDetails()); // Iron Man can fly
工廠模式

工廠模式是另一種基于類的創(chuàng)造模式。在這里,我們提供了一個(gè)泛型接口,它將對象實(shí)例化的責(zé)任委托給它的子類。
當(dāng)我們需要管理或操作不同但具有許多相似特征的對象集合時(shí),經(jīng)常使用這種模式。
在下面的例子中,我們創(chuàng)建了一個(gè)名為BallFactory的工廠類,它有一個(gè)接受參數(shù)的方法,根據(jù)參數(shù),它將對象實(shí)例化職責(zé)委托給相應(yīng)的類。如果類型參數(shù)是"football"或"soccer"對象實(shí)例化由Football類處理,但是如果類型參數(shù)是"basketball"對象實(shí)例化則由Basketball類處理。

class BallFactory {
  constructor() {
    this.createBall = function(type) {
      let ball;
      if (type === "football" || type === "soccer") ball = new Football();
      else if (type === "basketball") ball = new Basketball();
      ball.roll = function() {
        return `The ${this._type} is rolling.`;
      };

      return ball;
    };
  }
}

class Football {
  constructor() {
    this._type = "football";
    this.kick = function() {
      return "You kicked the football.";
    };
  }
}

class Basketball {
  constructor() {
    this._type = "basketball";
    this.bounce = function() {
      return "You bounced the basketball.";
    };
  }
}

// creating objects
const factory = new BallFactory();

const myFootball = factory.createBall("football");
const myBasketball = factory.createBall("basketball");

console.log(myFootball.roll()); // The football is rolling.
console.log(myBasketball.roll()); // The basketball is rolling.
console.log(myFootball.kick()); // You kicked the football.
console.log(myBasketball.bounce()); // You bounced the basketball.
原型模式

這是基于對象的創(chuàng)造型設(shè)計(jì)模式。在這里,我們使用現(xiàn)有對象的某種“骨架”來創(chuàng)建或?qū)嵗聦ο蟆?br>這種模式對JavaScript特別重要和有益,因?yàn)樗褂迷屠^承而不是經(jīng)典的面向?qū)ο罄^承。因此,它發(fā)揮了JavaScript的優(yōu)勢,并具有本機(jī)支持。
在下面的例子中,我們使用一個(gè)對象car作為原型,用JavaScript的Object.create特點(diǎn)創(chuàng)建另一個(gè)對象myCar。并在新對象上定義一個(gè)額外的屬性owner。

// using Object.create as was recommended by ES5 standard
const car = {
  noOfWheels: 4,
  start() {
    return "started";
  },
  stop() {
    return "stopped";
  },
};

// Object.create(proto[, propertiesObject])

const myCar = Object.create(car, { owner: { value: "John" } });

console.log(myCar.__proto__ === car); // true
適配器模式

這是一種結(jié)構(gòu)型模式,其中一個(gè)類的接口被轉(zhuǎn)換成另一個(gè)類。這種模式允許由于接口不兼容而無法協(xié)同工作的類一起工作。
此模式通常用于為新的重構(gòu)API創(chuàng)建包裝器,以便其他現(xiàn)有舊API仍然可以使用它們。這通常是在新的實(shí)現(xiàn)或代碼重構(gòu)(出于性能提高等原因)導(dǎo)致不同的公共API時(shí)完成的,而系統(tǒng)的其他部分仍在使用舊的API,需要進(jìn)行調(diào)整才能協(xié)同工作。
在下面的例子中,我們有一個(gè)舊的API,即類OldCalculator,和一個(gè)新的API,即類NewCalculator。類OldCalculator為加法和減法提供了一個(gè)operation方法,而NewCalculator為加法和減法提供了多帶帶的方法。適配器類CalcAdapter將NewCalculator包裹來將操作方法添加到面向公共的API中,同時(shí)在底層使用自己的加減法。

// old interface
class OldCalculator {
  constructor() {
    this.operations = function(term1, term2, operation) {
      switch (operation) {
        case "add":
          return term1 + term2;
        case "sub":
          return term1 - term2;
        default:
          return NaN;
      }
    };
  }
}

// new interface
class NewCalculator {
  constructor() {
    this.add = function(term1, term2) {
      return term1 + term2;
    };
    this.sub = function(term1, term2) {
      return term1 - term2;
    };
  }
}

// Adapter Class
class CalcAdapter {
  constructor() {
    const newCalc = new NewCalculator();

    this.operations = function(term1, term2, operation) {
      switch (operation) {
        case "add":
          // using the new implementation under the hood
          return newCalc.add(term1, term2);
        case "sub":
          return newCalc.sub(term1, term2);
        default:
          return NaN;
      }
    };
  }
}

// usage
const oldCalc = new OldCalculator();
console.log(oldCalc.operations(10, 5, "add")); // 15

const newCalc = new NewCalculator();
console.log(newCalc.add(10, 5)); // 15

const adaptedCalc = new CalcAdapter();
console.log(adaptedCalc.operations(10, 5, "add")); // 15;
復(fù)合模式

這是一種結(jié)構(gòu)型設(shè)計(jì)模式,它將對象組合成樹狀結(jié)構(gòu)來表示整個(gè)部分的層次結(jié)構(gòu)。在此模式中,類樹結(jié)構(gòu)中的每個(gè)節(jié)點(diǎn)可以是單個(gè)對象,也可以是對象的組合集合。無論如何,每個(gè)節(jié)點(diǎn)都被統(tǒng)一處理。


將這種模式可視化是有點(diǎn)復(fù)雜的。思考這個(gè)問題最簡單的方法是使用多層次菜單的例子。每個(gè)節(jié)點(diǎn)可以是一個(gè)不同的選項(xiàng),也可以是菜單本身,它的子節(jié)點(diǎn)有多個(gè)選項(xiàng)。帶有子組件的節(jié)點(diǎn)組件是復(fù)合組件,而沒有子組件的節(jié)點(diǎn)組件是葉子組件。
在下面的例子中,我們創(chuàng)建了一個(gè)Component基類,它實(shí)現(xiàn)了所需的公共功能,并抽象了所需的其他方法?;愡€有一個(gè)靜態(tài)方法,它使用遞歸遍歷由子類構(gòu)成的復(fù)合樹結(jié)構(gòu)。然后,我們創(chuàng)建兩個(gè)子類來擴(kuò)展基類—不包含任何子類的Leaf和可以包含子類的Composite—因此具有處理添加、搜索和刪除子功能的方法。這兩個(gè)子類用于創(chuàng)建復(fù)合結(jié)構(gòu)—在本例中是樹。

class Component {
  constructor(name) {
    this._name = name;
  }

  getNodeName() {
    return this._name;
  }

  // abstract methods that need to be overridden
  getType() {}

  addChild(component) {}

  removeChildByName(componentName) {}

  removeChildByIndex(index) {}

  getChildByName(componentName) {}

  getChildByIndex(index) {}

  noOfChildren() {}

  static logTreeStructure(root) {
    let treeStructure = "";
    function traverse(node, indent = 0) {
      treeStructure += `${"--".repeat(indent)}${node.getNodeName()}
`;
      indent++;
      for (let i = 0, length = node.noOfChildren(); i < length; i++) {
        traverse(node.getChildByIndex(i), indent);
      }
    }

    traverse(root);
    return treeStructure;
  }
}

class Leaf extends Component {
  constructor(name) {
    super(name);
    this._type = "Leaf Node";
  }

  getType() {
    return this._type;
  }

  noOfChildren() {
    return 0;
  }
}

class Composite extends Component {
  constructor(name) {
    super(name);
    this._type = "Composite Node";
    this._children = [];
  }

  getType() {
    return this._type;
  }

  addChild(component) {
    this._children = [...this._children, component];
  }

  removeChildByName(componentName) {
    this._children = [...this._children].filter(component => component.getNodeName() !== componentName);
  }

  removeChildByIndex(index) {
    this._children = [...this._children.slice(0, index), ...this._children.slice(index + 1)];
  }

  getChildByName(componentName) {
    return this._children.find(component => component.name === componentName);
  }

  getChildByIndex(index) {
    return this._children[index];
  }

  noOfChildren() {
    return this._children.length;
  }
}

// usage
const tree = new Composite("root");
tree.addChild(new Leaf("left"));
const right = new Composite("right");
tree.addChild(right);
right.addChild(new Leaf("right-left"));
const rightMid = new Composite("right-middle");
right.addChild(rightMid);
right.addChild(new Leaf("right-right"));
rightMid.addChild(new Leaf("left-end"));
rightMid.addChild(new Leaf("right-end"));

// log
console.log(Component.logTreeStructure(tree));
/*
root
--left
--right
----right-left
----right-middle
------left-end
------right-end
----right-right
*/
裝飾器模式
class Book {
  constructor(title, author, price) {
    this._title = title;
    this._author = author;
    this.price = price;
  }

  getDetails() {
    return `${this._title} by ${this._author}`;
  }
}

// decorator 1
function giftWrap(book) {
  book.isGiftWrapped = true;
  book.unwrap = function() {
    return `Unwrapped ${book.getDetails()}`;
  };

  return book;
}

// decorator 2
function hardbindBook(book) {
  book.isHardbound = true;
  book.price += 5;
  return book;
}

// usage
const alchemist = giftWrap(new Book("The Alchemist", "Paulo Coelho", 10));

console.log(alchemist.isGiftWrapped); // true
console.log(alchemist.unwrap()); // "Unwrapped The Alchemist by Paulo Coelho"

const inferno = hardbindBook(new Book("Inferno", "Dan Brown", 15));

console.log(inferno.isHardbound); // true
console.log(inferno.price); // 20
外觀模式

這是一種在JavaScript庫中廣泛使用的結(jié)構(gòu)設(shè)計(jì)模式。它用于提供一個(gè)統(tǒng)一的、更簡單的、面向公眾的接口,以便于使用,從而避免了其組成子系統(tǒng)或子類的復(fù)雜性。
這種模式的使用在jQuery這樣的庫中非常常見。
在這個(gè)例子中,我們創(chuàng)建了一個(gè)面向公共的API,其中包含了一個(gè)類ComplaintRegistry。它只公開客戶端要使用的一種方法,即registerComplaint。它根據(jù)類型參數(shù)內(nèi)部處理ProductComplaint或ServiceComplaint所需對象的實(shí)例化。它還處理所有其他復(fù)雜的功能,如生成唯一的ID、將complaints存儲(chǔ)在內(nèi)存中等等。但是,使用外觀模式隱藏了所有這些復(fù)雜性。

let currentId = 0;
class ComplaintRegistry {
  registerComplaint(customer, type, details) {
    const id = ComplaintRegistry._uniqueIdGenerator();
    let registry;
    if (type === "service") {
      registry = new ServiceComplaints();
    } else {
      registry = new ProductComplaints();
    }
    return registry.addComplaint({ id, customer, details });
  }

  static _uniqueIdGenerator() {
    return ++currentId;
  }
}

class Complaints {
  constructor() {
    this.complaints = [];
  }

  addComplaint(complaint) {
    this.complaints.push(complaint);
    return this.replyMessage(complaint);
  }

  getComplaint(id) {
    return this.complaints.find(complaint => complaint.id === id);
  }

  replyMessage(complaint) {}
}

class ProductComplaints extends Complaints {
  constructor() {
    super();
    if (ProductComplaints.exists) {
      return ProductComplaints.instance;
    }
    ProductComplaints.instance = this;
    ProductComplaints.exists = true;
    return this;
  }

  replyMessage({ id, customer, details }) {
    return `Complaint No. ${id} reported by ${customer} regarding ${details} have been filed with the Products Complaint Department. Replacement/Repairment of the product as per terms and conditions will be carried out soon.`;
  }
}

class ServiceComplaints extends Complaints {
  constructor() {
    super();
    if (ServiceComplaints.exists) {
      return ServiceComplaints.instance;
    }
    ServiceComplaints.instance = this;
    ServiceComplaints.exists = true;
    return this;
  }

  replyMessage({ id, customer, details }) {
    return `Complaint No. ${id} reported by ${customer} regarding ${details} have been filed with the Service Complaint Department. The issue will be resolved or the purchase will be refunded as per terms and conditions.`;
  }
}

// usage
const registry = new ComplaintRegistry();

const reportService = registry.registerComplaint("Martha", "service", "availability");
// "Complaint No. 1 reported by Martha regarding availability have been filed with the Service Complaint Department. The issue will be resolved or the purchase will be refunded as per terms and conditions."

const reportProduct = registry.registerComplaint("Jane", "product", "faded color");
// "Complaint No. 2 reported by Jane regarding faded color have been filed with the Products Complaint Department. Replacement/Repairment of the product as per terms and conditions will be carried out soon."
享元模式

這是一種結(jié)構(gòu)型設(shè)計(jì)模式,專注于通過細(xì)粒度對象進(jìn)行有效的數(shù)據(jù)共享。 它用于提高效率和記憶保存目的。
此模式可用于任何類型的緩存目的。 事實(shí)上,現(xiàn)代瀏覽器使用享元模式的變體來防止兩次加載相同的圖像。
在下面的例子中,我們創(chuàng)建了一個(gè)細(xì)粒度的享元類Icecream,用于分享有關(guān)冰淇淋口味的數(shù)據(jù),以及一個(gè)工廠級(jí)的IcecreamFactory來創(chuàng)建這些享元類對象。 對于內(nèi)存保留,如果同一對象被實(shí)例化兩次,則對象將被回收。
這是享元實(shí)現(xiàn)的一個(gè)簡單示例。

// flyweight class
class Icecream {
  constructor(flavour, price) {
    this.flavour = flavour;
    this.price = price;
  }
}

// factory for flyweight objects
class IcecreamFactory {
  constructor() {
    this._icecreams = [];
  }

  createIcecream(flavour, price) {
    let icecream = this.getIcecream(flavour);
    if (icecream) {
      return icecream;
    } else {
      const newIcecream = new Icecream(flavour, price);
      this._icecreams.push(newIcecream);
      return newIcecream;
    }
  }

  getIcecream(flavour) {
    return this._icecreams.find(icecream => icecream.flavour === flavour);
  }
}

// usage
const factory = new IcecreamFactory();

const chocoVanilla = factory.createIcecream("chocolate and vanilla", 15);
const vanillaChoco = factory.createIcecream("chocolate and vanilla", 15);

// reference to the same object
console.log(chocoVanilla === vanillaChoco); // true
代理模式

這是一種結(jié)構(gòu)型設(shè)計(jì)模式,其行為完全符合其名稱。它充當(dāng)另一個(gè)對象的代理或占位符來控制對它的訪問。
它通常用于目標(biāo)對象受到約束且可能無法有效地處理其所有職責(zé)的情況。在這種情況下,代理通常向客戶機(jī)提供相同的接口,并添加一個(gè)間接層,以支持對目標(biāo)對象的受控訪問,以避免對目標(biāo)對象施加過大的壓力。
在處理網(wǎng)絡(luò)請求較多的應(yīng)用程序時(shí),代理模式非常有用,可以避免不必要的或冗余的網(wǎng)絡(luò)請求。
在下面的例子中,我們將使用兩個(gè)新的ES6特性,Proxy和Reflect。代理對象用于為JavaScript對象的基本操作定義自定義行為(記住,函數(shù)和數(shù)組也是JavaScript中的對象)。它是一個(gè)可用于創(chuàng)建對象Proxy的構(gòu)造函數(shù)方法。它接受要代理的對象target和定義必要定制的handler對象。handler對象允許定義一些陷阱函數(shù),如get、set、has、apply等,這些函數(shù)用于將添加附加到它們的用法中的自定義行為。另一方面,Reflect是一個(gè)內(nèi)置對象,它提供類似于Proxy的handler對象支持的方法作為靜態(tài)方法。它不是構(gòu)造函數(shù); 其靜態(tài)方法用于可攔截的JavaScript操作。
現(xiàn)在,我們創(chuàng)建一個(gè)函數(shù),它可以看作是一個(gè)網(wǎng)絡(luò)請求。我們將其命名為networkFetch。它接受一個(gè)URL并相應(yīng)地作出響應(yīng)。我們希望實(shí)現(xiàn)一個(gè)代理,在這個(gè)代理中,只有在緩存中沒有網(wǎng)絡(luò)響應(yīng)時(shí),我們才能從網(wǎng)絡(luò)獲得響應(yīng)。否則,我們只從緩存返回一個(gè)響應(yīng)。
全局變量cache將存儲(chǔ)緩存的響應(yīng)。我們創(chuàng)建了一個(gè)名為proxiedNetworkFetch的代理,將原始networkFetch作為target,并在對象handler中使用apply方法來代理函數(shù)調(diào)用。apply方法在對象target本身上傳遞。這個(gè)值作為thisArg,參數(shù)以類似數(shù)組的結(jié)構(gòu)args傳遞給它。
我們檢查傳遞的url參數(shù)是否在緩存中。如果它存在于緩存中,我們將從那里返回響應(yīng),而不會(huì)調(diào)用原始目標(biāo)函數(shù)。如果沒有,那么我們使用Reflect.apply方法用thisArg(盡管在我們的例子中它沒有任何意義)來調(diào)用函數(shù)target和它傳遞的參數(shù)。

// Target
function networkFetch(url) {
  return `${url} - Response from network`;
}

// Proxy
// ES6 Proxy API = new Proxy(target, handler);
const cache = [];
const proxiedNetworkFetch = new Proxy(networkFetch, {
  apply(target, thisArg, args) {
    const urlParam = args[0];
    if (cache.includes(urlParam)) {
      return `${urlParam} - Response from cache`;
    } else {
      cache.push(urlParam);
      return Reflect.apply(target, thisArg, args);
    }
  },
});

// usage
console.log(proxiedNetworkFetch("dogPic.jpg")); // "dogPic.jpg - Response from network"
console.log(proxiedNetworkFetch("dogPic.jpg")); // "dogPic.jpg - Response from cache"
責(zé)任鏈模式

這是一種提供松散耦合對象鏈的行為型設(shè)計(jì)模式。這些對象中的每一個(gè)都可以選擇處理客戶機(jī)的請求。
責(zé)任鏈模式的一個(gè)很好的例子是DOM中的事件冒泡機(jī)制,其中一個(gè)事件通過一系列嵌套的DOM元素傳播,其中一個(gè)元素可能附加了一個(gè)“事件偵聽器”來偵聽該事件并對其進(jìn)行操作。
在下面的例子中,我們創(chuàng)建了一個(gè)CumulativeSum,可以使用一個(gè)可選的initialValue進(jìn)行實(shí)例化。它有一個(gè)方法add,將傳遞的值添加到對象的sum屬性中,并返回object本身,以允許鏈接add方法調(diào)用。
在jQuery中也可以看到這種常見的模式,對jQuery對象的幾乎任何方法調(diào)用都會(huì)返回一個(gè)jQuery對象,以便將方法調(diào)用鏈接在一起。

class CumulativeSum {
  constructor(intialValue = 0) {
    this.sum = intialValue;
  }

  add(value) {
    this.sum += value;
    return this;
  }
}

// usage
const sum1 = new CumulativeSum();
console.log(sum1.add(10).add(2).add(50).sum); // 62


const sum2 = new CumulativeSum(10);
console.log(sum2.add(10).add(20).add(5).sum); // 45
命令模式

這是一種行為型設(shè)計(jì)模式,旨在將操作或操作封裝為對象。 此模式允許通過將請求操作或調(diào)用方法的對象與執(zhí)行或處理實(shí)際實(shí)現(xiàn)的對象分離來松散耦合系統(tǒng)和類。
剪貼板交互API有點(diǎn)類似于命令模式。 如果您是Redux用戶,則您已經(jīng)遇到過命令模式。 使我們能夠回到之前的時(shí)間節(jié)點(diǎn)的操作不過就是把可以跟蹤以重做或撤消操作封裝起來。 因此,我們實(shí)現(xiàn)了時(shí)間旅行式調(diào)試。
在下面的例子中,我們有一個(gè)名為SpecialMath的類,它有多個(gè)方法和一個(gè)Command類,它封裝了要在其主題上執(zhí)行的命令,即SpecialMath類的一個(gè)對象。Command類還跟蹤所有已執(zhí)行的命令,這些命令可用于擴(kuò)展其功能以包括撤消和重做類型操作。

class SpecialMath {
  constructor(num) {
    this._num = num;
  }

  square() {
    return this._num ** 2;
  }

  cube() {
    return this._num ** 3;
  }

  squareRoot() {
    return Math.sqrt(this._num);
  }
}

class Command {
  constructor(subject) {
    this._subject = subject;
    this.commandsExecuted = [];
  }
  execute(command) {
    this.commandsExecuted.push(command);
    return this._subject[command]();
  }
}

// usage
const x = new Command(new SpecialMath(5));
x.execute("square");
x.execute("cube");

console.log(x.commandsExecuted); // ["square", "cube"]
迭代器模式

它是一種行為型設(shè)計(jì)模式,提供了一種方法來順序訪問聚合對象的各個(gè)元素,而無需暴露其內(nèi)部表示。
迭代器有一種特殊的行為,在這種行為中,我們通過調(diào)用next()一次遍歷一組有序的值,直到到達(dá)末尾。在ES6中引入迭代器和生成器使得迭代器模式的實(shí)現(xiàn)非常簡單。
下面有兩個(gè)例子。首先,一個(gè)IteratorClass使用iterator規(guī)范,而另一個(gè)iteratorUsingGenerator使用生成器函數(shù)。
Symbol.iterator(Symbol-一種新的基本數(shù)據(jù)類型)用于指定對象的默認(rèn)迭代器。它必須被定義為一個(gè)集合,以便能夠使用for...of的循環(huán)結(jié)構(gòu)。在第一個(gè)示例中,我們定義構(gòu)造函數(shù)來存儲(chǔ)一些數(shù)據(jù)集合,然后定義符號(hào)Symbol.iterator,它返回一個(gè)對象和用于迭代的next方法。
對于第二種情況,我們定義了一個(gè)生成器函數(shù),將數(shù)據(jù)數(shù)組傳遞給它,并使用next和yield迭代地返回它的元素。生成器函數(shù)是一種特殊類型的函數(shù),它充當(dāng)?shù)鞯墓S,可以迭代地顯式維護(hù)自己的內(nèi)部狀態(tài)和生成值。它可以暫停并恢復(fù)自己的執(zhí)行周期。

中介者模式

它是一種行為型設(shè)計(jì)模式,封裝了一組對象如何相互交互。它通過促進(jìn)松散耦合,防止對象彼此顯式引用,從而為一組對象提供了中央權(quán)限。
在下面的例子中,我們使用TrafficTower作為中介來控制Airplane對象之間的交互方式。所有Airplane對象都將自己注冊到一個(gè)TrafficTower對象中,而中介類對象處理Airplane對象如何接收所有其他Airplane對象的坐標(biāo)數(shù)據(jù)。

class TrafficTower {
  constructor() {
    this._airplanes = [];
  }

  register(airplane) {
    this._airplanes.push(airplane);
    airplane.register(this);
  }

  requestCoordinates(airplane) {
    return this._airplanes.filter(plane => airplane !== plane).map(plane => plane.coordinates);
  }
}

class Airplane {
  constructor(coordinates) {
    this.coordinates = coordinates;
    this.trafficTower = null;
  }

  register(trafficTower) {
    this.trafficTower = trafficTower;
  }

  requestCoordinates() {
    if (this.trafficTower) return this.trafficTower.requestCoordinates(this);
    return null;
  }
}

// usage
const tower = new TrafficTower();

const airplanes = [new Airplane(10), new Airplane(20), new Airplane(30)];
airplanes.forEach(airplane => {
  tower.register(airplane);
});

console.log(airplanes.map(airplane => airplane.requestCoordinates())) 
// [[20, 30], [10, 30], [10, 20]]
觀察者模式

它是一個(gè)至關(guān)重要的行為型設(shè)計(jì)模式,定義了對象之間一對多的依賴關(guān)系,這樣當(dāng)一個(gè)對象(發(fā)布方)更改其狀態(tài)時(shí),所有其他依賴對象(訂閱者)都會(huì)得到通知并自動(dòng)更新。這也稱為PubSub(發(fā)布者/訂閱者)或事件分派器/偵聽器模式。發(fā)布者有時(shí)稱為主題,訂閱者有時(shí)稱為觀察者。
如果您曾經(jīng)使用addEventListener或jQuery的.on編寫過偶數(shù)處理代碼,那么您可能已經(jīng)對這種模式有些熟悉了。它在反應(yīng)性編程(就像RxJS)中也有影響。
在本例中,我們創(chuàng)建了一個(gè)簡單的Subject類,該類具有從訂閱者集合中添加和刪除Observer類對象的方法。此外,還提供一個(gè)fire方法,用于將Subject類對象中的任何更改傳播給訂閱的觀察者。另一方面,Observer類有自己的內(nèi)部狀態(tài),以及基于訂閱的Subject傳播的更改更新內(nèi)部狀態(tài)的方法。

狀態(tài)模式

是一種行為型設(shè)計(jì)模式,允許對象根據(jù)其內(nèi)部狀態(tài)的變化來改變其行為。狀態(tài)模式類返回的對象似乎改變了它的類。它為有限的一組對象提供特定于狀態(tài)的邏輯,其中每個(gè)對象類型表示特定的狀態(tài)。
我們將以交通燈為例來理解這種模式。TrafficLight類根據(jù)其內(nèi)部狀態(tài)(Red、Yellow或Green類的對象)更改返回的對象。

策略模式

它是一種行為型設(shè)計(jì)模式,允許為特定任務(wù)封裝替代算法。它定義了一系列算法,并以一種可以在運(yùn)行時(shí)互換的方式封裝它們,而不需要客戶機(jī)的干涉或知識(shí)。
在下面的示例中,我們創(chuàng)建了一個(gè)Commute類,用于封裝所有可能的通勤策略。然后,我們定義了三種策略,即Bus、PersonalCar和Taxi。使用此模式,我們可以在運(yùn)行時(shí)將實(shí)現(xiàn)替換為Commute對象的travel方法。

模板方法模式

這是一種行為型設(shè)計(jì)模式,基于定義算法的框架或操作的實(shí)現(xiàn),但將一些步驟推遲到子類。它允許子類在不改變算法外部結(jié)構(gòu)的情況下重新定義算法的某些步驟。
在本例中,我們有一個(gè)模板類Employee,它部分實(shí)現(xiàn)了work方法。子類實(shí)現(xiàn)職責(zé)方法是為了使它作為一個(gè)整體發(fā)揮作用。然后,我們創(chuàng)建了兩個(gè)子類Developer和Tester,它們擴(kuò)展了模板類并實(shí)現(xiàn)了所需的方法。

總結(jié)

設(shè)計(jì)模式對軟件工程至關(guān)重要,也對我們解決常見問題非常有幫助。 但這是一個(gè)非常廣泛的主題,并且根本不可能在短短的一篇文章中囊括關(guān)于它們的所有內(nèi)容。 因此,我選擇簡短而簡潔地談?wù)撐艺J(rèn)為在編寫現(xiàn)代JavaScript時(shí)非常方便的那些。 為了深入了解,我建議你看一下這些書:
《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》,作者:Erich Gamma,Richard Helm,Ralph Johnson和John Vlissides(即“四人幫”)
《JavaScript設(shè)計(jì)模式》,作者:Addy Osmani
《JavaScript模式》,作者:Stoyan Stefanov
《JavaScript模式》,作者:Stoyan Stefanov

?? 看之后

點(diǎn)贊,讓更多的人也能看到這篇內(nèi)容(收藏不點(diǎn)贊,都是耍流氓 -_-)

關(guān)注公眾號(hào)「新前端社區(qū)」,號(hào)享受文章首發(fā)體驗(yàn)!每周重點(diǎn)攻克一個(gè)前端技術(shù)難點(diǎn)。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/106611.html

相關(guān)文章

  • 這些Spring中設(shè)計(jì)模式,你都知道?

    摘要:簡單工廠模式的實(shí)質(zhì)是由一個(gè)工廠類根據(jù)傳入的參數(shù),動(dòng)態(tài)決定應(yīng)該創(chuàng)建哪一個(gè)產(chǎn)品類。中的就是簡單工廠模式的體現(xiàn),根據(jù)傳入一個(gè)唯一的標(biāo)識(shí)來獲得對象,但是否是在傳入?yún)?shù)后創(chuàng)建還是傳入?yún)?shù)前創(chuàng)建這個(gè)要根據(jù)具體情況來定。 設(shè)計(jì)模式作為工作學(xué)習(xí)中的枕邊書,卻時(shí)常處于勤說不用的尷尬境地,也不是我們時(shí)常忘記,只是一直沒有記憶。 Spring作為業(yè)界的經(jīng)典框架,無論是在架構(gòu)設(shè)計(jì)方面,還是在代碼編寫方面,都堪...

    LeviDing 評(píng)論0 收藏0
  • 這些Spring中設(shè)計(jì)模式,你都知道

    摘要:簡單工廠模式的實(shí)質(zhì)是由一個(gè)工廠類根據(jù)傳入的參數(shù),動(dòng)態(tài)決定應(yīng)該創(chuàng)建哪一個(gè)產(chǎn)品類。中的就是簡單工廠模式的體現(xiàn),根據(jù)傳入一個(gè)唯一的標(biāo)識(shí)來獲得對象,但是否是在傳入?yún)?shù)后創(chuàng)建還是傳入?yún)?shù)前創(chuàng)建這個(gè)要根據(jù)具體情況來定。 設(shè)計(jì)模式作為工作學(xué)習(xí)中的枕邊書,卻時(shí)常處于勤說不用的尷尬境地,也不是我們時(shí)常忘記,只是一直沒有記憶。 Spring作為業(yè)界的經(jīng)典框架,無論是在架構(gòu)設(shè)計(jì)方面,還是在代碼編寫方面,都堪...

    hsluoyz 評(píng)論0 收藏0
  • 前端這幾個(gè)webApi你都知道

    摘要:跟相反,該元素可以向上查詢,也就是可以查詢到父元素就跟原生微信小程序一樣,能獲取標(biāo)簽上以為前綴的屬性集合蜘蛛俠蜘蛛俠注意這是開發(fā)規(guī)范問題,凡是自定義屬性都要加上前綴假設(shè)瀏覽器的參數(shù)是蜘蛛俠蜘蛛俠這是一個(gè)對象,該對象里封裝了許多操作元 closest 跟querySelector相反,該元素可以向上查詢,也就是可以查詢到父元素: document.querySelector(li...

    libin19890520 評(píng)論0 收藏0
  • 2019前端面試那些事兒

    摘要:雖然今年沒有換工作的打算但為了跟上時(shí)代的腳步還是忍不住整理了一份最新前端知識(shí)點(diǎn)知識(shí)點(diǎn)匯總新特性,語義化瀏覽器的標(biāo)準(zhǔn)模式和怪異模式和的區(qū)別使用的好處標(biāo)簽廢棄的標(biāo)簽,和一些定位寫法放置位置和原因什么是漸進(jìn)式渲染模板語言原理盒模型,新特性,偽 雖然今年沒有換工作的打算 但為了跟上時(shí)代的腳步 還是忍不住整理了一份最新前端知識(shí)點(diǎn) 知識(shí)點(diǎn)匯總1.HTMLHTML5新特性,語義化瀏覽器的標(biāo)準(zhǔn)模式和怪...

    JeOam 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<