创建型模式——工厂方法(Factory Method)
问题背景
为了引出问题,我们首先介绍一种未被纳入GoF设计模式的模式:简单工厂。这个模式是为了将某个接口的某个实现的创建和表示分离而存在的。听起来似乎有点晦涩,下面举个例子。在LOL的载入过程中,游戏要根据玩家在房间的选择来创建对应的英雄。由于英雄的创建过程比较复杂,所以考虑将创建工作交给工厂。假设每个英雄都是一个类,他们都实现了IChampion接口,那么使用简单工厂的代码是这样的:
public IChampion CreateChampion(string name)
{
switch (name)
{
case "Yasuo":
return new Yasuo();
case "Lee Sin":
return new LeeSin();
// ...
default:
throw new Exception("无此英雄");
}
}
对于英雄联盟这种三天两头就要出新英雄骗氪的游戏来说,这么做无疑会增加工作难度,因为每次出新英雄都得修改工厂。
解决方案
以上问题的根源在于需求的不稳定性,试想如果游戏发布之后就不再更新,那么简单工厂是完全无可非议的。但面对这种发布后需要运营和迭代的产品,就要考虑工厂方法了。工厂方法仅仅规定一个创建对象的接口,但不负责实现,具体的实现工作延迟到它的实现类中。改进后的程序结构是这样的:
使用工厂方法之后,当新增英雄时,只需要分别扩展IChampion和IChampionFactory,不需要修改原有代码。
效果
- 将对象的创建工作与应用代码解耦。
- 避免了在需求变化时修改原有代码。
缺陷
工厂类的存在使项目中类的数量翻倍,在大型项目中会造成类爆炸。
目前,主流编程语言大都支持元编程,工厂方法一般会基于反射实现,而不是基于接口。基于反射的工厂不存在类爆炸的问题,是当前的主流做法,这种工厂会在第二期的《反射工厂》中介绍。
相关模式
- 抽象工厂:工厂方法是抽象工厂的一种常见实现方式。
- 单例:工厂类常被设计成单例类。
实现
using System;
namespace FactoryMethod
{
class Client
{
public interface IChampion
{
void Show();
}
public interface IChampionFactory
{
IChampion CreateChampion();
}
public class Yasuo : IChampion
{
public void Show()
{
Console.WriteLine("死亡如风,常伴吾身");
}
}
public class LeeSin : IChampion
{
public void Show()
{
Console.WriteLine("我用双手成就你的梦想");
}
}
public class YasuoFactory : IChampionFactory
{
public IChampion CreateChampion()
{
return new Yasuo();
}
}
public class LeeSinFactory : IChampionFactory
{
public IChampion CreateChampion()
{
return new LeeSin();
}
}
static void Main(string[] args)
{
Console.WriteLine("创建亚索...");
IChampionFactory factory = new YasuoFactory();
var yasuo = factory.CreateChampion();
yasuo.Show();
Console.WriteLine("创建盲僧...");
factory = new LeeSinFactory();
var leesin = factory.CreateChampion();
leesin.Show();
}
}
}