`
yuanzher
  • 浏览: 30938 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

【转】设计模式之简单工厂模式

    博客分类:
  • java
 
阅读更多

转自:http://www.cnblogs.com/shenliang123/archive/2012/05/10/2494412.html

简单工厂模式就是将多个类对象交给工厂类来生成的设计方式

在不使用工厂模式前我们一般在某个类A中需要使用到类B的方法,那么我们首先想到的就是将类B在类A中进行实例化即B 实例 = new B();这样的方式对于我们初学者或者是小型的项目应该是不会构成威胁的,但是如果这个是一个应用于大型的项目,用户的需求也是经常需要改变的,如果我们使用这种方式硬编码耦合的方式来进行编码,那么如果需求驱使我们必须将B类换成C类,那么我们就只能去A类中进行更改;但在一个大型项目可能用到类B的可能有成百上千个,如果我们这样求修改的话那是不可想象的,根本是没法修改的;

简单工厂模式就可以为我们解决上面那个难题,设计如下:首先我们需要转换的是由原先的面向实现类编程转为面向接口编程,对于A对象而言,它只是需要调用B对象中的方法,而并不关心B对象的实现过程,转换使用面向接口编程就是让B实现类继承自IB接口,而A类只需要与IB接口耦合,而与其实现类进行解耦,但这里说的耦合也并不是说是在A类中用New来实例化接口,这样就没有实际的意义了,而是定义一个工厂类IBFactory,由该工厂类来创建IB的实例,然后类A通过调用IBFactory中的方法来得到IB实例

下面模拟使用简单工厂模式实现的需求:

需求:假设现在的我们需要对数据库中的内容进行输出,需要依赖于一个输出的类,但现在有两种方式进行输出,分别是:excel与word;(这里我简单模拟,因此代码会很简单);

1.首先我们先用写一个输出方式的接口,代码如下:

package xidian.sl.interfaces;

public interface Print {
    public void outPrint();
}

2.然后需要写出它的两个实现类,如下:
excel方式:

复制代码
package xidian.sl.impl;

import xidian.sl.interfaces.Print;

public class ExcelImpl implements Print {

    @Override
    public void outPrint() {
        System.out.println("使用excel进行导出");
    }

}
复制代码

word方式:

复制代码
package xidian.sl.impl;

import xidian.sl.interfaces.Print;

public class WordImpl implements Print {

    @Override
    public void outPrint() {
        System.out.println("使用word进行导出");
    }

}
复制代码

3.然后实现一个工厂类:

复制代码
package xidian.sl.interfaces;

import xidian.sl.impl.ExcelImpl;

public class PrintFactory {
    /**
     * 用于获取print实例的方法
     * */
    public Print getPrint(){
        /**
         * 这里默认返回的是excel方式进行导出
         * */
        return new ExcelImpl();
    }
}
复制代码

4.进行简单工厂方式的实现:

复制代码
package xidian.sl.impl;

import xidian.sl.interfaces.Print;
import xidian.sl.interfaces.PrintFactory;

public class DataOutput {
    private Print print;
    
    public DataOutput(Print print){
        this.print = print;
    }
    /**
     * 模拟导出,这里就是需要调用其他对象中的方法进行实现
     * */
    public void output(){
        print.outPrint();
    }
    
    public static void main(String[] args){
        /**
         * 实例化工厂类
         * */
        PrintFactory printFactory = new PrintFactory();
        /**
         * 实例化调用的类,通过构造方法来对DataOutput对象进行初始化
         * */
        DataOutput dataOutput = new DataOutput(printFactory.getPrint());
        dataOutput.output();
    }
}
复制代码

好了,接下来我们只要点击运行就会在控制台出现:使用excel进行导出;
如果由于需求的改变需要使用word来进行导出,很简单,我们只需要修改工厂类中的方法即可,其他都不需要变:

复制代码
package xidian.sl.interfaces;

import xidian.sl.impl.WordImpl;

public class PrintFactory {
    /**
     * 用于获取print实例的方法
     * */
    public Print getPrint(){
        /**
         * 这里默认返回的是excel方式进行导出
         * */
        //return new ExcelImpl();
        /**
         * 更改为使用word方式进行导出
         * */
        return new WordImpl();
    }
}
复制代码

到此我们已经实现了一个简单工厂模式,我们能够很明显的感觉到该模式的优势:让对象的调用者与对象创建过程进行分离,当对象调用者需要对象时只需直接向工厂请求即可,
从而避免了对象调用者与对象实现类以硬编码方式进行耦合。就如上面的程序,即使Print接口有很多实现类,我们只需要到工厂类中进行更换实现类的实例化即可,其他不需要

更改,这里也显示了面向接口编程的优势,这样对象的调用者就与接口进行耦合而不是与实现类,与接口耦合的好处就是接口可以有多个实现类,保证了我们可以不去修改接口,

而只是添加或者修改一个实现类即可;

 

下面我们将深入简单工厂模式:

用过spring框架的人都应该深有体会,spring本身就是一个巨大的工厂类,所有的bean都可以通过spring来进行管理,如DAOBean,ServiceBean,ActionBean等等;

下面来进行spring管理bean的演示,基本实现最简单的ioc容器

ApplicationContext.xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id = "print" class = "xidian.sl.interfaces.Print">
        <!-- 为print注入普通属性值 -->
        <propertty name = "print" ref = "wordImpl"></propertty>
    </bean>
    <!-- 配置两个Bean实例 -->
    <bean id = "wordImpl" class = "xidian.sl.impl.WordImpl"></bean>
    <bean id = "excelImpl" class = "xidian.sl.impl.ExcelImpl"></bean>
</beans>
复制代码

获取指定bean的接口:

package xidian.sl.interfaces;

public interface ApplicationContext {
    //获取指定Bean实例的方法
    Object getBean(String name) throws Exception;
}

该bean的实现类

复制代码
package xidian.sl.impl;

import java.io.File;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import xidian.sl.interfaces.ApplicationContext;


/**
 * 以下使用Dom4j来进行xml文件的解析
 * */
public class XmlApplicationContext implements ApplicationContext {
    /**
     * 保存容器中所有单例模式的bean实例,这里的hashMap是线程安全的
     * */
    private Map<String, Object> objPool = Collections.synchronizedMap(new HashMap<String, Object>());
    //保存文件对应的Document对象
    private Document document;
    //保存配置文件里的根元素
    private Element root;
    
    public XmlApplicationContext(String filePath) throws Exception{
        //使用dom4j读取配置文件
        SAXReader reader = new SAXReader();
        document = reader.read(new File(filePath));
        //得到跟节点beans
        root = document.getRootElement();
        //进行容器的初始化
        initPool();
        //初始化单例bean的属性
        initProp();
    }
    
    @Override
    public Object getBean(String name) throws Exception {
        Object target = objPool.get(name);
        //对于单例bean,容器已经初始化了所有的Bean实例
        if(target.getClass() != String.class){
            return target;
        }else{
            String clazz = (String)target;
            //对于非单例的并未注入属性值
            return Class.forName(clazz).newInstance();
        }
    }
    /**
     * 初始化容器中的所有单例bean
     * */
    private void initPool() throws Exception{
        //遍历配置文件中的每个<bean ../>元素
        for(Object obj: root.elements()){
            Element beanElement = (Element)obj;
            //获取Bean元素中的id属性
            String beanId = beanElement.attributeValue("id");
            //获取bean元素中的class属性
            String beanClazz = beanElement.attributeValue("class"); 
            //获取bean元素中的scope属性
            String beanScope = beanElement.attributeValue("scope"); 
            //如果scope属性不存在或为singleton
            if(beanScope == null|| beanScope.equals("singleton")){
                //以默认构造方法创建bean实例,并将其放入objPool中
                objPool.put(beanId, Class.forName(beanClazz).newInstance());   //利用反射的技术
            }else{
                //对于非单例的,存放Bean实现类的类名
                objPool.put(beanId, beanClazz);
            }
        }
        
    }

    /**
     * 初始化容器中的单例bean
     * */
    private void initProp() throws Exception{
        //遍历配置文件中的所有bean元素,即根节点的子节点
        for(Object object: root.elements()){
            Element beanElement = (Element)object;
            //获取Bean元素中的id属性
            String beanId = beanElement.attributeValue("id");
            //获取bean元素中的scope属性
            String beanScope = beanElement.attributeValue("scope"); 
            //如果scope属性不存在或为singleton
            if(beanScope == null|| beanScope.equals("singleton")){
                //取出objPool的指定bean实例
                Object bean = objPool.get(beanId);
                //遍历bean属性下的所有property属性
                for(Object prop: beanElement.elements()){
                    Element probElement = (Element)prop;
                    //获取property的name属性
                    String propName = probElement.attributeValue("name");
                    //获取property的value属性
                    String propValue = probElement.attributeValue("value");
                    //获取property的ref属性
                    String propRef = probElement.attributeValue("ref");
                    //将属性名的首字母大写
                    String propNameCamelize = propName.substring(0,1).toUpperCase()
                        +propName.substring(1, propName.length());
                    //如果value值存在
                    if(propValue == null|| propValue.length()> 0){
                        //获取设置注入所需要的setter方法
                        java.lang.reflect.Method setter = bean.getClass().getMethod("set"+propNameCamelize, String.class);
                        //执行setter注入
                        setter.invoke(bean, propValue);
                    }
                    if(propRef == null|| propRef.length()> 0){
                        //取得需要被注入的bean实例
                        Object target = objPool.get(propRef);
                        //如果不存在
                        if(target == null){
                            //此处处理单例bean依赖于非单例bean的情形
                        }
                        //定义设值注入需要的setter方法
                        Method setter = null;
                        //遍历target对象所实现的所有方法
                        for(Class superInterface: target.getClass().getInterfaces()){
                            try{
                                //获取设置注入所需要的setter方法
                                setter = bean.getClass().getMethod("set"+propNameCamelize, superInterface);
                                //如果成功获取,跳出循环
                                break;
                            }catch (Exception e) {
                                //如果没有找到就继续下次循环
                                continue;
                            }
                        }
                        //如果setter方法依然是null,直接取得target的实现类对应的setter方法
                        if(setter == null){
                            setter = bean.getClass().getMethod("set"+propNameCamelize, target.getClass());
                        }
                        //执行setter注入
                        setter.invoke(bean, target);
                    }
                    
                }
        }
        
    }
}
}
复制代码

最后进行注入bean的测试:

复制代码
package xidian.sl.servlet;

import xidian.sl.impl.XmlApplicationContext;
import xidian.sl.interfaces.ApplicationContext;
import xidian.sl.interfaces.Print;

public class IocTest {
    public static void main(String[] args) throws Exception{
        //创建IOC容器
        ApplicationContext ctx = new XmlApplicationContext("applicationContext.xml");
        //从IOC容器中取出print bean
        Print print = (Print)ctx.getBean("print");
        //测试Print对象
        print.outPrint();
    }
}
复制代码

 总结下简单工厂模式:不知道大家有没有发现一个问题就是上面管理实例分配的工厂类中,现在只能每次为其分配一个实例,如果要配置多个实例就需要在工厂类中就行逻辑

判断:

复制代码
package xidian.sl.interfaces;

import xidian.sl.impl.ExcelImpl;
import xidian.sl.impl.WordImpl;

public class PrintFactory {
    /**
     * 用于获取print实例的方法
     * */
    public Print getPrint(Integer param){
        if(param == 1){
            /**
             * 这里默认返回的是excel方式进行导出
             * */
            return new ExcelImpl();
        }else if(param == 2){
            /**
             * 更改为使用word方式进行导出
             * */
            return new WordImpl();
        }else{
            return null;
        }
        
    }
}
复制代码

简单工厂模式在java中的应用:

1.DateFormat:jdk中的一个工具类java.text.DateFormat,用来格式化一个本地日期与时间

通过源码我们可以知道DateFormat是一个抽象类(abstract),下面的源代码就是这个类里包含的方法,其实这些就是静态工厂方法,通过静态方法来提供自己的实例是完全可以的(抽象类本身不能进行实例化)。从源码可以看出getDateInstance()方法做了两件事情:

一.运用了多态性:由于SimpleDateFormat是DateFormat的子类,而getDateInstance()声明的类型为DateFormat而实际返回类型为子类SimpleDateFormat  

二.使用了静态工厂方法(static):由于DateFormat是抽象类不能进行实例化,因此也就不能调用其中的普通方法(非静态方法)。因此我们必须将其声明为static,才能返回实例

通过上面做的两件事情就将具体子类的实例化过程隐藏起来了,调用者不必考虑具体子类的实例化,因为抽象类会提供它的合适子类实例

源代码:

复制代码
 /**
     * 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);
    }
    /**
     * 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");
        }
    }
复制代码

以上那个应用其实是将工厂角色与抽象产品角色进行合并:也就是说一个抽象产品类同时也是子类的工厂类。

还有一种退化的简单工厂模式就是,将抽象产品类,具体产品类,工厂类三个角色都进行合并,举例:

复制代码
package xidian.sl.impl;

public class Product {
    public Product(){};
    /**
     * 静态工厂方法来返回自己的实例
     * */
    public static Product getInstance(){
        return new Product();
    }
}
复制代码

其实这个跟单例模式已经很像了,只要稍加修改就可以实现一个单例模式了

简单工厂模式的有缺点:

优点:该模式的核心就是工厂类,这个类中含有必要的判断逻辑,可以决定在什么时候创建哪个产品类的实例。客户端可以免除直接创建产品对象的责任。

缺点:1.工厂类集中了所有产品创建逻辑,形成一个无所不知的全能类,对于这样的类我们很难进行控制

通过在调用工厂类方法是传入类型参数来判断到底是要返回哪种实例;但复杂的逻辑判断一般我们都是要舍弃的;
我的下一遍博客就会解决这个问题:根据根据每个实例生成一个工厂类的工厂方法模式http://www.cnblogs.com/shenliang123/archive/2012/05/10/2494826.html

分享到:
评论

相关推荐

    设计模式之简单工厂模式

    **简单工厂模式**是软件设计模式中的一种基础模式,它属于创建型模式,主要用于简化对象的创建过程。在简单工厂模式中,一个专门的工厂类负责创建对象,客户端通过调用工厂类的方法来获取所需的对象,而无需关心具体...

    工厂模式:简单工厂模式、工厂方法模式、抽象工厂模式

    在给定的资源中,"设计模式交流之工厂模式-shannonxu-2014-10-24.pptx"可能是详细的PPT讲解,涵盖了这些模式的概念、结构、优缺点以及实际应用案例。而"Factory"可能是一个代码示例,展示了工厂模式的具体实现,包括...

    设计模式之简单工厂模式案例

    简单工厂模式是软件设计模式中的一种创建型模式,它提供了一种创建对象的最佳方式。在简单工厂模式中,一个工厂类负责创建所有相关的对象,而客户端只需要知道具体的产品类型,无需了解如何创建这些对象的细节。这种...

    三种设计模式(简单工厂_工厂方法_抽象工厂)

    简单工厂模式是一种创建型设计模式,它提供了一个静态方法来创建对象,而无需暴露创建逻辑。这个静态方法根据输入参数来决定返回哪个类的实例。这种方式将对象的创建与使用分离,使得代码更加简洁,易于理解和使用。...

    设计模式资料之工厂设计模式

    总结来说,这份“设计模式资料之工厂设计模式”可能会涵盖工厂模式的基本概念、分类、作用,尤其是重点讲解抽象工厂模式的原理、应用场景及实现方式。通过学习这些内容,开发者将能更好地理解和运用设计模式,提升...

    大话设计模式之简单工厂模式

    大话设计模式源代码之简单工厂模式 经典代码

    简单工厂模式-工厂方法模式-抽象工厂模式

    在软件设计模式中,工厂模式是一组非常基础且实用的设计模式,主要分为简单工厂模式、工厂方法模式和抽象工厂模式。这些模式都是为了解决对象创建的问题,通过封装对象的创建过程,使得代码更加灵活,易于扩展和维护...

    深入浅出设计模式之工厂模式

    根据提供的标题“深入浅出设计模式之工厂模式”与描述“将《Head First 设计模式》(中文版)按章节进行了分割,每章一个文件,方便大家下载”,我们可以推测出这部分内容主要关注的是设计模式中的工厂模式。...

    设计模式(简单工厂模式_排序)手写代码.rar

    简单工厂模式是软件设计模式中的一种创建型模式,它提供了一种创建对象的最佳方式。在简单工厂模式中,一个工厂类负责创建所有相关的或者相互依赖的对象,无需客户端代码指定具体的类,而是通过工厂方法来创建实例。...

    设计模式之工厂方法、简单工厂、抽象工厂

    这三种工厂模式的共同之处在于它们都为对象的创建提供了抽象层,隐藏了具体的实例化过程。然而,它们之间的区别在于灵活性和抽象程度: - 工厂方法模式提供了一种定义抽象接口的方法,让子类来决定具体实例化哪个类...

    设计模式单例模式和工厂模式综合应用

    工厂模式分为简单工厂模式、工厂方法模式和抽象工厂模式,根据具体需求选择合适的实现。 在这个项目中,单例模式可能被用来创建一个工厂类,这个工厂类负责生产特定类型的手机(或其他产品)。这样的设计可以确保在...

    设计模式之简单工厂模式源码

    简单工厂模式是软件设计模式中的一种基础模式,它在创建对象时起到了抽象和封装的作用,使得客户端无需关心具体对象的创建过程,只需要通过一个公共的工厂接口就能获取所需的对象。这种模式尤其适用于当系统中存在...

    设计模式(简单工厂和工厂方法C++版)Demo程序

    本Demo程序主要涵盖了两种常用的创建型设计模式——简单工厂模式(Simple Factory)和工厂方法模式(Factory Method)。这两种模式都是用来创建对象,但它们在灵活性和抽象程度上有所不同。 简单工厂模式是一种静态...

    设计模式之工厂

    "工厂模式"是其中一种常用的设计模式,它提供了一种创建对象的最佳方式。在这个主题下,我们将深入探讨三种主要的工厂模式:简单工厂、工厂方法和抽象工厂。 1. **简单工厂模式**: 简单工厂模式是最基础的形式,...

    设计模式之java工厂模式

    "设计模式之java工厂模式"是关于如何优雅地创建对象的一种经典设计模式,它属于创建者模式类别。创建者模式主要关注对象的创建,而工厂模式则在其中扮演着重要角色,因为它提供了一种抽象的方式来创建对象,从而使...

    设计模式——简单工厂模式

    **设计模式——简单工厂模式...简单工厂模式是设计模式的基础之一,理解并熟练应用它可以提高代码的可读性和可维护性。在实际开发中,根据项目需求和规模,可能会选择更复杂的工厂模式,如工厂方法模式或抽象工厂模式。

    设计模式之简单工厂模式(附demo)

    简单工厂模式是软件设计模式中的一种基础模式,它属于创建型模式,主要解决对象的创建问题,降低了系统与具体创建对象之间的耦合度。在这个模式中,有一个工厂类负责创建对象,客户端通过调用工厂类的特定方法来获取...

    java 设计模式 mvc模式 单例模式 代理 工厂 简单工厂

    MVC模式使得用户界面与数据逻辑分离,单例模式保证关键资源的唯一访问点,代理模式在访问对象前后增加额外的功能,工厂模式将对象创建与使用分离,简单工厂模式为对象创建提供一个简单便捷的接口。通过这些模式,...

    设计模式之工厂系列

    "设计模式之工厂系列"指的是几种不同的工厂模式,它们都是面向对象设计中的重要组成部分,尤其在Java编程语言中广泛使用。工厂模式的主要目标是提供一个创建对象的抽象接口,使得系统在不指定具体实现类的情况下,...

Global site tag (gtag.js) - Google Analytics