工厂模式是一种创建型模式,在工厂模式中,创建对象时不会对客户端暴露创建细节(将创建的代码封装到一个工厂类中),而是通过一个接口指向由工厂类创建并返回的对象。 对于设计模式和各个原则来说,无非就是要实现代码的高内聚和低耦合,还有尽量遵循开闭原则。开闭原则简单来说就是:软件中的对象(类,函数,模块)对扩展开放,对修改关闭。
于是,围绕开闭原则展开以下讨论。 传统工厂模式是如何违背了开闭原则的,工厂模式加反射机制是如何遵循开闭原则的。
以披萨为话题,我们要创建各种口味的披萨,例如奶酪披萨,水果披萨。 这个显然可以就通过工厂模式来实现,下面用传统工厂模式。 首先我们要声明一个IPizza接口:
public interface IPizza { void eat(); }IPizza的实现类:
//奶酪披萨 public class CheesePizza implements IPizza { private String pizzaType="奶酪披萨"; @Override public void eat() { System.out.println(pizzaType+"真香!"); } } //水果披萨 public class FruitPizza implements IPizza { private String pizzaType="水果披萨"; @Override public void eat() { System.out.println(pizzaType+"真好吃!"); } }下面通过工厂创建各种口味的披萨。
写一个工厂类,把创建各种口味披萨的任务交给它。
//披萨工厂类 public class PizzaFactory { private PizzaFactory(){} public static IPizza createPizza(String pizzaType){ IPizza pizza=null; if ("奶酪披萨".equals(pizzaType)) { pizza=new CheesePizza(); } else if ("水果披萨".equals(pizzaType)) { pizza=new FruitPizza(); } return pizza; } }测试:
public class PizzaTest { public static void main(String[] args) { IPizza pizza=PizzaFactory.createPizza("水果披萨"); pizza.eat(); pizza=PizzaFactory.createPizza("奶酪披萨"); pizza.eat(); } }控制台结果: OK!就是那么简单,但是问题来了,我现在突然想到还有一种口味的披萨:蛋黄披萨。我想把它添加到系统中,要怎么做呢? 第一步,实现IPizza接口
//蛋黄披萨 public class EggyolkPizza implements IPizza { private String pizzaType="蛋黄披萨"; @Override public void eat() { System.out.println(pizzaType+"贼香!"); } }第二步,修改工厂类(emmm,思考一下,这是不是违背了开闭原则)
修改过程就是添加一个else if分支。
public class PizzaFactory { private PizzaFactory(){} public static IPizza createPizza(String pizzaType){ IPizza pizza=null; if ("奶酪披萨".equals(pizzaType)) { pizza=new CheesePizza(); } else if ("水果披萨".equals(pizzaType)) { pizza=new FruitPizza(); } else if ("蛋黄披萨".equals(pizzaType)) { pizza=new EggyolkPizza(); } return pizza; } }代码搞定,测试一下: 添加披萨口味是成功了,但是我们也违背了开闭原则,如果有很多个口味的披萨引进来,那我们岂不是要改工厂类改到吐?
同样也是写一个工厂类,把创建各种口味披萨的任务交给它。
public class PizzaFactory { private PizzaFactory(){} public static IPizza createPizza(String pizzaType){ IPizza pizza=null; try { Class clazz=Class.forName(pizzaType); pizza=(IPizza)clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); } return pizza; } }测试:
public class PizzaTest { public static void main(String[] args) { IPizza pizza=PizzaFactory.createPizza("cn.thg.community.pizza.impl.CheesePizza"); pizza.eat(); pizza=PizzaFactory.createPizza("cn.thg.community.pizza.impl.FruitPizza"); pizza.eat(); } }控制台结果: OK! 添加一个口味的披萨,还是蛋黄披萨吧。 很简单,只需要扩展多一个类就行了,不需要取修改工厂类。 只需一步,扩展一个蛋黄披萨的类
public class EggyolkPizza implements IPizza { private String pizzaType="蛋黄披萨"; @Override public void eat() { System.out.println(pizzaType+"贼香!"); } }测试: 搞定。这样无论要加多少种口味,只需要拓展就行了,这不就很好的遵循了开闭原则吗,无论添加多少个披萨类,都不用对工厂类进行任何改变,直接在客户端(main方法)调用即可,相比较与传统工厂模式简直不要方便太多。
很显然,传统工厂模式违背了开闭原则(扩展新口味要修改工厂类),而工厂模式结合反射的方式则没有违背。只不过是后者传参的方式变成了传全限定类名。