Factory pattern

工廠模式(Factory pattern):讓一個特別的物件(工廠Factory)去建立其他物件(產品Product)。它封裝了生成其他物件(產品Product)的邏輯,最直接可見的好處是整理了你的程式碼。工廠物件可以有多種子類(subclass),用來產生多種不同的產品。

優點

  • 使用工廠模式的好處是當你增加產品時,你不需要修改先前的程式碼就可以增加產品

缺點

  • 增加程式碼的複雜度

建立一個IProduct介面,訂立產品必須要有的規則

  • 每個產品一定要有自己的名字(ProductName)
  • 每個產品有初始化自己的方式(Initialize())
  • 產品種類比較不會有共用的邏輯,因此將其設計為介面
    1
    2
    3
    4
    5
    public interface IProduct
    {
    public string ProductName { get; set; }
    public void Initialize();
    }

以下建立一個抽象工廠,抽象工廠用來規定工廠必須要有的動作

  • 每個工廠必須要能在對應的位置(Vector3 position)上生產產品(GetProduct())
    1
    2
    3
    4
    5
    6
    public abstract class Factory : MonoBehaviour
    {
    public abstract IProduct GetProduct(Vector3 position);
    // shared method with all factories

    }

建立一個產品A

  • 它含有一個ParticleSystem,並在初始化方法(Initialize)中會播放此ParticleSystem
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class ProductA : MonoBehaviour, IProduct
    {
    [SerializeField] private string productName = "ProductA";
    public string ProductName { get => productName; set => productName = value ; }
    private ParticleSystem particleSystem;
    public void Initialize()
    {
    // any unique logic to this product
    gameObject.name = productName;
    particleSystem = GetComponentInChildren<ParticleSystem>();
    particleSystem?.Stop();
    particleSystem?.Play();
    }
    }

建立一個工廠A

  • 它含有一個產品A的依賴,在GetProduct()方法中建立產品A實體之後,會呼叫產品A實體上的Initialize()方法用來初始化該實體。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class ConcreteFactoryA : Factory
    {
    [SerializeField] private ProductA productPrefab;
    public override IProduct GetProduct(Vector3 position)
    {
    // 使用Prefab在指定的position上建立一個instance
    GameObject instance = Instantiate(productPrefab.gameObject, position, Quaternion.identity);
    ProductA newProduct = instance.GetComponent<ProductA>();
    // 初始化產品A
    newProduct.Initialize();
    return newProduct;
    }
    }

建立一個產品B

  • 它含有一個AudioSource,在初始化方法(Initialize)中會播放此AudioSource
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class ProductB : MonoBehaviour, IProduct
    {
    [SerializeField] private string productName = "ProductB";
    public string ProductName { get => productName; set => productName = value; }

    private AudioSource audioSource;

    public void Initialize()
    {
    // do some logic here
    audioSource = GetComponent<AudioSource>();
    audioSource?.Stop();
    audioSource?.Play();

    }
    }

建立一個工廠B

  • 工廠B與工廠A類似,不過依賴換為產品B
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class ConcreteFactoryB : Factory
    {
    // used to create a Prefab
    [SerializeField] private ProductB productPrefab;

    public override IProduct GetProduct(Vector3 position)
    {
    // create a Prefab instance and get the product component
    GameObject instance = Instantiate(productPrefab.gameObject, position, Quaternion.identity);
    ProductB newProduct = instance.GetComponent<ProductB>();

    // each product contains its own logic
    newProduct.Initialize();

    // add any unique behavior to this factory
    instance.name = newProduct.ProductName;
    Debug.Log(GetLog(newProduct));

    return newProduct;
    }
    }

建立一個使用工廠的類

  • ClickToCreate這個類使用了工廠A與工廠B,
    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
    28
    29
    30
    31
    32
    public class ClickToCreate : MonoBehaviour
    {
    [SerializeField] private LayerMask layerToClick;
    [SerializeField] private Vector3 offset;
    [SerializeField] Factory[] factories;

    private Factory factory;

    private void Update()
    {
    GetProductAtClick();
    }

    private void GetProductAtClick()
    {
    // check click with raycast
    if (Input.GetMouseButtonDown(0))
    {
    // choose a random factory
    factory = factories[Random.Range(0, factories.Length)];

    // instantiate product at raycast intersection
    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    RaycastHit hitInfo;

    if (Physics.Raycast(ray, out hitInfo, Mathf.Infinity, layerToClick) && factory != null)
    {
    factory.GetProduct(hitInfo.point + offset);
    }
    }
    }
    }

評論