[Design Pattern] Factory 模式

假設我們今天要開一間玩具工廠,工廠生產的玩具有:金剛、哥吉拉,寫法可能像這樣:

Program.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*Program.cs*/

using System;
using DemoCode.DesignPattern.Factory;

namespace DemoCode
{
class Program
{
static void Main(string[] args)
{
IToy toy1 = new Gozilla();
IToy toy2 = new KingKong();

Console.WriteLine($"Toy I made: {toy1.ToyName}");
Console.WriteLine($"Toy I made: {toy2.ToyName}");
}
}
}

IToy.cs

1
2
3
4
5
6
7
8
9
/*IToy.cs*/

namespace DemoCode.DesignPattern.Factory
{
public interface IToy
{
public string ToyName { get; set; }
}
}

Gozilla.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*Gozilla.cs*/

namespace DemoCode.DesignPattern.Factory
{
public class Gozilla : IToy
{
public string ToyName { get; set; }

public Gozilla()
{
ToyName = "Gozilla";
}
}
}

KingKong.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*KingKong.cs*/

namespace DemoCode.DesignPattern.Factory
{
public class KingKong : IToy
{
public string ToyName { get; set; }

public KingKong()
{
ToyName = "KingKong";
}
}
}

架構圖會像是這樣子,我們會在 Program 裡直接建立 KingKongGozilla 執行個體,如果這個類別不常變更,其實直接建立是沒有問題的,但如果是在開發階段,我們應當依賴於抽象介面,避免受類別變化帶來的影響。


Factory 模式示範

DIP 原則告訴我們:應該優先依賴於抽象類別,避免依賴於實體類別。
所以我們新增一個工廠介面 IFactory,以及實作該介面的類別 Factory,藉由這個工廠來幫我們生產玩具:

Program.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*Program.cs*/

using System;
using DemoCode.DesignPattern.Factory;

namespace DemoCode
{
class Program
{
static void Main(string[] args)
{
IFactory factory = new Factory();

IToy toy1 = factory.Make("KingKong");
IToy toy2 = factory.Make("Gozilla");

Console.WriteLine($"Toy I made is: {toy1.ToyName}");
Console.WriteLine($"Toy I made is: {toy2.ToyName}");
}
}
}

IFactory.cs

1
2
3
4
5
6
7
8
9
/*IFactory.cs*/

namespace DemoCode.DesignPattern.Factory
{
public interface IFactory
{
IToy Make(string toyName);
}
}

Factory.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*Factory.cs*/
using System;

namespace DemoCode.DesignPattern.Factory
{
public class Factory : IFactory
{
public Factory()
{
}

public IToy Make(string toyName)
{
switch (toyName)
{
case "Gozilla":
return new Gozilla();

case "KingKong":
return new KingKong();

default:
throw new ApplicationException($"沒有生產該玩具: {toyName}");
}
}
}
}

我們在 Program 中建立一個 Factory 的類別實作,並將製作玩具的工作交給 Factory,這樣的話職責就很明確,工廠專職製造玩具,Program 只需直接使用 Factory 所回傳的類別實作。

完整程式碼請參考

結論

Factory 模式使得高層模組不依賴於類別的實作,並遵守 DIP 原則,在程式的職責管理上也較為清楚明白。但如果你的類別是不常變更且穩定的,那也不需使用 Factory 模式,直接建立類別實作會省事許多。

參考

Factory 模式.無瑕的程式碼 敏捷完整篇:物件導向原則、設計模式與 C# 實踐

Comments