1.什么是工厂模式?
工厂模式
-
简单工厂: 将创建一类对象的细节封装在一个对象中,外界只需要通过这个对象(工厂对象)根据特定的参数直接获取想要的对象即可。
-
工厂方法模式:定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
2.通过具体的需求实例来理解
2.1 员工A的困惑
在自己学习到一个原则后:要针对抽象编程,而不是针对具体实现编程。 员工A有啦自己的困惑,为啥我们知道这个原则,但是还是逃不掉new,要知道我们在代码里写出new A()的时候就已经是针对具体实现编程了!!
当看到new 的时候,就会想到具体,我们无法回避这个问题,例如我们在学习策略模式时:
Duck duck = new MallardDuck();
这里就还是针对具体实现编程,因为程序里还是依赖了具体的MallardDuck类,即使继承了抽象类Duck。
当我们需要根据某个条件在某群类中实例化一个的时候,我们的代码可能就会变成这样:
public class MakeDuck{
public boolean picnic;
public boolean hunting;
public boolean inBathTub;
public Duck getDuck(){
Duck duck;
if(picnic){
duck = new MallardDuck();
}else if(hunting){
duck = new DecoyDuck();
}else if(inBathTub){
duck = new RubberDuck();
}
duck.fly();
duck.quack();
duck.swim();
return duck;
}
}
当我们写了这样的代码后,一旦后面需要增加Duck实现类,或者更改对象构造条件,我们都需要打开这个类进行修改。 这样很明显让我们违反了 开闭原则(对扩展开放,对修改关闭)
我们好像陷入了僵局,陷入了new和设计原则的僵局了,那么如何破局呢? 员工B给出了他的看法
2.2 员工B的观察
员工B细心的发现,其实上面的根据条件构造不同对象的做法是不是就是我们编程过程中 变化的部分。 我们之前的策略模式在这里好像能给我们启发 : 找出变化的部分,把它们从不变的部分分离出来。
于是员工B结合公司正在做的披萨订单项目,实践了他的想法, 情况是这样的:
在这个项目里,进行披萨订单的方法是这样写的:
public static Pizza orderPizza(String type){
Pizza pizza = null;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}
assert pizza != null;
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
我们其实不难发现,其中变化的部分就是:
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}
我们可以把它提取出来,放到一个类里:
然后调用那个类去生产实例对象就好了,这样就把变化的部分提取出来了。
package factoryPattern.first;
public class PizzaFactory {
public static Pizza getPizza(String type){
Pizza pizza = null;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}
return pizza;
}
}
剩余完整代码如下:
PizzaStore.java
package factoryPattern.first;
public class PizzaStore {
void prepare(String pizzaName){
System.out.println("准备中 "+pizzaName);
System.out.println("和面团中....");
System.out.println("添加酱汁");
System.out.println("添加配料:");
}
void bake(){
System.out.println("准备烘烤 25 分钟...");
}
void cut(){
System.out.println("烘烤完成,进行切割");
}
void box(){
System.out.println("切割完毕,进行装盒");
}
public Pizza orderPizza(String type){
Pizza pizza = PizzaFactory.getPizza(type);
assert pizza != null;
prepare(pizza.getPizzaName());
bake();
cut();
box();
return pizza;
}
}
package factoryPattern.first;
public class Pizza {
private String pizzaName;
public String getPizzaName() {
return pizzaName;
}
public void setPizzaName(String pizzaName) {
this.pizzaName = pizzaName;
}
}
package factoryPattern.first;
public class CheesePizza extends Pizza{
public CheesePizza(){
this.setPizzaName("CheesePizza!!");
}
}
package factoryPattern.first;
public class GreekPizza extends Pizza{
public GreekPizza(){
this.setPizzaName("GreekPizza!!");
}
}
下面进行测试下:
package factoryPattern.first;
public class MainTest {
public static void main(String[] args) {
Pizza pizza = Pizza.orderPizza("cheese");
}
}
这样的话我们就可以在披萨种类变化的时候只修改变化的类PizzaFactory就好了。
这,就是简单工厂模式。
2.3 披萨订单系统的一次需求
现在客户的订单披萨系统不能满足部分需求了:随着披萨店越来越多,部分加盟店想要自己提供自己的特色披萨,然而目前系统好像只能做指定类型的披萨。例如纽约,芝加哥,加州。
这时员工A想了下,他想到了一个解决方案,下面我们来看看:
我们可以利用PizzaFactory,将PizzaFactory抽象成一个接口,写出三种不同的披萨工厂类实现它,分别为:
NYPizzaFactory , ChicagoPizzaFactory , CaliforniaPizzaFactory , 那么这几个加盟店就有了自己的披萨工厂类使用了。
UML如图:
下面我们来实现看看:
首先,先将这些Pizza的实体类建立起来:
package factoryPattern.second.pizza;
public class Pizza {
private String pizzaName;
public String getPizzaName() {
return pizzaName;
}
public void setPizzaName(String pizzaName) {
this.pizzaName = pizzaName;
}
}
package factoryPattern.second.pizza;
public class NYPizza1 extends Pizza{
public NYPizza1(){
this.setPizzaName("NYPizza1");
}
}
package factoryPattern.second.pizza;
public class NYPizza2 extends Pizza{
public NYPizza2(){
this.setPizzaName("NYPizza2");
}
}
package factoryPattern.second.pizza;
public class ChicagoPizza1 extends Pizza{
public ChicagoPizza1(){
this.setPizzaName("ChicagoPizza1");
}
}
package factoryPattern.second.pizza;
public class CaliforniaPizza1 extends Pizza{
public CaliforniaPizza1(){
this.setPizzaName("CaliforniaPizza1");
}
}
然后将纽约,加州,芝加哥披萨工厂建立起来:
package factoryPattern.second.factory;
import factoryPattern.second.pizza.Pizza;
public interface PizzaFactory {
Pizza getPizza(String type);
}
package factoryPattern.second.factory;
import factoryPattern.second.pizza.NYPizza1;
import factoryPattern.second.pizza.NYPizza2;
import factoryPattern.second.pizza.Pizza;
public class NYPizzaFactory implements PizzaFactory{
@Override
public Pizza getPizza(String type) {
Pizza pizza = null;
if(type.equals("NY1")){
pizza = new NYPizza1();
}else if(type.equals("NY2")){
pizza = new NYPizza2();
}
return pizza;
}
}
package factoryPattern.second.factory;
import factoryPattern.second.pizza.CaliforniaPizza1;
import factoryPattern.second.pizza.Pizza;
public class CaliforniaPizzaFactory implements PizzaFactory{
@Override
public Pizza getPizza(String type) {
Pizza pizza = null;
if(type.equals("California1")){
pizza = new CaliforniaPizza1();
}
return pizza;
}
}
package factoryPattern.second.factory;
import factoryPattern.second.pizza.ChicagoPizza1;
import factoryPattern.second.pizza.Pizza;
public class ChicagoPizzaFactory implements PizzaFactory {
@Override
public Pizza getPizza(String type) {
Pizza pizza = null;
if(type.equals("ChicagoPizza1")){
pizza = new ChicagoPizza1();
}
return pizza;
}
}
然后,编写PizzaStore类:
package factoryPattern.second;
import factoryPattern.second.factory.PizzaFactory;
import factoryPattern.second.pizza.Pizza;
public class PizzaStore {
private PizzaFactory pizzaFactory;
public PizzaStore(PizzaFactory pizzaFactory){
this.pizzaFactory = pizzaFactory;
}
void prepare(String pizzaName){
System.out.println("准备中 "+pizzaName);
System.out.println("和面团中....");
System.out.println("添加酱汁");
System.out.println("添加配料:");
}
void bake(){
System.out.println("准备烘烤 25 分钟...");
}
void cut(){
System.out.println("烘烤完成,进行切割");
}
void box(){
System.out.println("切割完毕,进行装盒");
}
public Pizza orderPizza(String type){
Pizza pizza = pizzaFactory.getPizza(type);
assert pizza != null;
prepare(pizza.getPizzaName());
bake();
cut();
box();
return pizza;
}
}
OK,新的Pizza订单系统创建完毕,下面我们进行使用测试一下:
package factoryPattern.second;
import factoryPattern.second.factory.CaliforniaPizzaFactory;
import factoryPattern.second.factory.ChicagoPizzaFactory;
import factoryPattern.second.factory.NYPizzaFactory;
public class MainTest {
public static void main(String[] args) {
//纽约分店
PizzaStore pizzaStore1 = new PizzaStore(new NYPizzaFactory());
pizzaStore1.orderPizza("NY1");
//芝加哥分店
PizzaStore pizzaStore2 = new PizzaStore(new ChicagoPizzaFactory());
pizzaStore2.orderPizza("ChicagoPizza1");
//加州分店
PizzaStore pizzaStore3 = new PizzaStore(new CaliforniaPizzaFactory());
pizzaStore3.orderPizza("California1");
}
}
完成,满足需求!!!
2.4 披萨订单系统的第二次需求
随着业务的扩展,市场的变化,一些加盟店虽然使用我们推出的披萨工厂进行创建披萨,但是在制作工艺和流程上,不同的加盟店的厨师却有着不同的见解,于是他们希望我们能提供一个更具弹性的订单系统,支持制作流程的特殊化。
这个要怎么实现呢? 领导将这个任务交给的员工B来做, 员工B结合现状, 思考了良久,给出了下面的方案:
下面来实现它:
Pizza.java
package factoryPattern.third.pizza;
public class Pizza {
private String pizzaName;
public String getPizzaName() {
return pizzaName;
}
public void setPizzaName(String pizzaName) {
this.pizzaName = pizzaName;
}
public void prepare(){
System.out.println("准备:"+ pizzaName);
}
public void bake(){
System.out.println("烘烤......");
}
public void cut(){
System.out.println("切片.....");
}
public void box(){
System.out.println("装盒.......");
}
}
然后定义具体的Pizza, 这样就可以给不同的披萨类定制不同的制作流程了。
package factoryPattern.third.pizza;
public class CaliforniaPizza1 extends Pizza {
public CaliforniaPizza1(){
this.setPizzaName("CaliforniaPizza1");
}
}
package factoryPattern.third.pizza;
public class ChicagoPizza1 extends Pizza {
public ChicagoPizza1(){
this.setPizzaName("ChicagoPizza1");
}
@Override
public void bake(){
System.out.println("ChicagoPizza1 烘烤流程变啦");
}
}
package factoryPattern.third.pizza;
public class NYPizza1 extends Pizza {
public NYPizza1(){
this.setPizzaName("NYPizza1");
}
}
package factoryPattern.third.pizza;
public class NYPizza2 extends Pizza {
public NYPizza2(){
this.setPizzaName("NYPizza2");
}
}
将PizzaStore定义成抽象类,之前工厂类的制作披萨的方法抽象到这里来。
package factoryPattern.third;
import factoryPattern.third.pizza.Pizza;
public abstract class PizzaStore {
public Pizza orderPizza(String type){
Pizza pizza = createPizza(type);
assert pizza != null;
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza createPizza(String type);
}
然后具体的Sotre去实现createPizza方法,这样既保证了不同加盟店的Pizza种类的不同,也实现 了不同Pizza的制作工艺的特殊化。
package factoryPattern.third.store;
import factoryPattern.third.PizzaStore;
import factoryPattern.third.pizza.ChicagoPizza1;
import factoryPattern.third.pizza.Pizza;
public class ChicagoPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
Pizza pizza = null;
if(type.equals("ChicagoPizza1")){
pizza = new ChicagoPizza1();
}
return pizza;
}
}
package factoryPattern.third.store;
import factoryPattern.third.PizzaStore;
import factoryPattern.third.pizza.NYPizza1;
import factoryPattern.third.pizza.NYPizza2;
import factoryPattern.third.pizza.Pizza;
public class NYPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
Pizza pizza = null;
if(type.equals("NY1")){
pizza = new NYPizza1();
}else if(type.equals("NY2")){
pizza = new NYPizza2();
}
return pizza;
}
}
下面进行测试:
package factoryPattern.third;
import factoryPattern.third.store.ChicagoPizzaStore;
import factoryPattern.third.store.NYPizzaStore;
public class MainTest {
public static void main(String[] args) {
PizzaStore pizzaStore1 = new NYPizzaStore();
pizzaStore1.orderPizza("NY1");
PizzaStore pizzaStore2 = new ChicagoPizzaStore();
pizzaStore2.orderPizza("ChicagoPizza1");
}
}
完成, 这次的需求问题解决了。 下面我们来认识一下工厂方法模式:
定义一个创建对象的接口,但是由子类去决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类中。
2.5 出现了依赖问题
尽管上面的设计已经好像看着可以,但是,我们好像也能明显的发觉到对于具体的PizzaStore的编写,我们好像违反了一个原则: 不要依赖具体,要依赖抽象。
例如我们看一下NYPizzaStore,它里面好像依赖了具体的Pizza对象,现在看来它就依赖了两个,但是如果长期以这样的方式来做的话,不难发现我们会进入一个依赖噩梦。
package factoryPattern.third.store;
import factoryPattern.third.PizzaStore;
import factoryPattern.third.pizza.NYPizza1;
import factoryPattern.third.pizza.NYPizza2;
import factoryPattern.third.pizza.Pizza;
public class NYPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
Pizza pizza = null;
if(type.equals("NY1")){
pizza = new NYPizza1();
}else if(type.equals("NY2")){
pizza = new NYPizza2();
}
return pizza;
}
}
如何解决这个问题,下一节的抽象工厂模式也许能解决这个问题,点此跳转:
抽象工厂模式解决此问题的方案
抽象工厂模式:提供一个接口用于创建相关或依赖对象的家族,而不需要明确指定具体类。