首页 > 代码库 > JAVA设计模式--简单工厂模式

JAVA设计模式--简单工厂模式

我们都知道工厂是用来生产产品的,在程序语言中所谓的工厂就是为我们创建实例化对象的,工厂模式专门负责将大量有共同接口的类实例化,程序中可以动态决

将哪一个类实例化,工厂模式在一般的书中都认为分为两种,一种是简单工厂模式,另一种是工厂方法模式。

1、简单工厂模式结构

看图中的三类角色:

抽象产品角色:担任这个角色的类一般是工厂模式所创建的对象的父类,抽象产品角色可以用一个Java接口或者抽象类来实现。

具体产品角色:工厂中创建的所有实例都是具体产品角色的实例对象。

工厂角色:担任这个角色的是工厂方法模式的核心,工厂类根据传入参量来动态决定创建哪一个具体产品对象,与外部类有着紧密的业务联系,这个角色往往

由一个java类实现。

举个切实相关的例子,假如我们在做项目的时候需要一个导出的功能,系统要支持可以导出word、pdf、excel等格式的文件。这时候我们就按照简单工厂模式来

设计这个程序:

抽象产品角色我们用一个抽象类来实现,抽象类中包含一个抽象的导出方法,包含一些基本属性,比如说文件名称。

具体产品角色:ExportWord、ExportPdf、ExportExcel三个JAVA类都继承抽象类,然后实现父类中的抽象方法。

工厂中我们通过客户端传递过来的参数决定具体去创建哪个子类的实例化对象。

2、多层次的产品结构

在系统中我们还会遇到更为复杂的产品结构,举个例子:


针对这种具有多层级产品的情况,简单工厂模式采用的是以不变应万变的策略,一律使用同一工厂类,这样做有利有弊,下面就具体说一下它的利弊:

优点:设计简单,产品类的等级结构不会反应到工厂类来

缺点:其一,这个工厂类包含了所有产品的创建逻辑,形成了一个无所不知的全能类,当这个类出问题的时候,客户端与整个产品链的联系都中断了;

      其二,当我们要增加一种产品的时候,必须要修改工厂类,功能拓展比较麻烦,不满足我们设计程序的"开--闭"原则。

      其三,简单工厂模式使用静态方法作为工厂方法,静态方法无法由子类继承,因此,工厂角色无法基于继承的等级结构。

注:"开--闭"原则,一个软件应当对外拓展开放,对修改关闭,也就是说我们在设计一个模块的时候,应当让这个模块具有不被修改的前提下可拓展的

能力。听着挺别扭,举个书里写的例子:

玉帝招安美猴王,将猴子封为弼马温,我们将”玉帝管理天庭秩序“看做一个软件实体,那么他给猴子封官,纳进自己的团队,这就是”开“,另一方面

这一做法避免了猴子破坏天庭秩序,这就是”闭“。

3、简单工厂模式在Java中的应用

书中有一个典型的例子,就是我们常用来进行日期格式化的DateFormat类

看一段代码:

import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.Locale;
public class SimpleFactoryDemo1 {
	public static void main(String[] args) throws ParseException {
		Locale locale =Locale.CHINA;
		Date d=new Date();
		System.out.println(d);
		System.out.println(DateFormat.getDateInstance(DateFormat.DEFAULT,locale).format(d)); 
	}
}
这段代码是获取了一个中文格式的日期。

乍一看,DateFormat.getDateInstance()好像是在获取自己的实例对象,其实不是,看JDK源代码很清楚的看到DateFormat类是抽象类,是无法进行实例化的,

那么我们看一下到底什么情况?

getDateFormat源代码如下:

    /**
     * Gets the date formatter with the default formatting style
     * for the default locale.
     * @return a date formatter.
     */
    public final static DateFormat getDateInstance()
    {
        return get(0, DEFAULT, 2, Locale.getDefault());
    }

    /**
     * Gets the date formatter with the given formatting style
     * for the default locale.
     * @param style the given formatting style. For example,
     * SHORT for "M/d/yy" in the US locale.
     * @return a date formatter.
     */
    public final static DateFormat getDateInstance(int style)
    {
        return get(0, style, 2, Locale.getDefault());
    }

    /**
     * Gets the date formatter with the given formatting style
     * for the given locale.
     * @param style the given formatting style. For example,
     * SHORT for "M/d/yy" in the US locale.
     * @param aLocale the given locale.
     * @return a date formatter.
     */
    public final static DateFormat getDateInstance(int style,
                                                 Locale aLocale)
    {
        return get(0, style, 2, aLocale);
    }
我们看到三种getDateFormat方法最终都调用了get()方法,我们再跟踪一下这个get方法:

    /**
     * Creates a DateFormat with the given time and/or date style in the given
     * locale.
     * @param timeStyle a value from 0 to 3 indicating the time format,
     * ignored if flags is 2
     * @param dateStyle a value from 0 to 3 indicating the time format,
     * ignored if flags is 1
     * @param flags either 1 for a time format, 2 for a date format,
     * or 3 for a date/time format
     * @param loc the locale for the format
     */
    private static DateFormat get(int timeStyle, int dateStyle,
                                  int flags, Locale loc) {
        if ((flags & 1) != 0) {
            if (timeStyle < 0 || timeStyle > 3) {
                throw new IllegalArgumentException("Illegal time style " + timeStyle);
            }
        } else {
            timeStyle = -1;
        }
        if ((flags & 2) != 0) {
            if (dateStyle < 0 || dateStyle > 3) {
                throw new IllegalArgumentException("Illegal date style " + dateStyle);
            }
        } else {
            dateStyle = -1;
        }
        try {
            // Check whether a provider can provide an implementation that's closer 
            // to the requested locale than what the Java runtime itself can provide.
            LocaleServiceProviderPool pool =
                LocaleServiceProviderPool.getPool(DateFormatProvider.class);
            if (pool.hasProviders()) {
                DateFormat providersInstance = pool.getLocalizedObject(
                                                    DateFormatGetter.INSTANCE,
                                                    loc, 
                                                    timeStyle,
                                                    dateStyle,
                                                    flags);
                if (providersInstance != null) {
                    return providersInstance;
                }
            }

            return new SimpleDateFormat(timeStyle, dateStyle, loc);
        } catch (MissingResourceException e) {
            return new SimpleDateFormat("M/d/yy h:mm a");
        }
    }
看到了吧,最终返回的是SimpleDateFormat类的实例化对象,因为SimpleDateFormat是DateFormat的子类,所以getDateInstance()方法的返回类型可以是

DateFormat类,这也是多态性的一种体现。

实例总结:由于使用了简单工厂模式,客户端完全不用关心工厂方法所返还的对象时怎么创建和构成的,工厂方法将实例化哪些对象和具体如何实例化这些对象的

细节隐藏起来,使得这些对象的使用简化起来。与一般的简单工厂模式不同的是,上面这个例子工厂角色与抽象产品角色合并为一个类DateFormat,也就是抽象

产品角色负责了具体产品的创建,这也是简单工厂模式的一个特例

以后想到什么应用实例再补充…………






JAVA设计模式--简单工厂模式