一個菜鳥的設計模式之旅,文章可能會有不對的地方,懇請大佬指出錯誤。
程式設計旅途是漫長遙遠的,在不同時刻有不同的感悟,本文會一直更新下去。
由於 Go 中缺少類和繼承等 OOP 特性, 所以無法使用 Go 來實現經典的工廠方法模式。 不過, 我們仍然能實現模式的基礎版本, 即簡單工廠。案例中使用工廠結構體來構建多種型別的武器。因此工廠方法模式程式碼使用 C# 表示。
Gun: AK47 gun
Power: 4
Gun: Musket gun
Power: 1
首先, 建立一個名為 iGun
的介面, 其中將定義一支槍所需具備的所有方法。 然後是實現了 iGun 介面的 gun
槍支結構體型別。 兩種具體的槍支——ak47
與 musket
火槍 ——兩者都嵌入了槍支結構體, 且間接實現了所有的 iGun
方法。
gunFactory
槍支工廠結構體將發揮工廠的作用, 即通過傳入引數構建所需型別的槍支。 main.go 則扮演著使用者端的角色。 其不會直接與 ak47
或 musket
進行互動, 而是依靠 gunFactory
來建立多種槍支的範例, 僅使用字元引數來控制生產。
package main
type IGun interface {
setName(name string)
setPower(power int)
getName() string
getPower() int
}
package main
type Gun struct {
name string
power int
}
func (g *Gun) setName(name string) {
g.name = name
}
func (g *Gun) getName() string {
return g.name
}
func (g *Gun) setPower(power int) {
g.power = power
}
func (g *Gun) getPower() int {
return g.power
}
package main
type Ak47 struct {
Gun
}
func newAk47() IGun {
return &Ak47{
Gun: Gun{
name: "AK47 gun",
power: 4,
},
}
}
package main
type musket struct {
Gun
}
func newMusket() IGun {
return &musket{
Gun: Gun{
name: "Musket gun",
power: 1,
},
}
}
package main
import "fmt"
func getGun(gunType string) (IGun, error) {
if gunType == "ak47" {
return newAk47(), nil
}
if gunType == "musket" {
return newMusket(), nil
}
return nil, fmt.Errorf("Wrong gun type passed")
}
package main
import "fmt"
func main() {
ak47, _ := getGun("ak47")
musket, _ := getGun("musket")
printDetails(ak47)
printDetails(musket)
}
func printDetails(g IGun) {
fmt.Printf("Gun: %s", g.getName())
fmt.Println()
fmt.Printf("Power: %d", g.getPower())
fmt.Println()
}
Gun: AK47 gun
Power: 4
Gun: Musket gun
Power: 1
程式功能和簡單工廠相同,用工廠方法模式實現。
namespace 工廠方法;
public abstract class Gun
{
protected Gun(string name, int power)
{
Name = name;
Power = power;
}
public string Name { get; private set; }
public int Power { get; private set; }
public void setName(string name)
{
Name = name;
}
public void setPower(int power)
{
Power = power;
}
}
public class AK47 : Gun
{
public AK47() : base("ak47", 10)
{
}
}
public class Musket : Gun
{
public Musket() : base("musket", 5)
{
}
}
namespace 工廠方法;
public interface GunFactory
{
Gun createGun();
}
public class AK47Factory : GunFactory
{
public Gun createGun()
{
Console.WriteLine("正在生產AK47");
return new AK47();
}
}
public class MusketFactory : GunFactory
{
public Gun createGun()
{
Console.WriteLine("正在生產Musket");
return new Musket();
}
}
using 工廠方法;
GunFactory gunFactory = new AK47Factory();
Gun ak47_1 = gunFactory.createGun();
Gun ak47_2 = gunFactory.createGun();
gunFactory = new MusketFactory();
Gun Musket_1 = gunFactory.createGun();
List<Gun> guns = new List<Gun>() { ak47_1, ak47_2, Musket_1 };
foreach (Gun gun in guns)
{
Console.WriteLine($"武器名字:{gun.Name}");
Console.WriteLine($"武器傷害:{gun.Power}");
}
正在生產AK47
正在生產AK47
正在生產Musket
武器名字:ak47
武器傷害:10
武器名字:ak47
武器傷害:10
武器名字:musket
武器傷害:5
簡單工廠模式也是工廠模式的一種,但不屬於23種設計模式。
目的使使用者端與產品解耦。將產品建立範例過程從使用者端程式碼中獨立出去。生成具體物件的邏輯判斷也從使用者端分離至簡單工廠類中。
簡單工廠模式的最大優點在於工廠類中包含了必要的邏輯判斷,根據使用者端的選擇條件動態範例化相關的類,對於使用者端來說,去除了與具體產品的依賴。
但簡單工廠違背了開放-封閉原則
,導致每次新增新類別的時候,都需要去修改工廠類的分支case判斷,可以用反射動態生成範例解決。
簡單工廠反射案例:我的設計模式之旅、01 策略模式、簡單工廠、反射 - 小能日記
工廠方法是一種建立型設計模式, 其在父類別中提供一個建立物件的方法,允許子類決定範例化物件的型別。
工廠方法模式:定義一個用於建立物件的介面,讓子類決定範例化哪一個類。工廠方法使一個類的範例化延遲到子類。
工廠方法類 (建立者類):主要職責並非是建立產品。其中通常會包含一些核心業務邏輯,這些邏輯依賴於由工廠方法返回的產品物件。子類可通過重寫工廠方法並使其返回不同型別的產品來間接修改業務邏輯。
主要解決:主要解決介面選擇的問題。使用者端將所有產品視為抽象的介面,使用者端知道所有產品都提供該介面要求的方法,並不關心其具體實現方式。
何時使用:
如何解決:讓其子類實現工廠介面,返回的也是一個抽象的產品。使用特殊的工廠方法代替對於物件建構函式new的直接呼叫。
實現步驟:
關鍵程式碼:建立過程在其子類執行。
應用範例:
優點:
缺點:每次增加一個產品時,都需要增加一個具體類和物件實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加了系統具體類的依賴。這並不是什麼好事。
使用場景:
注意事項:
作為一種建立類模式,在任何需要生成複雜物件的地方,都可以使用工廠方法模式。有一點需要注意的地方就是複雜物件適合使用工廠模式,而簡單物件,特別是只需要通過 new 就可以完成建立的物件,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統的複雜度。
建立物件的邏輯判斷依舊在使用者端中實現。
與其他模式的關係:
首先,你需要建立儲存空間來存放所有已經建立的物件。
當他人請求一個物件時,程式將在物件池中搜尋可用物件。
然後將其返回給使用者端程式碼。
如果沒有可用物件,程式則建立一個新物件(並將其新增到物件池中)。
四個步驟的程式碼必須位於同一處,確保重複程式碼不會汙染程式!