iT邦幫忙

2022 iThome 鐵人賽

DAY 12
1

昨天我們談到了 Factory模式,工廠可以生產實作同個介面 IShape的各種形狀。假設今天再多一個條件是「顏色」,而店家會去販售特定形狀的卡片搭配特定顏色的顏料,並且提供原物料的工廠也是專門提供特定形狀的卡片及顏料,那麼這時候就適合用抽象工廠模式去實作。

也就是說,我先將不同形狀的卡片都實作同一個卡片介面,各種顏料也是實作同一個顏料介面,接著我將工廠變成一個抽象類別的工廠,在裡面去定義兩種方法,一種回傳卡片介面,一種回傳顏料介面,而繼承自抽象工廠的真正工廠再去實作細節(也就是要生產哪種卡片及顏料)。

最後,在商店這邊,因為是特定形狀及顏色卡片的專賣店,所以我知道要找特定的工廠去生產,並且由於工廠回傳的實例是去實作卡片介面和顏料介面,所以店家在使用時就可以不用在意他們的實例,直接使用介面去做卡片生產或其他處理,不用擔心到時候工廠提供錯誤的原料,因為工廠提供的東西就是確定的(除非你呼叫到不是你要的工廠XD),這樣就完成了,來看實作吧~

Abstract Factory - 定義

為建立一組相關或相依的物件提供一個介面,而且無須指定它們的具體類別。

https://ithelp.ithome.com.tw/upload/images/20220921/20136443QIu48U3OoC.png

(圖片來源:https://miro.medium.com/max/568/1*qWRYA76F_iuxL_BOsYdVtA.png)

範例 UML

https://ithelp.ithome.com.tw/upload/images/20220925/201364432EuO0WF77p.png

不囉嗦上Code!

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("藍色顏料使用中!");
        }
    }
}

-結果

https://ithelp.ithome.com.tw/upload/images/20220921/20136443RhG2sE0pGD.png

簡單的小結

在本章中作者有提到一個蠻重要的概念,『switch 語句可能說明需要抽象』,switch語句本身說明:

  • 需要多型行為
  • 存在職責錯放

上面的例子中,如果沒有將工廠做成抽象工廠,而是像 Factory模式中一個工廠直接去做各種不同形狀卡片以及顏料的生產,那勢必就會用到 switch(或者單純的 if else),去判斷說現在店家是誰,他可能給我的是字串「Red」和「Circle」,我再根據這些字串用 switch 去做對應,回傳相對應的實體類別,但如果我今天又新增一個種類的卡片跟顏料呢?那我就必須再去這個工廠裡面修改程式碼,也違反了OCP(開放封閉原則)。

所以下次看到在類別中用到很多 switchif else,就可以考慮看看是否要變成抽象囉~


上一篇
【DAY11】Factory模式 - 來當工廠老闆吧!
下一篇
【DAY13】淺談設計模式的原則與策略
系列文
勇闖秘境!探索物件導向背後的設計模式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言