浅谈设计模式和其Unity中的应用:三、抽象工厂模式

什么是抽象工厂模式

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

简单的来说,就是套娃。超级工厂生产普通工厂,普通工厂生产产品。
抽象工厂模式是工厂模式的升级版
关于工厂模式可以看看我的上一篇文章:浅谈设计模式和其Unity中的应用:二、工厂模式

怎么实现抽象工厂模式(C#实现)

先理解抽象工厂模式:超级工厂生产普通工厂,普通工厂生产产品。
那我们就在工厂模式的基础上再建一个工厂模式就行了。
这里以我上一篇文章为例子。

此时多一个条件,不仅有生产电脑的工厂,还有生产手机的工厂。

步骤一

首先应该有一个电脑和手机接口

public interface Computer {
    
    
   void Func();
}
public interface Phone {
    
    
   void Func();
}

步骤二

然后有具体的电脑和手机,创建实现接口的实体类。

电脑

public class Dell : Computer {
    
    
 
   public override void Func()
   {
    
    
    	
   }
   
}
public class ASUS: Computer {
    
    
 
   public override void Func()
   {
    
    
      
   }
   
}
public class Acer: Computer {
    
    
 
   public override void Func()
   {
    
    
      
   }
   
}

手机

public class OPPO : Phone {
    
    
 
   public override void Func()
   {
    
    
      
   }
   
}
public class VIVO: Phone {
    
    
 
   public override void Func()
   {
    
    
      
   }
   
}

步骤三

为电脑和手机工厂创建抽象超级工厂类

public abstract class BaseFactory
{
    
    
    public abstract Computer GetComputer(string produceName);
    public abstract Phone GetPhone(string produceName);
}

步骤四

创建一个电脑和手机工厂,生成基于给定信息的实体类的对象。

电脑

public class ComputerFactory : BaseFactory
{
    
    

   //使用 GetComputer方法获取电脑类型的对象
   public override Computer GetComputer(string computerName)
   {
    
    
      	if(computerName == null)
      	{
    
    
        	result = null;
      	}
   	  	Computer result = null;
   	  	switch(computerName)
   	  	{
    
    
			case: "Dell":
				result = new Dell();
				break;
			case: "ASUS":
				result = new ASUS();
				break;
			case: "Acer":
				result = new Acer();
				break;
			default:
				result = null;
				break;
		}
      	return result;
   }
   
   public override Phone GetPhone(string phoneName)
   {
    
    
   		return null;
   }
   
}

手机

public class PhoneFactory : BaseFactory
{
    
    

   //使用 GetPhone方法获取手机类型的对象
   public Phone GetPhone(string phoneName)
   {
    
    
   		Phone result = null;
      	if(phoneName== null)
      	{
    
    
        	result = null;
      	}
   	  	switch(phoneName)
   	  	{
    
    
			case: "OPPO":
				result = new OPPO();
				break;
			case: "VIVO":
				result = new VIVO();
				break;
			default:
				result = null;
				break;
		}
      	return result;
   }
   
   public override Computer GetComputer(string computerName)
   {
    
    
   		return null;
   }
   
}

步骤五

创建一个工厂创造器/生成器类,通过传递工厂信息来获取工厂。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum FactoryType
{
    
    
   	ComputerFactory,
   	PhoneFactory
}

public class FactoryProducer
{
    
    
    public static BaseFactory GetFactory(FactoryType factoryType)
    {
    
    
        BaseFactory factory = null;
        switch (factoryType)
        {
    
    
            case FactoryType.ComputerFactory:
                factory = new ComputerFactory();
                break;
            case FactoryType.PhoneFactory:
                factory = new PhoneFactory();
                break;
            default:
            	factory = null;
                break;
        }
        return factory;
    }
}

步骤七

使用 FactoryProducer 来获取 BaseFactory,通过传递类型信息来获取实体类的对象。

public class AbstractFactoryPatternDemo
{
    
    
   public static void Main(String[] args) {
    
    
 
      //获取电脑工厂
      BaseFactory computerFactory = FactoryProducer.GetFactory(FactoryType.ComputerFactory);
 
      //获取 Dell 的对象,并调用它的 Func 方法
      Computer dell = computerFactory.GetComputer("Dell");
 
      //调用 Dell 的 Func方法
      dell.Func();
	
	  //...
	  //DoSomethings
   }
}

在Unity中的应用(单例模式+对象池+抽象工厂模式)

注 意 : 此 处 代 码 会 有 点 问 题 , 请 大 家 在 看 到 时 候 仔 细 思 考 , 我 会 把 问 题 和 解 决 方 案 放 在 下 方 。 \color{#0000FF}{注意:此处代码会有点问题,请大家在看到时候仔细思考,我会把问题和解决方案放在下方。}

本应用是上一篇文章工厂模式的升级版,只是把工厂模式改为了抽象工厂模式。
条件增加:不止子弹这一个工厂,还有投掷物这个工厂。
相对于上一篇文章的重复步骤我就用不写了或者用文字简单说了。

步骤一

建立子弹实体类:BulletController和投掷物实体类:MissileController

步骤二

建立一个抽象的超级工厂类。

注意,此时BaseFactory继承自单例类Singleton

public abstract class BaseFactory : Singleton<BaseFactory>
{
    
    
    public abstract Component Creat(string produceName);
    public abstract Component Creat(string produceName, Vector3 position, Quaternion rotation);
    public abstract void RecoveryGameObjectToPool(string produceName, Component go);
}

步骤三

创建子弹工厂和投掷物工厂。

public class BulletFactory : BaseFactory
public class MissileFactory : BaseFactory

步骤四

创建一个工厂创造器/生成器类,通过传递工厂信息来获取工厂。

public enum FactoryType
{
    
    
    BulletFactory,
    MissileFactory
}

public class FactoryProducer
{
    
    
    public static BaseFactory GetFactory(FactoryType factoryType)
    {
    
    
        BaseFactory factory = null;
        switch (factoryType)
        {
    
    
            case FactoryType.BulletFactory:
                factory = BulletFactory.Ins;
                break;
            case FactoryType.MissileFactory:
                factory = MissileFactory.Ins;
                break;
            default:
                break;
        }
        return factory;
    }
}

应用场景

假设我们玩家点击屏幕,枪械应该要射出子弹。
那么我们就这样:

public void Shoot(string bulletName)
{
    
    
	Component bullet = FactoryProducer.GetFactory(FactoryType.BulletFactory).Creat(bulletName);
	//让子弹开始移动
	bullet.GetComponent<BulletController>().StartMove();
}

问题出现

上述Unity应用在我运行的时候发生了点小错误。

为了记录下这次错误,我打算保留上述错误留下我的解决方案。

问题:由于我的BaseFactory继承自单例类,所以按道理我的BaseFactory应该只有一个子类实例,但是我的需求是BaseFactory应该是一个抽象工厂类,它有很多子类继承它,比如子弹工厂、投掷物工厂。但是实际情况是它的所有的子类就只有一个实例(原因在下方),我的单例类代码如下:

public class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
    
    
    private static T _ins;
    public static T Ins {
    
     get {
    
     return _ins; } }
    protected virtual void Awake()
    {
    
    
        if(_ins == null)
        {
    
    
            _ins = (T)this;
        }
    }
    protected virtual void OnDestroy()
    {
    
    
        _ins = null;
    }
    public static bool IsInitialized
    {
    
    
        get {
    
     return _ins != null; }
    }
}

其中最重要的就是

protected virtual void Awake()
{
    
    
    if(_ins == null)
    {
    
    
         _ins = (T)this;
    }
}

问题分析

假设我的子弹工厂先调用Awake方法,然后我的投掷物工厂再调用Awake方法,发现_ins已经不为null了(因为他们都调用的BaseFactoryAwake,自然_ins就是第一次调用者:子弹工厂的实例,由于if条件,第一次调用已经初始化了,所以第二次就会越过)。所以我们所有的工厂都是子弹工厂了,即使你这样:MissileFactory.Ins,调用的依然是父类BaseFactoryIns也就是子弹工厂。

解决办法

我们可以让所有的工厂都继承自单例类,那么我们所有的工厂都有自己的实例,但是C#是单继承的,我们又要继承单例类,又要继承BaseFactory,这怎么办。
那我们就只好把BaseFactory写成接口了,也就是abstract class修改成interface
修改代码如下:

public interface IBaseFactory
{
    
    
    Component Creat(string produceName);
    Component Creat(string produceName, Vector3 position, Quaternion rotation);
    void RecoveryGameObjectToPool(string produceName, Component go);
}
public class BulletFactory : Singleton<BulletFactory>, IBaseFactory
public class MissileFactory : Singleton<MissileFactory>, IBaseFactory

下一篇文章来了解我们的状态模式有限状态机FSM

浅谈设计模式和其Unity中的应用:四、状态模式


要更努力地成为一名优秀的游戏开发者!

猜你喜欢

转载自blog.csdn.net/qq_52855744/article/details/122286102