工厂模式
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类
new对象的问题
当使用new 关键之创建一个对象的时候,你是在实例化一个具体的类,你使用的是实现,而不是接口。代码绑定着具体的类会导致代码更脆弱,更缺乏弹性。
例如如下代码,有一大堆不同的鸭子实现类时,但是必须等到运行时,才知道该实例化哪一个
1 2 3 4 5 6 7 8
| Duck duck; if (picnic) { duck = new MallardDuck(); } else if (hunting) { duck = new DecoyDuck(); } else if (inBathTub) { duck = new RubberDuck(); }
|
当看到这样的代码,一旦有变化或者扩展,就必须冲洗打卡这段代码进行检查和修改,通常修改这样的代码将造成部分系统更难维护和更新,而且也更容易犯错。
在技术上new 对象没有错,毕竟这是Java的基础部分,真正的的问题是我们的程序需要改变。针对接口编程,可以隔离掉以后系统可能发生的一大堆改变。如果代码是针对接口而写,那么通过多态,它可以与任何新类实现该接口,但是当代码使用大量的具体类时,就是自找麻烦,因为一旦加入具体的类,就必须改变代码,也就是说你的代码并非”对修改关闭”
简单工厂
简单工厂只负责创建对象,这个工厂可能不止服务于一个地方,以后对象类型如果有变动,只需修改简单工厂的逻辑就可以
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class SimplePizzaFactory { public Pizza createPizza(String type){ Pizza pizza=null; if("".equals(type)){ pizza= new CheesePizza(); }else if ("Clam".equals(type)){ pizza=new ClamPizza(); }else if ("Greek".equals(type)){ pizza=new GreekPizza(); } return pizza; } }
|
使用简单工厂,将new对象的操作移动到了简单工厂中,这里只传入订单类型来使用工厂创建对象
1 2 3 4 5 6 7 8 9 10 11 12
| public class SimplePizzaStore { SimplePizzaFactory simplePizzaFactory; public SimplePizzaStore(SimplePizzaFactory factory){ this.simplePizzaFactory=factory; } public Pizza orderPizza(String type){ Pizza pizza=this.simplePizzaFactory.createPizza(type); return pizza; } }
|
简单工厂其实是一种常用的编程习惯,而不是一种设计模式。
工厂方法
使用简单工厂只能穿件简单工厂中一种模式的对象,现在我们想使用不同的工厂创建不同的对象
1 2 3 4 5 6 7 8 9
| public abstract class AbstractPizzaStore { public final Pizza orderPizza(String type){ Pizza pizza=createPizza(type); return pizza; } protected abstract Pizza createPizza(String type); }
|
将创建pizza对象的方法抽象到PizzaStore中,然后让具体的子类去实现不同的创建方式,同时,将orderPizza方法在本类中设置为final,这个方法因为比较固定, 防止子类覆盖掉该方法
具体的pizza 店根据自己的需求创建不同风味的pizza
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class ChinesePizzaStore extends AbstractPizzaStore { @Override protected Pizza createPizza(String type) { if ("chesses".equals(type)) { return new ChineseStyleCheesePizza(); } else if ("clam".equals(type)) { return new ChineseStyleClamPizza(); } else { return new ChineseStyleCheesePizza(); } } }
|
测试入口
1 2 3 4 5 6
| public class Main{ public static void main(String[] args){ PizzStore pizzStore=new ChinesePizzStore(); pizzStore.orderPizza("cheese"); } }
|
工厂方法用来处理对象的创建,并将这样的行为封装在子类中,这样,客户程序中关于超类的代码就和子类对象创建代码解耦了
所有工厂模式都用来封装对象的创建,工厂方法模式,通过让子类决定该创建什么样的对象,来达到将对象创建过程封装的目的,让我们来看看这些类图。
工厂方法模式能够封装具体类型的实例化,看看下面的类图,抽象的Creator提供了一个创建对象的方法接口,也称为”工厂方法”,在抽象的Creator中,任何其他实现的方法,都可以使用到这个工厂方法所制造出来的产品,但是只有子类真正实现这个工厂方法并穿件产品。
抽象工厂
从上面的代码中可以看到,PizzaStore
中依赖了具体的Pizza
类。而设计原则中有一条要依赖抽象类,不要依赖具体的类,这也是依赖倒置原则(Dependency Inversion Principle),这个原则应该告诉我们重写代码以便于我们依赖抽象类,而不是具体的类。对于高层次及低层次模块都应该如此。
原则的应用
非常依赖PizzStore的主要问题在于,它依赖每个披萨类型,因为它是在自己的orderPizza方法中。实例化这些具体类型。
在为各种不同的PizzStore提供不同的原料时,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| public interface PizzaIngredientFactory { Dough createDough(); Sauce createSauce(); Cheese createCheese(); Clam createClam(); } public class NYPizzaIngredientFactory implements PizzaIngredientFactory { @Override public Dough createDough() { return new ThinCrustDough(); } @Override public Sauce createSauce() { return new MarinaraSauce(); } @Override public Cheese createCheese() { return new ReggianoCheese(); } @Override public Clam createClam() { return new FreshClams(); } } public class CheesePizza extends AbstractPizza { PizzaIngredientFactory ingredientFactory; public CheesePizza(PizzaIngredientFactory factory) { this.ingredientFactory = factory; } @Override void prepare() { log.info("Preparing ...."); super.dough = this.ingredientFactory.createDough(); super.cheese = this.ingredientFactory.createCheese(); super.clam = this.ingredientFactory.createClam(); super.sauce = this.ingredientFactory.createSauce(); } } } public class NYPizzaStore extends AbstractPizzaStore { public class NYPizzaStore extends AbstractPizzaStore { @Override protected Pizza createPizza(String item) { Pizza pizza=null; PizzaIngredientFactory ingredientFactory=new NYPizzaIngredientFactory(); if (item.equals("cheese")) { pizza = new CheesePizza(ingredientFactory); pizza.setName("New York Style Cheese Pizza"); } else if (item.equals("clam")) { pizza = new ClamPizza(ingredientFactory); pizza.setName("New York Style Clam Pizza"); } return pizza; } }
|
我们引入了新类型的工厂,也就是所谓的抽象工厂,来创建原料家族。通过抽象工厂所提供的接口,可以创建产品的家族,利用这个接口书写代码,我们的代码将从实际工厂解耦,以便在不同上下文中实现各式各样的工厂,制造出各种不同的产品。因为代码从实际的产品中解耦了,所以我们可以替换不同的工厂来取得不同的行为。
AbstractPizza 的代码利用相关的工厂生产原料。所生产的原料依赖所使用的工厂,Pizza类根本不关心这些原料。它只知道如何制作披萨。现在Pizza和区域原料之间被解耦。
下面是抽象工厂的类图关系
抽象工厂定义
抽象工厂提供了一个接口,用于创建相关领域或者依赖对象的家族,而不需要明确指定具体的类