系列文章
引言
今天是冬至,去饺子馆吃饺子,看他们店里面的水饺种类挺多,在等待中,在想是不是可以用设计模式模拟一下,生产饺子的过程,正好最近也在看工厂模式,也就现学现卖了。当然,实现的方式很多,只是一个例子而已。祝大家冬至,多多吃水饺.....
对象创建的问题?
我们应该面向接口编程而不是面向实现编程,因为面向实现编程会使得我们的设计更脆弱,缺乏灵活性。但是我们每次使用new时,是不是正在违背这一设计原则呢?
当我们拥有一组相关的具体类时,是不是常常被迫写出类似下面的代码?
1 Duck duck; 2 3 if(picnic){ 4 5 duck=new MallardDuck(); 6 7 }else if(hunting){ 8 9 duck=new DecogDuck();10 11 }else if(inBathTub){12 13 duck=new RubberDuck();14 15 }(以上为伪代码,只为说明问题)
向上面的实例化过程,知道运行时我们才知道需要实例化哪个类。
这样做的后果是如果应用要做变化或扩展,往往要修改这段代码。这使得维护困难,并容易引入错误。
问题在哪儿?
出现上面那种问题,是不是new的问题呢?
从技术上来说,new并没有任何问题。new只是面向对象语言的最基本部分,真正的问题在于“变化”。
如果对接口编程,我们可以实现与许多“变化”的隔离,因为通过多态机制,我们的代码对于实现接口的新类依然适用。但是使用具体类麻烦就来了,因为增加新的具体类时相应的代码可能就必须修改?
怎么办?
面向对象的设计原则:识别变化的部分,并将与不变化的部分相分离。
书中Pizza店案例分析
PizzaStore类中的一段代码-订做pizza
修改后的代码
由于市场竞争,其他pizza店推出了新产品,我们也得增加!例如VeggiePizza。 GreekPizza最近不受欢迎,把它从菜单中取消。
于是。。。
分析:变与不变的部分
分离
我们将专管制作pizza的对象叫做Pizza工厂
Pizza工厂---SimplePizzaFactory
思考一下?
这看来好像我们只是把问题从一个对象推给了另一个对象!这样做有什么好处?
SimplePizzaFactory可以有许多个客户,这样,当实现改变时我们只需要修改SimplePizzaFactory,而不需修改众多的客户。 提高了聚合度,PizzaStore的职责是使用pizza对象, SimplePizzaFactory的职责是决定创建什么样的pizza对象。
用工厂重写PizzaStore类
1 public class PizzaStore { 2 SimplePizzaFactory factory; 3 public PizzaStore(SimplePizzaFactory factory) { 4 this.factory = factory; 5 } 6 public Pizza orderPizza(String type) { 7 Pizza pizza; 8 pizza=factory.createPizza(type); 9 pizza.prepare();10 pizza.bake();11 pizza.cut();12 pizza.box();13 return pizza;14 }15 //other methods here16 }
简单工厂模式
饺子馆中的简单工厂实现
水饺要实现的接口
1 ///2 /// 水饺要实现的接口 3 /// 4 public interface IDumpling 5 { 6 string DumplingName { get; } 7 ///8 /// 水饺的准备阶段方法 9 /// 10 void Prepare();11 ///12 /// 煮13 /// 14 void Boild();15 ///16 /// 展示订单17 /// 18 void Show();19 }
具体的水饺类
1 ///2 /// 猪肉大葱水饺类 3 /// 4 public class PorkAndScallionDumpling:IDumpling 5 { 6 7 public string DumplingName 8 { 9 get { return "猪肉大葱水饺"; }10 }11 12 public void Prepare()13 {14 Console.WriteLine("准备猪肉茴香水饺25个");15 }16 17 public void Boild()18 {19 Console.WriteLine("正在煮......请稍等.....");20 }21 22 public void Show()23 {24 Console.WriteLine("您的{0},准备好了。", this.DumplingName);25 }26 }
1 ///2 /// 猪肉茴香水饺类 3 /// 4 public class PorkAndFennelDumpling : IDumpling 5 { 6 public string DumplingName 7 { 8 get { return "猪肉茴香水饺"; } 9 }10 11 public void Prepare()12 {13 Console.WriteLine("准备猪肉茴香水饺25个");14 }15 16 public void Boild()17 {18 Console.WriteLine("正在煮......请稍等.....");19 }20 21 public void Show()22 {23 Console.WriteLine("您的{0},准备好了。", this.DumplingName);24 }25 }
工厂类
1 ///2 /// 水饺的生产工厂 3 /// 4 public static class SimpleDumplingFactory 5 { 6 public static IDumpling CreateDumpling(string dumplingName) 7 { 8 IDumpling dumpling = null; 9 switch (dumplingName)10 {11 case "猪肉大葱":12 dumpling = new PorkAndScallionDumpling();13 break;14 case "猪肉茴香":15 dumpling = new PorkAndFennelDumpling();16 break;17 }18 return dumpling;19 }20 }
控制台代码
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 IDumpling dumpling = SimpleDumplingFactory.CreateDumpling("猪肉茴香"); 6 dumpling.Prepare(); 7 dumpling.Boild(); 8 dumpling.Show(); 9 Console.Read();10 }11 }
结果
授权pizza店
我们的pizza店非常成功,许多人都想开设我们的授权加盟店。为保证质量,我们希望他们使用我们经过时间考验的代码。
但是,不同地区的加盟pizza店可能希望供应不同口味的pizza。怎么解决这个问题呢?解决方法之一:建立不同的工厂
1 //建立不同的工厂:如NYPizzaFactory、 ChicagoPizzaFactory、 CaliforniaPizzaFactory,在PizzaStore中包含相应工厂的实例。其代码类似于: 2 //该pizza店提供纽约风味的pizza 3 NYPizzaFactory nyFactory=new NYPizzaFactory();//建立一个生产纽约风味pizza的工厂 4 PizzaStore nyStore=new PizzaStore(nyFactory);//建立一个pizza店,引用纽约风味pizza的工厂 5 nyStore.orderPizza(“Veggie”);//生产的是纽约风味的pizza 6 7 //该pizza店提供芝加哥风味的pizza 8 ChicagoPizzaFactory chicagoFactory=new ChicagoPizzaFactory(); 9 PizzaStore chicagoStore=new PizzaStore(chicagoFactory);10 chicagoStore.orderPizza(“Veggie”);
抽象工厂模式
这么多工厂,可以再增加抽象层
另一种解决方法-工厂方法模式思路:改写的PizzaStore,将createPizza()方法放回到PizzaStore,但是声明为抽象方法,然后,为每一种地方风味创建一个PizzaStore的子类。
改造后的PizzaStore的代码
1 public abstract class PizzaStore { 2 3 public Pizza orderPizza(String type) { 4 Pizza pizza = createPizza(type);//在PizzaStore内调用自身的一个方法来制造pizza,而不是使用一个factory对象 5 6 pizza.prepare(); 7 pizza.bake(); 8 pizza.cut(); 9 pizza.box();10 return pizza;11 }12 abstract Pizza createPizza(String type);//factory对象成了这里的一个抽象方法13 14 }
下面我们需要PizzaStore的各种子类(对应不同的地区风味)
让子类做决定
声明工厂方法
abstract Pizza createPizza(String type);abstract Product factoryMethod(String type);
工厂方法是抽象的,在一个超类中定义。必须由子类来实现。
工厂方法返回一个产品,该产品通常在其所在类的方法中定义。(如orderPizza())工厂方法通常提供参数,用以选择一个产品的不同品种。工厂方法将客户(超类中的方法,如PizzaStore中的orderPizza())与具体的产品相隔离。工厂方法怎么工作?
假定张三喜欢纽约风味的pizza,李四喜欢芝加哥风味的pizza。
需要相应Pizza店的实例调用orderPizza()订购想要的pizza品种createPizza()被调用,并返回pizza到orderPizza()方法。尽管不知道是什么pizza,但orderPizza()仍知道对它进行后续处理。工厂方法模式中的类
创建者类 The Creator classes
产品类 The Product classes
工厂方法模式的定义
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法模式让一个类的实例化延迟到其子类。
工厂方法模式的结构
总结:Factory Method模式
意图
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法模式让一个类的实例化延迟到其子类。别名虚拟构造器Factory Method—参与者
Product(document)定义工厂方法所创建对象的接口。
ConcreteProduct(mydocument)实现product接口。Creator(application)声明工厂方法,可以调用工厂方法以创建一个product对象ConcreteCreator (MyApplication)重新定义工厂方法,以返回一个ConcreteProduct实例(篇幅有点长,关于工厂方法模式的实例就不再列举了,感兴趣的可以自己实现一下)
参考书:
《First Head 设计模式》