为何使用?
工厂模式是我们最常用的模式了,著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。
为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑实用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。
我们以类Sample为例, 如果我们要创建Sample的实例对象:
Sample sample=new Sample();
可是,实际情况是,通常我们都要在创建sample实例时做点初始化的工作,比如赋值 查询数据库等。
首先,我们想到的是,可以使用Sample的构造函数,这样生成实例就写成:
Sample sample=new Sample(参数);
但是,如果创建sample实例时所做的初始化工作不是象赋值这样简单的事,可能是很长一段代码,如果也写入构造函数中,那你的代码很难看了(就需要Refactor重整)。
为什么说代码很难看,初学者可能没有这种感觉,我们分析如下,初始化工作如果是很长一段代码,说明要做的工作很多,将很多工作装入一个方法中,相当于将很多鸡蛋放在一个篮子里,是很危险的,这也是有背于Java面向对象的原则,面向对象的封装(Encapsulation)和分派(Delegation)告诉我们,尽量将长的代码分派“切割”成每段,将每段再“封装”起来(减少段和段之间偶合联系性),这样,就会将风险分散,以后如果需要修改,只要更改每段,不会再发生牵一动百的事情。
在本例中,首先,我们需要将创建实例的工作与使用实例的工作分开, 也就是说,让创建实例所需要的大量初始化工作从Sample的构造函数中分离出去。
这时我们就需要Factory工厂模式来生成对象了,不能再用上面简单new Sample(参数)。还有,如果Sample有个继承如MySample, 按照面向接口编程,我们需要将Sample抽象成一个接口.现在Sample是接口,有两个子类MySample 和HisSample .我们要实例化他们时,如下:
Sample mysample=new MySample();
Sample hissample=new HisSample();
随着项目的深入,Sample可能还会"生出很多儿子出来", 那么我们要对这些儿子一个个实例化,更糟糕的是,可能还要对以前的代码进行修改:加入后来生出儿子的实例.这在传统程序中是无法避免的.
但如果你一开始就有意识使用了工厂模式,这些麻烦就没有了.
今天我去市场,要决定是买水果等产品,还是选择种水果的产品。具体怎么操作自己选择。来到市场,我发现主要有一些水果:苹果(Apple),葡萄(Grape)和鸭梨(Pear)。到底买什么好呢?我一阵思量。俗话说:“饭后一只烟,赛过活神仙。饭后吃苹果,西施见我躲。”为了老婆的漂亮,我决定买苹果。
下面开始Factory模式研究,当然是用我上面举的例子来说明。
Simple Factory模式
专门定义一个类来负责创建其它类的实例,被创建的实例通常都具有共同的父类。
Factory Method模式
将对象的创建交由父类中定义的一个标准方法来完成,而不是其构造函数,究竟应该创建何种对象由具体的子类负责决定。 Abstract Factory模式
提供一个共同的接口来创建相互关联的多个对象。
一、Simple Factory模式:
1、在这里,我们先定义水果(Fruit)接口:
public interface Fruit { void plant(); // 水果是被种植的 void enableEat(); // 水果能吃 }
2、苹果(Apple)是对水果(Fruit)接口的实现:
public class Apple implements Fruit{ public void plant(){ System.out.println("种苹果!"); } public void enableEat(){ System.out.println("苹果好吃!"); } }
3、葡萄(Grape)是对水果(Fruit)接口的实现:
public class Grape implements Fruit{ public void plant(){ System.out.println("种葡萄!"); } public void enableEat(){ System.out.println("葡萄好吃!"); } }
4、鸭梨(Pear)是对水果(Fruit)接口的实现:
public class Pear implements Fruit{ public void plant(){ System.out.println("种鸭梨!"); } public void enableEat(){ System.out.println("鸭梨好吃!"); } }
5、定义买水果(BuyFruit)这一过程类:
public class BuyFruit { /** * 简单工厂方法 * @param which * @return */ public static Fruit buyFruit(String which){ if (which.equalsIgnoreCase("apple")) { // 如果是苹果,则返回苹果实例 return new Apple(); } else if (which.equalsIgnoreCase("pear")){ // 如果是鸭梨,则返回鸭梨实例 return new Strawberry(); } else if (which.equalsIgnoreCase("grape")) { // 如果是葡萄,则返回葡萄实例 return new Grape(); } else{ return null; } } }
6、 编写测试类:
public class FruitTest { public static void main(String args[]){ BuyFruit buy = new BuyFruit(); // 开始买水果这个过程 buy.buyFruit("apple").enableEat(); // 调用苹果的enableEat()方法 } }
7、 说明:
A:我要购买苹果,只需向工厂角色(BuyFruit)请求即可。而工厂角色在接到请求后,会自行判断创建和提供哪一个产品。
B:但是对于工厂角色(BuyFruit)来说,增加新的产品(比如说增加草莓)就是一个痛苦的过程。工厂角色必须知道每一种产品,如何创建它们,以及何时向客户端提供它们。换言之,接纳新的产品意味着修改这个工厂。
C:因此Simple Factory模式的开放性比较差。
有什么办法可以解决这个问题吗?那就需要Factory Method模式来为我们服务了。
二、Factory Method模式:
1、同样,我们先定义水果(Fruit)接口:
public interface Fruit { void plant(); // 水果是被种植的 void enableEat(); // 水果能吃 }
2、苹果(Apple)是对水果(Fruit)接口的实现:
public class Apple implements Fruit{ public void plant(){ System.out.println("种苹果!"); } public void enableEat(){ System.out.println("苹果好吃!"); } }
3、葡萄(Grape)是对水果(Fruit)接口的实现:
public class Grape implements Fruit{ public void plant(){ System.out.println("种葡萄!"); } public void enableEat(){ System.out.println("葡萄好吃!"); } }
4、鸭梨(Pear)是对水果(Fruit)接口的实现:
public class Pear implements Fruit{ public void plant(){ System.out.println("种鸭梨!"); } public void enableEat(){ System.out.println("鸭梨好吃!"); } }
5、在这里我们将买水果(BuyFruit)定义为接口类:
public interface BuyFruit{ /** * 工厂方法 * @return */ public Fruit buyFruit(); // 定义买水果这一过程 }
6、买苹果是(BuyApple)对买水果(BuyFruit)这个接口的实现
public class BuyApple implements BuyFruit{ public Fruit buyFruit(){ return new Apple(); // 返回苹果实例 } }
7、买鸭梨是(BuyPear)对买水果(BuyFruit)这个接口的实现
public class BuyPear implements BuyFruit{ public Fruit BuyPear (){ return new Pear(); // 返回鸭梨实例 } }
8、买葡萄是(BuyGrape)对买水果(BuyFruit)这个接口的实现
public class BuyGrape implements BuyFruit{ public Fruit BuyGrape (){ return new Grape (); // 返回葡萄实例 } }
9、编写测试类:
public class FruitTest { public static void main(String args[]){ BuyApple buy = new BuyApple(); // 开始买水果这个过程 buy.buyFruit().enableEat(); // 调用苹果的enableEat()方法 } }
10、说明:
A:工厂方法模式和简单工厂模式在结构上的不同是很明显的。工厂方法模式的核心是一个抽象工厂类,而简单工厂模式把核心放在一个具体类上。工厂方法模式可以允许很多具体工厂类从抽象工厂类中将创建行为继承下来,从而可以成为多个简单工厂模式的综合,进而推广了简单工厂模式。
B:工厂方法模式退化后可以变得很像简单工厂模式。设想如果非常确定一个系统只需要一个具体工厂类,那么就不妨把抽象工厂类合并到具体的工厂类中去。由于反正只有一个具体工厂类,所以不妨将工厂方法改成为静态方法,这时候就得到了简单工厂模式。
C:如果需要加入一个新的水果,那么只需要加入一个新的水果类以及它所对应的工厂类。没有必要修改客户端,也没有必要修改抽象工厂角色或者其他已有的具体工厂角色。对于增加新的水果类而言,这个系统完全支持“开-闭”原则。
D:对Factory Method模式而言,它只是针对一种类别(如本例中的水果类Fruit),但如果我们还想买肉,那就不行了,这是就必须要Abstract Factory Method模式帮忙了。
三、Abstract Factory模式
1、同样,我们先定义水果(Fruit)接口:
public interface Fruit { void plant(); // 水果是被种植的 void enableEat(); // 水果能吃 }
2、苹果(Apple)是对水果(Fruit)接口的实现:
public class Apple implements Fruit{ public void plant(){ System.out.println("种苹果!"); } public void enableEat(){ System.out.println("苹果好吃!"); } }
3、葡萄(Grape)是对水果(Fruit)接口的实现:
public class Grape implements Fruit{ public void plant(){ System.out.println("种葡萄!"); } public void enableEat(){ System.out.println("葡萄好吃!"); } }
4、鸭梨(Pear)是对水果(Fruit)接口的实现:
public class Pear implements Fruit{ public void plant(){ System.out.println("种鸭梨!"); } public void enableEat(){ System.out.println("鸭梨好吃!"); } }
5、定义肉(Meat)接口:
public interface Meat { void feed(); // 肉是喂养的 void enableEat(); // 肉能吃 }
6、猪肉(BigMeat)是对肉(Meat)接口的实现:
public class BigMeat implements Meat{ public void feed(){ System.out.println("养猪!"); } public void enableEat(){ System.out.println("猪肉好吃!"); } }
7、牛肉(CowMeat)是对肉(Meat)接口的实现:
public class CowMeat implements Meat { public void feed(){ System.out.println("养牛!"); } public void enableEat(){ System.out.println("牛肉好吃!"); } }
8、我们可以定义买货人(Buyer)接口:
public interface Buyer { /** * 买水果工厂方法 * @param whichFruit * @return */ public Fruit buyFruit(Fruit whichFruit); /** * 买肉的工厂方法 * @param whichMeat * @return */ public Meat buyMeat(Meat whichMeat); }
9、我(MyBuyer)是对买货人(Buyer)接口的实现:
public class MyBuyer implements Buyer{ /** * 买水果工厂方法 */ public Fruit buyFruit(Fruit whichFruit){ return whichFruit; } /** * 买肉的工厂方法 */ public Meat buyMeat(Meat whichMeat){ return whichMeat; } }
10、编写测试类:
public class MyBuyerAbstractTest { public static void main(String args[]){ Fruit apple = new Apple(); // 苹果实例 Meat big = new BigMeat(); // 猪肉实例 MyBuyer my = new MyBuyer(); // 我是买者的实例 my.buyFruit(apple).enableEat(); // 我买苹果 my.buyMeat(big).enableEat(); // 我买猪肉 } }
11、说明:
A:抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,创建多个产品族中的产品对象。这就是抽象工厂模式的用意。
B:抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。