考虑这种情况,游戏中背包可以存放道具(Item)和装备(Equipment),道具可使用可堆叠不可装备,装备可装备不可使用不可堆叠。他们都继承自物品(Stuff)。定义如下:
public enum StuffType
{
Item,
Equipment,
Material,
}
public abstract class Stuff
{
public int id;
public readonly StuffType type;
public Stuff(StuffType stuffType, int stuffId)
{
type = stuffType;
id = stuffId;
}
}
public class Item : Stuff
{
public Item(int stuffId):base(StuffType.Item, stuffId)
{
}
}
public class Equipment : Stuff
{
public Equipment(int stuffId):base(StuffType.Equipment, stuffId)
{
}
}
当我们需要根据类型创建装备或者道具的时候(例如掉落时)应该怎么办呢?你可能想到了简单工厂:
public static class StuffFactory
{
public static Stuff CreateStuff(StuffType stuffType, int stuffId)
{
Stuff stuff = null;
switch (stuffType) {
case StuffType.Item:
stuff = new Item (stuffId);
break;
case StuffType.Equipment:
stuff = new Equipment (stuffId);
break;
}
return stuff;
}
}
使用:
Stuff st = StuffFactory.CreateStuff (StuffType.Equipment, 10);
恩,这样还不错。这个时候策划微笑着想你走来,你一看不妙想赶紧请假走人,策划一把抢过你的请假单,对你说“有个需求很简单,我需要增加一个材料类型,不能使用可以堆叠,可以用来合成道具或者装备”。你拿出了策划发誓不增加物品类型的录音,然而并没有什么卵用。
我们需要增加一个类,并且需要修改StuffFactory,这可能并不是你想要的,因为这违背了开闭原则(对扩展开放,对修改关闭)。所以可以考虑使用下面的方法:
public interface IFactory
{
Stuff CreateStuff(int stuffId);
}
public class ItemFactory:IFactory
{
public Stuff CreateStuff(int stuffId)
{
return new Item (stuffId);
}
}
public class EquipmentFactory:IFactory
{
public Stuff CreateStuff(int stuffId)
{
return new Equipment (stuffId);
}
}
IFactory stuffFactory = null;
StuffType stype = StuffType.Item;
//TODO:
switch (stype) {
case StuffType.Item:
stuffFactory = new ItemFactory ();
break;
case StuffType.Equipment:
stuffFactory = new EquipmentFactory ();
break;
}
Stuff stuff = stuffFactory.CreateStuff (9);
这就是工厂方法模式。它将类型的判定交给了用户来判断,没需要增加一个类型,原有的工厂不需要做修改,而用户则需要增加一个case选项。当我们需要增加一个材料类型时,需要为StuffType这个枚举类型添加一个项Material,并增加这些代码:
public class Material:Stuff
{
public Material(int stuffId):base(StuffType.Material, stuffId)
{
}
}
public class MaterialFactory:IFactory
{
public Stuff CreateStuff(int stuffId)
{
return new Material (stuffId);
}
}
工厂方法模式虽然遵守了开闭原则,但是缺点也很明显,需要为每一个类创建一个工厂类,而且为了保证多态,我们没办法将工厂类设置为静态类。
或者我们可以考虑使用泛型来解决问题:
public class TFactory : IFactory where T: Stuff,new()
{
public Stuff CreateStuff(int stuffId)
{
T ret = new T ();
ret.id = stuffId;
return ret;
}
}
我们需要为每一个类添加默认构造函数,例如:
public Item():base(StuffType.Item, 0)
{
}
IFactory sf = null;
StuffType tstype = StuffType.Equipment;
//TODO:
switch (tstype) {
case StuffType.Item:
sf = new TFactory- ();
break;
case StuffType.Equipment:
sf = new TFactory
();
break;
case StuffType.Material:
sf = new TFactory ();
break;
}
Stuff tstuff = sf.CreateStuff (8);
public static class TStaticFactory
{
public static T CreateStuff(int stuffId) where T: Stuff,new()
{
T ret = new T ();
ret.id = stuffId;
return ret;
}
}
StuffType tsstype = StuffType.Item;
Stuff tsstuff = null;
int id = 7;
//TODO:
switch (tsstype) {
case StuffType.Item:
tsstuff = TStaticFactory.CreateStuff- (id);
break;
case StuffType.Equipment:
tsstuff = TStaticFactory.CreateStuff
(id);
break;
case StuffType.Material:
tsstuff = TStaticFactory.CreateStuff (id);
break;
}
似乎非静态的工厂会好一点,但是仍然不可避免的要每次创建一个工厂实例。
实际情况下,似乎简单工厂运用的更广泛一些,虽然违背了开闭原则,但是写起了比较方便,适用于轻量级的继承关系。