关于简单工厂模式与开闭原则的讨论(1)

说个老生常谈的问题的了,就是简单工厂模式到底是不是违背了开闭原则?

在讨论之前,先重温下什么是开闭原则。

开闭原则(open/closed principle)的定义是,software entities (classes, modules, functions) should be open for extension, but closed for modification,一个类开放它的拓展,关闭它的修改,即一个类可以允许拓展它的功能,而不允许修改它的源码。


先举个简单的例子,关于开闭原则的,看下面一段代码

class DefenseBuff
{
        private int m_buffType;

        public int BuffType
        {
            get { return m_buffType; }
            set { m_buffType = value; }
        }

        public int GetDiscountDamage(int TotalDamage)
        {
                if (m_buffType == 1)
                {
                    return TotalDamage - 100;
                }
                else
                {
                    return TotalDamage - 50;
                }
        }
}
上面的问题在于,如果策划要加一个buff种类,你必须又加一个if-else的条件判断,这样会造成无休止的修改此类,然后再次测试此类能否正常运行,包括引用到它的类又能否正常运行。如果到后期系统越变越庞大,则非常难维护。

这就是违背了开闭原则。

如果我们改成下面这样:用继承和多态的方式,如果加了新的类型,我们只要创建一个新的子类就行了,这样就不会违背开闭原则

class DefenseBuff
{
        public virtual int GetDiscountDamage(int TotalDamage)
        {
            return TotalDamage;
        }
}

class SilverDefenseBuff : DefenseBuff
{
	public override int GetDiscountDamage(int TotalDamage)
	{
		return base.GetDiscountDamage(TotalDamage) - 50;
	}
}

class GoldDefenseBuff : DefenseBuff
{
	public override int GetDiscountDamage(int TotalDamage)
	{
		return base.GetDiscountDamage(TotalDamage) - 100;
	}
}
或者我们可以重Excel数据表中读数据,然而在现实开发中,策划一般把数值写在Excel表中的,不会向上面那样写死的,然而我们也不需要再修改代码,而策划可以任意修改数值和增加类型。

class DefenseBuff
{
	private int m_buffType;

	public int BuffType
	{
		get { return m_buffType; }
		set { m_buffType = value; }
	}
    public DefenseBuff(int buffType)
	{
		m_buffType = buffType;
	}
	public int GetDiscountDamage(int TotalDamage)
	{
		return BuffTable.GetType(m_buffType).DiscountDamage;//从excel配置表读取数据
	}
}
var player = WorldMap.Instance.GetSelfPlayer();
Monster m = WorldMap.Instance.GetMonsterPool().GetAt(0);
DefenseBuff bf = new DefenseBuff(PlayerProperty.Instance.GetMetaData(player.ID).buffType);
player.Damage(bf.GetDiscountDamage(MonsterTable.GetType(m.ID)).AttackDamage));

说完了开闭原则,现在来说一下简单工厂模式,看下面代码:

using System;

enum MonsterType
{
	SKELETON,
	GHOUST,
	EVIL,
	CACODEMON
}

abstract class Monster
{
	public abstract int AttackDamage { get; }
}
	
class Skeleton : Monster
{
	public override int AttackDamage
	{
		get
		{
			return 10;
		}
		
	}
}

class Ghoust : Monster
{
	public override int AttackDamage
	{
		get
		{
			return 20;
		}
	}
}

class Evil : Monster
{
	public override int AttackDamage
	{
		get
		{
			return 50;
		}
	}
}

class Cacodemon : Monster
{
	public override int AttackDamage
	{
		get
		{
			return 100;
		}
	}
}

static class MonsterFactory
{
	public static Monster Create(MonsterType monster_type)
	{
		switch(monster_type)
		{
			case MonsterType.SKELETON:
				return new Skeleton();
			case MonsterType.GHOUST:
				return new Ghoust();
			case MonsterType.EVIL:
				return new Evil();
			case MonsterType.CACODEMON:
				return new Cacodemon();
		}
	}
}


var player = WorldMap.Instance.GetSelfPlayer();
var monster = MonsterFactory.Create(MonsterType.SKELETON);
player.Damage(monster.AttackDamage);
上述代码的问题就在于,如果策划要加一个新的怪物类,那我们就必须增加一个Swith-Case条件,就必须修改MonsterFactory的内部代码,这个其实在一定程度上也已经违背了开闭原则

如果改成下面的方式,类似于抽象工厂的方式,如果新加了一个怪物类,我们只要新增一个MonsterInfo的子类即可,就不会违背开闭原则了。

public abstract class MonsterInfo
{
    public abstract Monster CreateMonster();
}

public class SkeletonInfo : MonsterInfo
{
    public override Skeleton CreateMonster()
    {
        return new Skeleton();
    }
}

public class GhoustInfo : MonsterInfo
{
	public override Ghoust CreateMonster()
	{
		return new Ghoust();
	}
}

public static class MonsterFactory
{
    public static Monster Create(MonsterInfo aInfo)
    {       
        return aInfo.CreateMonster();
    }
}
var player = WorldMap.Instance.GetSelfPlayer();
var monster = MonsterFactory.Create(new SkeletonInfo());
player.Damage(monster.AttackDamage);
或者我们使用泛型,会更简洁方便

static class MonsterFactory<T> where T : Monster,new()
{
	public static Monster Create()
	{
		return new T();
	}
}
var player = WorldMap.Instance.GetSelfPlayer();
var monster = MonsterFactory<Skeleton>.Create();
player.Damage(monster.AttackDamage);

或者使用反射的方式:

public static class MonsterFactory
{
	public static Monster Create(Type monsterType)
	{
		return Activator.CreateInstance(monsterType) as Monster;
	}
	
	public static Monster Create(string monsterType)
	{
		Type type = Assembly.GetExecutingAssembly().GetType(monsterType);
        return Activator.CreateInstance(type) as Monster;
	}
}
var player = WorldMap.Instance.GetSelfPlayer();
var monster1 = MonsterFactory.Create(typeof(SkeletonInfo));
var monster2 = MonsterFactory.Create("Ghoust");
player.Damage(monster1.AttackDamage);
player.Damage(monster2.AttackDamage);

综上所述,我们基本可以认为带过多的switch-case或if-else的简单工厂模式是违背开闭原则的,会增加代码的耦合度,会造成当系统变得庞大时,极其难以维护。

其实我们有很多的方法,可以绕开过多swith-case或if-else的条件,以满足开闭原则来实现简单的工厂模式。

关于更多的工厂模式和开闭原则,我们今后再继续讨论,今天先说这么多。如果有什么错误或不对的,欢迎大家多多指正。



  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值