简单工厂/工厂/抽象工厂模式
前言:
先说一下问题背景,有位在学Unity做游戏的朋友向我吐槽了下面这段代码:先枚举完所有的怪物,然后又switch case有所的怪物,后续每新增一个怪物,就要在这里进行修改,很是麻烦。
我一看,这不就是简单工厂模式嘛,然后就建议他去了解学习一下工厂模式,但据他反馈,还是没怎么搞懂,故写下这篇博客,工厂模式简单地说就是:根据不同的条件需求,构建不同的实例。在游戏开发中的应用场景例如:英雄、NPC、怪物这块。
下面依次介绍最常使用的三种工厂模式(简单工厂/工厂/抽象工厂)。
简单工厂模式:
定义:
简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。
特点:
只生产一种类型的产品,在工厂中动态创建。
举例说明:
由于朋友是在游戏开发中遇到的问题,所以我下面以游戏中的怪物举例:
游戏中有各种各样的怪物,比如狗、熊等,如何使用工厂模式创建他们呢?
参考这张图:
1、先创建一个抽象类,规定一些怪物都有的一些方法,比如攻击。
//创建一个怪物抽象类AbstractMonster
public abstract class AbstractMonster
{
public abstract void Attack();
}
2、分别创建狗和熊的类,继承上面的抽象类,狗和熊的类对应上图的“工厂内部”
//狗
public class Dog : AbstractMonster
{
public override void Attack()
{
Debug.Log("狗发动攻击");
}
}
//熊
public class Bear : AbstractMonster
{
public override void Attack()
{
Debug.Log("熊发动攻击");
}
}
3、创建一个简单工厂类,此工厂就是用来生产各种类型的怪物,对应上图中的“工厂”
//怪物类型枚举
public enum MonsterType
{
Dog,
Bear,
}
public class SimpleFactory
{
//创建怪物
public AbstractMonster CreateMonster(MonsterType emMonsterType)
{
//根据传进来的参数决定实例化哪种怪物
AbstractMonster monster = null;
switch (emMonsterType)
{
case MonsterType.Dog:
monster = new Dog();
break;
case MonsterType.Bear:
monster = new Bear();
break;
default:
break;
}
//返回怪物类型
return monster;
}
}
4、创建FactoryMain脚本,开始生产怪物,对应上图中的“工厂外部”
//由于是用Unity开发游戏,这个脚本要挂载在游戏物体上,所以脚本要继承MonoBehaviour
public class FactoryMain : MonoBehaviour
{
void Start()
{
RunSimpleFactory();
}
void RunSimpleFactory()
{
//实例化一个工厂。
SimpleFactory factory = new SimpleFactory();
//因为CreateMonster(MonsterType.Dog)返回的是一个抽象类,所以必须由AbstractMonster类型的参数接受。
//由于CreateMonster(MonsterType.Dog)中,已经将对应的类实例化,所以这里的dog指的就是Dog类。
AbstractMonster dog = factory.CreateMonster(MonsterType.Dog);
//工厂生产完之后,工厂外部只需要直接调用即可,不用管内部是怎么实现的
dog.Attack();
AbstractMonster bear = factory.CreateMonster(MonsterType.Bear);
bear.Attack();
}
}
5、查看运行结果,符合预期,这就是简单工厂模式。
工厂模式:
定义:
简单工厂模式中,新增产品时需要修改工厂类,为了解决这个问题而出现了工厂模式。
特点:
只生产一种类型的产品,在具体的子类工厂中创建。
举例说明:
先思考一个问题:
上文简单工厂模式的例子中,我们生产了狗和熊这两种怪物,如果这个时候新增一种怪物老虎,要怎么做呢?
如果接着按照简单工厂模式的思路,那么就是:新增一个老虎类,继承AbstractMonster,实现其中的方法,然后在工厂类中的switch中新增老虎的情况,也就是定义中说到的新增产品时需要修改工厂类。
这么做是可行的,但是如果要新增几十上百种怪物呢?就像最开始问题背景中提到的那样,每次新增都要修改工厂类,很麻烦,还很蠢,违背了开闭原则。
所谓开闭原则,指的是:在面向对象编程领域中,规定“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。
于是,为了解决这种情况,就要用到工厂模式。参考下图:
我们不难发现,工厂模式把中间的工厂部分给拆分成更具体的工厂了,每个工厂只生产对应的东西,如,狗工厂只生产狗,熊工厂只生产熊,老虎工厂只生产老虎。这样,在有新增的时候,只要添加对应的怪物和其工厂就可以,改造步骤如下:
1、给所有的工厂创建一个抽象基类FactoryBase,这个基类包含了工厂的通用方法,比如都是生产怪物的。
public abstract class FactoryBase : MonoBehaviour
{
public abstract AbstractMonster CreateMonster();
}
2、接着创建不同怪物的工厂,继承工厂基类,直接返回对应怪物的实例
//狗工厂
public class DogFactory : FactoryBase
{
public override AbstractMonster CreateMonster()
{
return new Dog();
}
}
//熊工厂
public class BearFactory : FactoryBase
{
public override AbstractMonster CreateMonster()
{
return new Bear();
}
}
//老虎工厂
public class TigerFactory : FactoryBase
{
public override AbstractMonster CreateMonster()
{
return new Tiger();
}
}
3、那么在外部访问的时候,外部只需要创建各自的中间件就可以对具体的怪物进行访问了。
void RunFactory()
{
DogFactory dogFactory = new DogFactory();
AbstractMonster dog = dogFactory.CreateMonster();
dog.Attack();
BearFactory bearFactory = new BearFactory();
AbstractMonster bear = bearFactory.CreateMonster();
bear.Attack();
TigerFactory tigerFactory = new TigerFactory();
AbstractMonster tiger = tigerFactory.CreateMonster();
tiger.Attack();
}
4、运行结果:
5、综上,所以之后如果有新的怪物需求,那么只需要创建对应的怪物工厂,返回对应的怪物就可以了,不要对已有的东西进行修改,符合开闭原则。
抽象工厂模式:
定义:
为了解决系列产品的问题,就有了抽象工厂模式。
特点:
可以生产多种产品。而简单工厂和工厂只生产一种产品。
举例说明:
接着再思考一个问题,如果策划现在对你说,游戏里除了怪物狗(怪物狗是敌方单位),还有宠物狗(友方单位)这个时候要怎么办呢?
参考下图:
不难发现,工厂内部相同分类的东西更多了,而且敌方单位Dog和友方单位FriendDog的效果肯定不一样,那么如何创建呢?步骤如下:
1、由于我们现在有个新的产品(即友方单位)了,就要创建相对应的抽象基类。
public abstract class AbstractFriendly
{
public abstract void Attack();
}
2、接着让需要构建的友方单位继承AbstractFriendly
public class FriendlyDog : AbstractFriendly
{
public override void Attack()
{
Debug.Log("友方单位:狗发动攻击");
}
}
public class FriendlyBear : AbstractFriendly
{
public override void Attack()
{
Debug.Log("友方单位:熊发动攻击");
}
}
public class FriendlyTiger : AbstractFriendly
{
public override void Attack()
{
Debug.Log("友方单位:老虎发动攻击");
}
}
3、接着对工厂进行构造,先创建抽象工厂的基类,然后对应类型的动物继承基类
//工厂基类
public abstract class AbstractFactory_Base
{
//构造怪物
public abstract AbstractMonster CreateMonster();
//构造友方单位
public abstract AbstractFriendly CreateFriendly();
}
//狗工厂
public class AbstractFactory_Dog : AbstractFactory_Base
{
public override AbstractFriendly CreateFriendly()
{
return new FriendlyDog();
}
public override AbstractMonster CreateMonster()
{
return new Dog();
}
}
//熊工厂
public class AbstractFactory_Bear : AbstractFactory_Base
{
public override AbstractFriendly CreateFriendly()
{
return new FriendlyBear();
}
public override AbstractMonster CreateMonster()
{
return new Bear();
}
}
//老虎工厂
public class AbstractFactory_Tiger : AbstractFactory_Base
{
public override AbstractFriendly CreateFriendly()
{
return new FriendlyTiger();
}
public override AbstractMonster CreateMonster()
{
return new Tiger();
}
}
4、那么在外部访问的时候,只要先创建对应的工厂,然后根据不同的需求生产不同的具体产品就可以了
void RunAbstractFactory()
{
//创建狗工厂
AbstractFactory_Dog dogFactory = new AbstractFactory_Dog();
//构造敌方单位的狗
AbstractMonster monsterDog = dogFactory.CreateMonster();
monsterDog.Attack();
//构造友方单位的狗
AbstractFriendly friendlyDog = dogFactory.CreateFriendly();
friendlyDog.Attack();
AbstractFactory_Bear bearFactory = new AbstractFactory_Bear();
AbstractMonster monsterBear = bearFactory.CreateMonster();
monsterBear.Attack();
AbstractFriendly friendlyBear = bearFactory.CreateFriendly();
friendlyBear.Attack();
AbstractFactory_Tiger tigerFactory = new AbstractFactory_Tiger();
AbstractMonster monsterTiger = tigerFactory.CreateMonster();
monsterTiger.Attack();
AbstractFriendly friendlyTiger = tigerFactory.CreateFriendly();
friendlyTiger.Attack();
}
5、运行结果:
结束,感谢能读到这,如果错误还请欢迎指正!