昨天我們談到了 Factory模式,工廠可以生產實作同個介面 IShape
的各種形狀。假設今天再多一個條件是「顏色」,而店家會去販售特定形狀的卡片搭配特定顏色的顏料,並且提供原物料的工廠也是專門提供特定形狀的卡片及顏料,那麼這時候就適合用抽象工廠模式去實作。
也就是說,我先將不同形狀的卡片都實作同一個卡片介面,各種顏料也是實作同一個顏料介面,接著我將工廠變成一個抽象類別的工廠,在裡面去定義兩種方法,一種回傳卡片介面,一種回傳顏料介面,而繼承自抽象工廠的真正工廠再去實作細節(也就是要生產哪種卡片及顏料)。
最後,在商店這邊,因為是特定形狀及顏色卡片的專賣店,所以我知道要找特定的工廠去生產,並且由於工廠回傳的實例是去實作卡片介面和顏料介面,所以店家在使用時就可以不用在意他們的實例,直接使用介面去做卡片生產或其他處理,不用擔心到時候工廠提供錯誤的原料,因為工廠提供的東西就是確定的(除非你呼叫到不是你要的工廠XD),這樣就完成了,來看實作吧~
為建立一組相關或相依的物件提供一個介面,而且無須指定它們的具體類別。
(圖片來源:https://miro.medium.com/max/568/1*qWRYA76F_iuxL_BOsYdVtA.png)
using System;
namespace DAY12_AbstractFactory
{
internal class Program
{
static void Main(string[] args)
{
// 建造專門製作紅色圓形卡片的商店,並進行卡片生產
CardShop shop = new RedCircleCardShop();
shop.Make();
}
}
public abstract class CardShop
{
// 製作卡片
public abstract void Make();
}
public class RedCircleCardShop : CardShop
{
private readonly IShapeCard _shapeCard;
private readonly IColor _color;
public RedCircleCardShop()
{
// 在建構子,因為是賣紅色圓形卡片的商店,所以提供原料工廠就是對應的紅色圓形卡片工廠
var factory = new RedCircleCardFactory();
_shapeCard = factory.GetShape();
_color = factory.GetColor();
}
public override void Make()
{
_shapeCard.UseShape();
_color.UseColor();
Console.WriteLine($"製作完成,已將{_shapeCard.Name}和{_color.Name}組合在一起~");
}
}
public abstract class CardFactory
{
// 給定抽象方法,讓繼承的公司去實作生產的實例
public abstract IShapeCard GetShape();
public abstract IColor GetColor();
}
public class RedCircleCardFactory : CardFactory
{
// 實作生產的實例
public override IColor GetColor()
{
return new Red();
}
public override IShapeCard GetShape()
{
return new CircleCard();
}
}
public interface IShapeCard
{
string Name { get; set; }
void UseShape();
}
public class RectangleCard : IShapeCard
{
public string Name { get => "長方形卡片"; set => throw new NotImplementedException(); }
public void UseShape()
{
Console.WriteLine("長方形卡片使用中!");
}
}
public class CircleCard : IShapeCard
{
public string Name { get => "圓形卡片"; set => throw new NotImplementedException(); }
public void UseShape()
{
Console.WriteLine("圓形卡片使用中!");
}
}
public interface IColor
{
string Name { get; set; }
void UseColor();
}
public class Red : IColor
{
public string Name { get => "紅色顏料"; set => throw new NotImplementedException(); }
public void UseColor()
{
Console.WriteLine("紅色顏料使用中!");
}
}
public class Blue : IColor
{
public string Name { get => "藍色顏料"; set => throw new NotImplementedException(); }
public void UseColor()
{
Console.WriteLine("藍色顏料使用中!");
}
}
}
-結果
在本章中作者有提到一個蠻重要的概念,『switch 語句可能說明需要抽象』,switch語句本身說明:
上面的例子中,如果沒有將工廠做成抽象工廠,而是像 Factory模式中一個工廠直接去做各種不同形狀卡片以及顏料的生產,那勢必就會用到 switch
(或者單純的 if else
),去判斷說現在店家是誰,他可能給我的是字串「Red」和「Circle」,我再根據這些字串用 switch 去做對應,回傳相對應的實體類別,但如果我今天又新增一個種類的卡片跟顏料呢?那我就必須再去這個工廠裡面修改程式碼,也違反了OCP(開放封閉原則)。
所以下次看到在類別中用到很多 switch
或 if else
,就可以考慮看看是否要變成抽象囉~