小白学习设计模式——简单工厂、工厂方法、抽象工厂
其实看设计模式已有一段时间了,主要是看《大话设计模式》还有四人帮的《设计模式》,总觉得理解的不够全面,这应该跟项目经验有关系,所谓的理论和实践并重。但又觉得学习本来就是这样,对一个知识有一定了解,以后运用或磕碰时再刷新对这个知识的认知,学学还是有好处的~
用个故事来描述吧,以下故事为虚实结合而成......
小白正在为公司开发一款射击类游戏,负责的是选枪的模块;提交的代码如下
static void Main(string[] args) //客户端代码 { Console.WriteLine("请输入要选择的武器:A.步枪 B.机关枪 C.狙击枪"); string fireType = Console.ReadLine(); Weapon fireWeapon = null; switch (fireType) { case "A": fireWeapon = new 步枪(); break; case "B": fireWeapon = new 机关枪(); break; case "C": fireWeapon = new 狙击枪(); break; default: Console.WriteLine("请选择正确的武器"); break; } } abstract class Weapon { //...武器抽象类 } class 步枪:Weapon { //...省略 } class 机关枪:Weapon { //...省略 } class 狙击枪:Weapon { //...省略 }
提交代码后小白正哼着愉悦的曲子准备打卡,突然那熟悉而又鬼魅的声音打断他。
总监突然出现在后面说道:"小白,去吃饭啦?赶紧去~吃饭回来啊~”。
小白:"其实我是下...班"。
总监:"你看你写些什么!还下班,你怎么可以这么写呢?什么都塞到客户端,这样客户端代码多恶心啊!别吃饭了,快使用简单工厂模式改改!"
"...好,我这就改!",小白说道。
不一会儿,小白的代码改出来了:
static void Main(string[] args) { Console.WriteLine("请输入要选择的武器:A.步枪 B.机关枪 C.狙击枪"); string fireType = Console.ReadLine(); Weapon fireWeapon = WeaponFactory.createWeapon(fireType); } abstract class Weapon { //...武器抽象类 } class 步枪:Weapon { //...省略 } class 机关枪:Weapon { //...省略 } class 狙击枪:Weapon { //...省略 } //增加一个工厂类 public class WeaponFactory { public static Weapon createWeapon(string fireType) { Weapon fireWeapon = null; switch (fireType) { case "A": fireWeapon = new 步枪(); break; case "B": fireWeapon = new 机关枪(); break; case "C": fireWeapon = new 狙击枪(); break; default: Console.WriteLine("请选择正确的武器"); break; } return fireWeapon; } }
总监:"恩,这样客户端整洁了,而且还能把这个功能复用到web等程序中。"
简单工厂:就是运用基本的继承多态和抽取工厂方法到达代码整洁和方法复用,自己总结的,书上并没有定义。
总监:"但是我现在要添加一个武器怎么办?"
小白:"增加一个"Weapon的子类和增加一个case条件咯"
总监:"那是不是违反了面向对象的开闭原则(不能修改已经写好的类,只能扩展),我一天增加一种新武器,你这个类岂不是要改烂?赶紧用工厂方法改一下"
小白:"我肚子饿了..."
不一会儿,小白的代码又改出来了:
static void Main(string[] args) //客户端代码 { Console.WriteLine("请输入要选择的武器:A.步枪 B.机关枪 C.狙击枪"); string fireType = Console.ReadLine(); Weapon fireWeapon = null; switch (fireType) { case "A": fireWeapon = new 步枪工厂().CreateWeapon(); break; case "B": fireWeapon = new 机关枪工厂().CreateWeapon(); break; case "C": fireWeapon = new 狙击枪工厂().CreateWeapon(); break; default: Console.WriteLine("请选择正确的武器"); break; } } abstract class Weapon { //...武器抽象类 } class 步枪:Weapon { //...省略 } class 机关枪:Weapon { //...省略 } class 狙击枪:Weapon { //...省略 } interface IFactory { //...工厂接口 Weapon CreateWeapon(); } class 步枪工厂 : IFactory { public Weapon CreateWeapon() { return new 步枪(); } } class 机关枪工厂 : IFactory { public Weapon CreateWeapon() { return new 机关枪(); } } class 狙击枪工厂 : IFactory { public Weapon CreateWeapon() { return new 狙击枪(); } }
小白:"这样子就解决了工厂方法的问题啦,这样只要在有新增的武器我就加个武器类和相应的工厂类就行啦~吃饭咯~"
有个问题困扰了我很久,什么工厂方法,不觉得代码看起来跟一开始一样吗?只是套了个工厂的壳,还不如直接创建对象,我觉得有这样的想法还是因为项目经验的不足导致的。直到我看到了百度百科中提到java的collection中的iterator运用了工厂方法(这里可以把list看做工厂,list.iterator就是创建对象的工厂方法)。设计模式多数可能是嵌套应用在某一个特定场景,所以当你遇到这些场景之后才能算真正理解设计模式带来的好处。
”工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中“这个是设计模式书中的原话,一直都不知道他什么意思,也是看到下面java迭代器的例子才明白(创建出来的是什么迭代器,完全是由集合的子类来决定的,附java代码)
List list=new ArrayList(); Iterator iter = list.iterator(); while(iter.hasNext()) { //do something... }
总监:"嗯...问题是解决了,可是客户端又变恶心了,赶紧去给我处理下,用反射什么的试试...这么晚了外面餐馆也关门了,待会请你吃泡面吧"
小白:"卧槽都饿扁了吃泡面哪够...."
private static readonly string AssemblyName = "当前程序集名称"; private static readonly string factory = ConfigurationManager.AppSettings["factory"]; static void Main(string[] args) //客户端代码 { IFactory weaponFactory = (IFactory)Assembly.Load("当前程序集名称").CreateInstance("当前程序集名称" + "." + factory);
Weapon.fireWeapon= weaponFactory.CreateWeapon(); } //....下面的代码与上面一样,省略
小白:"这样只要在配置文件中更改要创建的武器名字就可以啦~"
总监:"恩...好吧,今天就到这里,给你泡面,下班吧。"
小白:"ye!!泡面泡起来!"
客户:"等下,小白同学,我这里怎么报错了奇怪了,你过来看看"
小白:"卧槽客户你哪冒出来的,这么晚了还在这里!"
小白:"......卧槽你搞什么,步枪的子弹怎么能跟火箭筒一起用!?"
客户:"不小心按到.....不对,都怪你,怎么可以让我按错呢,你程序不应该有什么容错性的吗?赶紧给我改!"
总监:"还不快去改!用抽象工厂试试!"
小白:"我..要..睡觉TT"
private static readonly string AssemblyName = "当前程序集名称"; private static readonly string factory = ConfigurationManager.AppSettings["factory"]; static void Main(string[] args) //客户端代码 { IFactory weaponFactory = (IFactory)Assembly.Load("当前程序集名称").CreateInstance("当前程序集名称" + "." + factory); Weapon fireWeapon = weaponFactory.CreateWeapon(); Bullet fireBullet =weaponFactory.CreateBullet(); } abstract class Weapon { //...武器抽象类 } class 步枪:Weapon { //...省略 } class 机关枪:Weapon { //...省略 } class 狙击枪:Weapon { //...省略 } abstract class Bullet { //...子弹抽象类 } class 步枪子弹 : Bullet { //...省略 } class 机关枪子弹 : Bullet { //...省略 } class 狙击枪子弹 : Bullet { //...省略 } interface IFactory { //...工厂接口 Weapon CreateWeapon(); Bullet CreateBullet(); } class 步枪工厂 : IFactory { public Weapon CreateWeapon() { return new 步枪(); } public Bullet CreateBullet() { return new 步枪子弹(); } } class 机关枪工厂 : IFactory { public Weapon CreateWeapon() { return new 机关枪(); } public Bullet CreateBullet() { return new 机关枪子弹(); } } class 狙击枪工厂 : IFactory { public Weapon CreateWeapon() { return new 狙击枪(); } public Bullet CreateBullet() { return new 狙击枪子弹(); } }
"提供一个创建产品的接口来负责创建相关或依赖的对象,而不具体明确指定具体类。"这句是设计模式书上的原话,有了这个例子就好理解了,抽象工厂相对于工厂方法来说添加了"组"的概念,就不会出现"火箭筒用步枪子弹这种错误",同时也提高了扩展性,后期要是还出个什么枪套之类的对象,只需要在相应的工厂添加构造方法即可。
写这一个故事也是纪念下通宵的那一晚,哈哈~还有以上代码并没有测试,难免有错,还有对于设计模式的理解仅代表个人观点,假如哪错了或者什么的,请大家纠正指导!
天亮了,小白还在敲代码
这时候老板来了,"小白,今天这么早~待会跟我一起去总部开个会。"
小白:"老板...我能不能带个枕头去啊?"