工厂模式是一种创建性模式。
它是最常用的一种设计模式,在Java系统中随时可见。(在DOM解析XML时就用到了) 。
它定义了一个创建对象的接口,让子类来决定具体实例化那一个类。
当一个类无法预料要创建那种类的对象时;或者一个类需要子类来指定创建的对象时, 我们就需要用到工厂模式。
简单来说,工厂模式可以根据不同的条件产生不同的实例。当然这些不同的实例都是属于相同类型的。
工厂模式把创建这些实例的具体过程封装起来。简化了实例化的操作,也改善了程序的扩展性和可延伸性。
上代码:
一、静态(简单)工厂模式:
比如说要吃面条,就要抽出一个面条的基类。让面馆作为一个工厂来决定做什么样的面条。
产品的抽象类:
public abstract class Inoodles {
/*
* 面条基类
* 描述每种面条啥样的
*/
public abstract void desc();
}
兰州拉面:
public class LzNooldles extends Inoodles{
/*
* 兰州拉面
*/
@Override
public void desc() {
System.out.println("兰州拉面");
}
}
泡面:
public class PaoNoodles extends Inoodles {
@Override
public void desc() {
System.out.println("加班必备的泡面");
}
}
油泼面:
public class YouPoNooldes extends Inoodles {
@Override
public void desc() {
System.out.println("陕西油泼面");
}
}
面馆工厂类:
ublic class SimpleNooldlesFactory {
/*
* 面工厂
*/
/*
* 工场是一个具体的类,而不是接口抽象类。有一个重要的创建对象create()的方法。
* create()方法是静态的。所以也称为静态工厂
* 缺点:
* 扩展性差。
* 不同产品需要不同的额外参数时,特别麻烦。
*/
public static final int TYPE_LZ=1;
public static final int TYPE_PM=2;
public static final int TYPE_YP=3;
public static Inoodles createNoodles(int type){
switch (type) {
case TYPE_LZ:
return new LzNooldles();
case TYPE_PM:
return new PaoNoodles();
case TYPE_YP:
default:
return new YouPoNooldes();
}
}
}
测试:
public static void main(String[] args) {
Inoodles noodes=SimpleNooldlesFactory.createNoodles(SimpleNooldlesFactory.TYPE_PM);
noodes.desc();
}
输出:
加班必备的泡面
特点:
抽象工厂作为具体的类,而不是接口或者抽象类。
有一个重要的create()方法,利用if或者 switch创建产品并返回。
create()方法通常是静态的,所以也称之为静态工厂。
缺点
扩展性差(我想增加一种面条,除了新增一个面条产品类,还需要修改工厂类方法)
不同的产品需要不同额外参数的时候 不支持。
二:反射(简单)工厂模式:
利用反射Class.forName(clz.getName().newInstance())
实现的简单工厂
ublic class StaticNoodlesFactory {
/*
* 利用反射Class。forName(clz.getName().newInstance())实现的简单工厂
*/
@SuppressWarnings("unchecked")
public static <T extends Inoodles> T createNoodles(Class<T>clz){
T result=null;
try {
result=(T) Class.forName(clz.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return result;
}
}
测试类:
public class Test {
public static void main(String[] args) {
Inoodles lz=StaticNoodlesFactory.createNoodles(PaoNoodles.class);
lz.desc();
}
}
输出结果:
加班必备的泡面
特点
它也是一个具体的类,非接口抽象类。但它的create()方法,是利用反射机制生成对象返回,好处是增加一种产品时,不需要修改create()的代码。
缺点
这种写法粗看牛逼,细想之下,不谈reflection的效率还有以下问题:
Class.forName(clz.getName()).newInstance()调用的是无参构造函数生成对象,
它和new Object()是一样的性质,而工厂方法应该用于复杂对象的初始化 ,当需要调用有参的构造函数时便无能为力了,这样像为了工厂而工厂。
不同的产品需要不同额外参数的时候 不支持。
三:多工厂模式(常用)
使用前两种实现的工厂,都有一个缺点:不同的产品需要不同额外参数的时候 不支持。
而且如果使用时传递的type、Class出错,将不能得到正确的对象,容错率不高。
而多方法的工厂模式为不同产品,提供不同的生产方法,使用时 需要哪种产品就调用该种产品的方法,使用方便、容错率高。
public class MulWayNoodlesFactory {
/*
* 生产泡面
*/
public static Inoodles createPao(){
return new PaoNoodles();
}
/*
* 生产兰州拉面
*/
public static Inoodles createLz(){
return new LzNooldles();
}
/*
* 生产油泼面
*/
public static Inoodles createYou(){
return new YouPoNooldes();
}
}
public class Test {
public static void main(String[] args) {
Inoodles in=MulWayNoodlesFactory.createPao();
in.desc();
}
}
加班必备的泡面
这种模式优点较大,增加一个新面条,只要去增加一个static方法即可,也不修改原方法逻辑。
四:普通工厂
public abstract class NoodlesFactory {
public abstract Inoodles create();
}
public class LzNooldlese extends NoodlesFactory{
/*
* 兰州拉面
*/
@Override
public Inoodles create() {
return new LzNooldles();
}
}
public class PaoNoodls extends NoodlesFactory {
@Override
public Inoodles create() {
return new PaoNoodles();
}
}
public class YouPoNoolds extends NoodlesFactory {
@Override
public Inoodles create() {
return new YouPoNooldes();
}
}
public class Test {
public static void main(String[] args) {
NoodlesFactory fac=new PaoNoodls();
Inoodles sd=fac.create();
sd.desc();
}
}
加班必备的泡面
普通工厂就是把简单工厂中具体的工厂类,划分成两层:抽象工厂层+具体的工厂子类层。(一般->特殊)
普通工厂与简单工厂模式的区别:
可以看出,普通工厂模式特点:不仅仅做出来的产品要抽象, 工厂也应该需要抽象。
工厂方法使一个产品类的实例化延迟到其具体工厂子类.
工厂方法的好处就是更拥抱变化。当需求变化,只需要增删相应的类,不需要修改已有的类。
而简单工厂需要修改工厂类的create()方法,多方法静态工厂模式需要增加一个静态方法。
缺点:
引入抽象工厂层后,每次新增一个具体产品类,也要同时新增一个具体工厂类。
五:抽象工厂
以上介绍的工厂都是单产品系的。抽象工厂是多产品系 (貌似也有产品家族的说法)。
举个例子来说,每个店(工厂)不仅仅卖面条,还提供饮料卖。
提供饮料卖,饮料是产品,先抽象一个产品类,饮料:
public abstract class Drinks {
/*
* 描述每种饮料多少钱
*/
public abstract void prices();
}
public class Cola extends Drinks {
@Override
public void prices() {
System.out.println("可乐3.5");
}
}
public class Water extends Drinks {
@Override
public void prices() {
System.out.println("水不要钱");
}
}
public abstract class FoodFactory {
/*
* 生产面条
*/
public abstract Inoodles createNoodels();
/*
* 生产饮料
*/
public abstract Drinks createDrinks();
}
public class LzFoodFactory extends FoodFactory {
@Override
public Inoodles createNoodels() {
return new LzNooldles();
}
@Override
public Drinks createDrinks() {
return new Water();
}
}
public class KFC extends FoodFactory {
@Override
public Inoodles createNoodels() {
return new PaoNoodles();
}
@Override
public Drinks createDrinks() {
return new Cola();
}
}
public class Test {
public static void main(String[] args) {
FoodFactory fac=new KFC();
fac.createDrinks().prices();
fac.createNoodels().desc();
fac=new LzFoodFactory();
fac.createDrinks().prices();
fac.createNoodels().desc();
}
}
可乐3.5
加班必备的泡面
水不要钱
兰州拉面
小结:
将工厂也抽象了,在使用时,工厂和产品都是面向接口编程,OO(面向对象)的不得了。
缺点
但是将工厂也抽象后,有个显著问题,就是类爆炸了。而且每次拓展新产品种类,例如不仅卖吃卖喝,我还想卖睡,提供床位服务,这需要修改抽象工厂类,因此所有的具体工厂子类,都被牵连,需要同步被修改。
一句话总结工厂模式:方便创建 同种产品类型的 复杂参数 对象
工厂模式重点就是适用于 构建同产品类型(同一个接口、基类)的不同对象时,这些对象new很复杂,需要很多的参数,而这些参数中大部分都是固定的,so,懒惰的程序员便用工厂模式封装之。
如果构建某个对象很复杂,需要很多参数,但这些参数大部分都是“不固定”的,应该使用Builder模式。
为了适应程序的扩展性,拥抱变化,便衍生出了 普通工厂、抽象工厂等模式。
更多模式详见:
http://blog.youkuaiyun.com/zhangerqing/article/details/8194653
http://blog.youkuaiyun.com/column/details/code-design.html