1. 程式人生 > 實用技巧 >10行實現最短路演算法——Dijkstra

10行實現最短路演算法——Dijkstra

工廠方法模式

工廠方法模式Factory Method Pattern又稱為工廠模式,也叫虛擬構造器Virtual Constructor模式或者多型工廠Polymorphic Factory模式,它屬於類建立型模式,在工廠方法模式中,工廠父類負責定義建立產品物件的公共介面,而工廠子類則負責生成具體的產品物件,這樣做的目的是將產品類的例項化操作延遲到工廠子類中完成,即通過工廠子類來確定究竟應該例項化哪一個具體產品類。

描述

簡單工廠模式有一個問題,類的建立依賴工廠類,也就是說如果想要拓展程式,必須對工廠類進行修改,而工廠方法模式就是將具體的建立過程交給專門的工廠子類去完成,即將產品類的例項化操作延遲到工廠子類中完成,由此可以不必再去修改工廠類,而只需要定義實現工廠的子類即可。

模式分析

工廠方法模式是簡單工廠模式的進一步抽象和推廣。由於使用了面向物件的多型性,工廠方法模式保持了簡單工廠模式的優點,而且克服了它的缺點。在工廠方法模式中,核心的工廠類不再負責所有產品的建立,而是將具體建立工作交給子類去做。這個核心類僅僅負責給出具體工廠必須實現的介面,而不負責哪一個產品類被例項化這種細節,這使得工廠方法模式可以允許系統在不修改工廠角色的情況下引進新產品。

模式結構

  • Product:抽象產品。
  • ConcreteProduct:具體產品。
  • Factory:抽象工廠。
  • ConcreteFactory:具體工廠。

優點

  • 在工廠方法模式中,工廠方法用來建立客戶所需要的產品,同時還向客戶隱藏了哪種具體產品類將被例項化這一細節,使用者只需要關心所需產品對應的工廠,無須關心建立細節,甚至無須知道具體產品類的類名。
  • 基於工廠角色和產品角色的多型性設計是工廠方法模式的關鍵。它能夠使工廠可以自主確定建立何種產品物件,而如何建立這個物件的細節則完全封裝在具體工廠內部。工廠方法模式之所以又被稱為多型工廠模式,是因為所有的具體工廠類都具有同一抽象父類。
  • 使用工廠方法模式的另一個優點是在系統中加入新產品時,無須修改抽象工廠和抽象產品提供的介面,無須修改客戶端,也無須修改其他的具體工廠和具體產品,而只要新增一個具體工廠和具體產品就可以了。這樣,系統的可擴充套件性也就變得非常好,完全符合“開閉原則”。

缺點

  • 在新增新產品時,需要編寫新的具體產品類,而且還要提供與之對應的具體工廠類,系統中類的個數將成對增加,在一定程度上增加了系統的複雜度,有更多的類需要編譯和執行,會給系統帶來一些額外的開銷。
  • 由於考慮到系統的可擴充套件性,需要引入抽象層,在客戶端程式碼中均使用抽象層進行定義,增加了系統的抽象性和理解難度,且在實現時可能需要用到反射等技術,增加了系統的實現難度。

實現

工廠方法模式的本意是將實際建立物件的工作推遲到子類中,這樣核心類就變成了抽象類,但是在JavaScript中很難像傳統面向物件那樣去實現建立抽象類,所以在JavaScript中只需要參考其核心思想即可。

class Shape { // 產品基類
    say(){
        console.log(this.name);
    }
}

class Rectangle extends Shape{ // 長方形產品
    constructor(){
        super();
        this.name = "Rectangle";
    }
}

class Square extends Shape{ // 正方形產品
    constructor(){
        super();
        this.name = "Square";
    }
}

class Circle extends Shape{ // 圓形產品
    constructor(){
        super();
        this.name = "Circle";
    }
}

class Factory{ // 工廠基類
    getProduct(){}
}

class RectangleFactory extends Factory{
    constructor(){
        super();
    }

    getProduct(){
        return new Rectangle();
    }
}

class SquareFactory extends Factory{
    constructor(){
        super();
    }

    getProduct(){
        return new Square();
    }
}

class CircleFactory extends Factory{
    constructor(){
        super();
    }

    getProduct(){
        return new Circle();
    }
}


var rectangle = (new RectangleFactory).getProduct();
rectangle.say(); // Rectangle

var square = (new SquareFactory).getProduct();
square.say(); // Square

var circle = (new CircleFactory).getProduct();
circle.say(); // Circle
// 組裝圖形的例子 // 假如需要組裝三個圖形
class Shape { // 形狀基類
    say(){
        console.log(this.name);
    }
}

class Rectangle extends Shape{ // 長方形
    constructor(){
        super();
        this.name = "Rectangle";
    }
}

class Square extends Shape{ // 正方形
    constructor(){
        super();
        this.name = "Square";
    }
}

class Circle extends Shape{ // 圓形
    constructor(){
        super();
        this.name = "Circle";
    }
}

class Combination{
    constructor(rectangle, square, circle){
        console.log(`Combination: ${rectangle.name} ${square.name} ${circle.name}`);
    }
}

// 直接組裝 呼叫者只需要一個組合完成的圖形,而為了組裝需要分別例項化三個形狀,耦合度太高
(function(){
    var rectangle = new Rectangle();
    var square = new Square();
    var circle = new Circle();
    new Combination(rectangle, square, circle);
})();

// 使用工廠 隱藏具體實現細節 降低耦合度
class CombinationFactory{
    getProduct(){
        var rectangle = new Rectangle();
        var square = new Square();
        var circle = new Circle();
        return new Combination(rectangle, square, circle); 
    }
}

(function(){
    var combination = (new CombinationFactory).getProduct(); 
})();

每日一題

https://github.com/WindrunnerMax/EveryDay

參考

https://blog.csdn.net/carson_ho/article/details/52343584
https://wiki.jikexueyuan.com/project/java-design-pattern/factory-pattern.html
https://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/factory_method.html