1:主题拆解
①依赖倒置原则-SimpleFactory
②简单工厂+ 配置文件=可配置
③简单工厂+ 配置文件+反射=可配置可扩展
④简单工厂升级IOC控制反转
2:基本介绍
①学习设计模式的套路:场景出发-->解决问题-->沉淀总结-->推广应用(无尽升级)
②需要指出GOF23种设计模式是不包含简单工厂,因此上面写的是 附1
③一句话描述:简单工厂设计模式包含一组需要创建的对象,通过一个工厂类来实例化对象
④依赖倒置原则:上层模块不应该依赖于下层模块,二者应该通过抽象来依赖
依赖抽象,而不是依赖细节。
3:场景介绍
我们以魔兽争霸冰封王座的游戏背景来解析当前的设计模式。
①定义了4个种族,依次为Human,NE,ORC,Undead
public class Human : IRace
{
public void ShowKing()
{
Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Sky");
}
}
public class NE : IRace
{
public void ShowKing()
{
Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Moon");
}
}
public class ORC : IRace
{
public void ShowKing()
{
Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Grubby");
}
}
public class Undead : IRace
{
public void ShowKing()
{
Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "GoStop");
}
}
②定义了一个接口,包括ShowKing方法。给上面四个种族继承
public interface IRace
{
void ShowKing();
}
③定义一个玩家类Player,包含方法4个方法PlayHuman,PlayNE,PlayORC,PlayUndead
public class Player
{
public int Id { get; set; }
public string Name { get; set; }
public void PlayHuman(Human human)
{
Console.WriteLine("******************************");
Console.WriteLine("This is {0} Play War3.{1}", this.Name, human.GetType().Name);
human.ShowKing();
}
public void PlayNE(NE ne)
{
Console.WriteLine("******************************");
Console.WriteLine("This is {0} Play War3.{1}", this.Name, ne.GetType().Name);
ne.ShowKing();
}
public void PlayORC(ORC orc)
{
Console.WriteLine("******************************");
Console.WriteLine("This is {0} Play War3.{1}", this.Name, orc.GetType().Name);
orc.ShowKing();
}
public void PlayUndead(Undead undead)
{
Console.WriteLine("******************************");
Console.WriteLine("This is {0} Play War3.{1}", this.Name, undead.GetType().Name);
undead.ShowKing();
}
}
④基于上面3步完成之后,玩家就可以开始进行War3游戏了。
例如玩家Micro,依次开始玩遍四个种族,实现的代码如下
Player player = new Player()
{
Id = 666,
Name = "Micro"
};
{
Human human = new Human();
player.PlayHuman(human);
NE ne = new NE();
player.PlayNE(ne);
ORC orc = new ORC();
player.PlayORC(orc);
Undead undead = new Undead();
player.PlayUndead(undead);
}
4:解决问题
从上层模块的调用可以看出,每次调用一个种族都需要对下端的细节形成了依赖。
根据设计模式依赖导致原则,该方式的代码得要做升级。
升级1
Player添加方法,用面向接口,面向抽象的方式实现
public void Play(IRace iRace)
{
Console.WriteLine("******************************");
Console.WriteLine("This is {0} Play War3.{1}", this.Name, iRace.GetType().Name);
iRace.ShowKing();
}
IRace human = new Human();
IRace ne = new NE();
IRace orc = new ORC();
IRace undead = new Undead();
player.Play(human);
player.Play(ne);
player.Play(orc);
player.Play(undead);
分析:左边换成抽象,依赖倒置原则,采用接口的方式。但是我们发现右边照样也是依赖的细节
升级2
添加一个工厂类,用于解决四个种族的创建
public class ObjectFactory
{
public static IRace CreateInstance(RaceType raceType)
{
IRace race = null;
switch (raceType)
{
case RaceType.Human:
race = new Human();
break;
case RaceType.NE:
race = new NE();
break;
case RaceType.ORC:
race = new ORC();
break;
case RaceType.Undead:
race = new Undead();
break;
default:
throw new Exception("wrong raceType");
}
return race;
}
}
public enum RaceType
{
Human,
NE,
ORC,
Undead
}
IRace human = ObjectFactory.CreateInstance(RaceType.Human);
IRace ne = ObjectFactory.CreateInstance(RaceType.NE);
IRace orc = ObjectFactory.CreateInstance(RaceType.ORC);
IRace undead = ObjectFactory.CreateInstance(RaceType.Undead);
player.Play(human);
player.Play(ne);
player.Play(orc);
player.Play(undead);
分析:到此为此,简单工厂的模式就已经结束了。
5:简单工厂优缺点
优点
①使用到了依赖倒置原则,上层模块不应该依赖于下层模块,二者应该通过抽象来依赖
依赖抽象,而不是依赖细节。
②去掉上端对细节的依赖,保证上端的稳定。
③上端不必管这些对象究竟如何创建及如何组织的.明确了各自的职责和权利,有利于整个软件体系结构的优化。
缺点
①没有消除矛盾,只是转移矛盾,甚至还集中了矛盾。
②由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中,职责过重,一旦异常,整个系统将受影响。
③它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。
④简单工厂模式使用了static工厂方法,造成工厂角色无法形成基于继承的等级结构
6:可配置版升级
通过配置文件的方式实现初始化不同的工厂实例
①配置文件
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<appSettings>
<add key="IRaceType" value="Human"/>
</appSettings>
</configuration>
②工厂类中添加通过配置文件的方式指定实例化对象
private static string IRaceType = ConfigurationManager.AppSettings["IRaceType"];
public static IRace CreateInstanceConfig()
{
RaceType raceType = (RaceType)Enum.Parse(typeof(RaceType), IRaceType);
return CreateInstance(raceType);
}
③上端调用
IRace race = ObjectFactory.CreateInstanceConfig();
player.Play(race);
分析:到此为此,我们就可以在不改动任何源码的情况下,只通过修改配置文件中的值就能实现类的实例化
7:可配置可扩展升级版
真正的把细节依赖给去掉,意味着不能直接new,那怎么获取对象? 于是可以想到反射
①配置文件
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<appSettings>
<add key="IRaceTypeReflection" value="FactoryPattern.War3.Service.Human,FactoryPattern.War3.Service"/>
</appSettings>
</configuration>
②工厂类是反射的实现方法
private static string IRaceTypeReflection = ConfigurationManager.AppSettings["IRaceTypeReflection"];
public static IRace CreateInstanceConfigReflection()
{
Assembly assembly = Assembly.Load(IRaceTypeReflection.Split(',')[1]);
Type type = assembly.GetType(IRaceTypeReflection.Split(',')[0]);
return (IRace)Activator.CreateInstance(type);
}
③上端调用
IRace race = ObjectFactory.CreateInstanceConfigReflection();
player.Play(race);
分析:此时我们就已经实现了,通过配置文件+简单工厂+反射实现完全面向抽象的方式调用。
8:如何扩展
对于上面可配置可以扩展,扩展点在哪里?如何扩展?
假如War3升级新增加了第5种族,该如何实现?
咱也不废话,直接上源码
①新建项目,添加第5种族类,继承自IRace
public class Five : IRace
{
public void ShowKing()
{
Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Alex");
}
}
②配置文件设置成第5种族
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<appSettings>
<add key="IRaceTypeReflection" value="FactoryPattern.War3.ServiceExtend.Five,FactoryPattern.War3.ServiceExtend"/>
</appSettings>
</configuration>
③上端调用部分不变
IRace race = ObjectFactory.CreateInstanceConfigReflection();
player.Play(race);
分析:此时我们发现在不更改原代码的情况下,只需额外添加新的第5种族,就可以实现对第5种族的扩展。
9:简单工厂升级IOC控制反转
①相信我们发现任何设计模式都是解决一类问题的,不是万能的,通常在解决一类问题的时候,还会带来新的问题。
②我们可以不停的采用技术手段扬长避短,对程序不停的进行升级,相信在不断升级的过程中,我们自己对知识的综合利用能力的总结。
③拆解到此处,我们其实也明白了系统框架中的IOC控制反转,依赖倒置等等无非就是设计模式进行升级的产品。