java设计模式之简单工厂模式

转载请注明出处:http://blog.csdn.net/H_Zhang/article/details/51203749

软件开发中学好设计模式可以让你不用重复去造轮子。那么什么是设计模式呢?

设计模式:是一种解决特定环境下、重复出现的、特定问题的解决方案。

在开发软件的过程中,可能会碰到许多特定的问题,利用设计模式去解决这些问题可以节省大量时间。并且这些设计模式都是前人总结的优秀经验,其中的理论思想都是经过大量验证的。

好的,那就废话不多说,直接进入本文的主题:简单工厂模式。


面向接口编程

大家都知道,软件开发都需要进行分层设计。一般分为三层:表示层,逻辑层,数据层。在开发java web的项目也是如此,一般都会分为:View层,Service层,DAO层。其中的道理都是一样的:将程序的显示逻辑和业务处理逻辑进行分离。

那么层与层之间就需要隔离变化,也就是说DAO层内部发生了变化不能影响到Service层或者View层的代码。这就需要“面向接口编程”。java中接口的用法相信大家都非常熟悉了。使用接口可以进行“封装隔离”,外部客户端只能使用接口进行调用,而不知道内部的具体实现,这样外部调用和内部实现就被接口隔离开了。只要接口不变,内部实现的变化不会影响到外部客户端的使用。

举个例子:你在开发一个DAO组件,主要实现为数据库中的user表提供CRUD操作。你会先定义一个接口UserDao:

public interface UserDao
{
    //根据id,从数据库中查找用户
    public User getUserById(int id);

    //将新用户插入到数据中
    public void insertUser(User user);

    //删除某一用户
    public void deleteUser(User user);

    //更新某一个用户
    public void updateUser(User user);
}

接下来你需要去实现这个接口,你新建了一个实现类UserDaoJdbcImpl,内部利用jdbc编程实现了CRUD

public class UserDaoJdbcImpl implements UserDao
{
    public User getUserById(int id)
    {
        //使用jdbc实现
    }

    public void insertUser(User user)
    {
        //使用jdbc实现
    }

    public void deleteUser(User user)
    {
        //使用jdbc实现
    }

    public void updateUser(User user)
    {
        //使用jdbc实现
    }
}

此时,Service层就可以使用这个DAO组件了

public class UserService
{
    public UserDao userDao;
    /**
     * 仅为了说明情况
     */
    public UserService()
    {
        userDao = new UserDaoJdbcImpl();
    } 
}

仔细看上面使用DAO组件的方法userDao = new UserDaoJdbcImpl();这么写是有问题的。因为Service层不仅知道了接口,同时还知道了具体实现就是UserDaoJdbcImpl

比如,你发现利用jdbc方法实现CRUD太过于繁琐,你打算利用一个ORM框架来简化编程。于是乎你又增加了一个实现类UserDaoHibernateImpl ,利用Hibernate实现了CRUD

public class UserDaoHibernateImpl implements UserDao
{
    public User getUserById(int id)
    {
        //使用Hibernate实现
    }

    public void insertUser(User user)
    {
        //使用Hibernate实现
    }

    public void deleteUser(User user)
    {
        //使用Hibernate实现
    }

    public void updateUser(User user)
    {
        //使用Hibernate实现
    }
}

这样Service层的代码也得跟着变化:

public UserService()
{
    userDao = new UserDaoHibernateImpl();
} 

所以,你会发现Service层和DAO层耦合度太高。接口的核心思想就是隔离变化,所以,实现类UserDaoHibernateImpl或者UserDaoJdbcImpl应该被接口封装隔离。也就是说,客户端根本就不应该知道UserDao的实现类是UserDaoHibernateImpl还是UserDaoJdbcImpl


简单工厂模式

可以使用简单工厂模式,来实现层与层之间解耦。首先看简单工厂模式定义:

简单工厂提供一个创建对象实例的功能,而无须关心其具体实现,被创建的实例类型可以使接口,抽象类,也可以是具体类。

先简单分析一下:为了隔离变化,模块外部是不应该知道模块内部的具体实现的,但是模块内部是知道具体实现类的,并且创建接口实例需要用到具体实现类。

那么,干脆就在模块内部新建一个类,这个类就专门负责创建接口,然后将创建好的接口返回给客户端。把这样的类就叫做工厂类(Factory)。这样一来客户端通过Factory来获取接口对象,而不用在关心接口具体谁来实现。

工厂嘛,造东西的。在java里面,通常情况下就是用来造接口的。所以,下面我们就为DAO组件添加一个简单工厂类:

public class DaoFactory
{
    public static final int JDBC_IMPL = 1;
    public static final int HIBERNATE_IMPL = 2;
    //根据type类型,选择合适的实现类来创建接口对象
    public static UserDao createUserDao(int type)
    {
        UserDao userDao = null;
        if(type == JDBC_IMPL){
            userDao = new UserDaoJdbcImpl();
        }else if(type == HIBERNATE_IMPL){
            userDao = new UserDaoHibernateImpl();
        }

        return userDao;
    }
}

客户端(Service层)如何使用简单工厂呢?这个时候客户端不应该自己去创建接口对象了,而是通过使用简单工厂来获取:

public class UserService
{
    public UserDao userDao;
    /**
     * 仅为了说明情况
     */
    public UserService()
    {
        userDao = DaoFactory.createUserDao(DaoFactory.HIBERNATE_IMPL);
    } 
}

我画了一个UML图来说明使用简单工厂模式之后,各个组件之间的关系。


这里写图片描述

图中虚线框就好比DAO组件的包装界面,表示接口,实现类,工厂类共同组成了DAO组件。在封装体中,只有接口和工厂类是让外部知道并使用的,所以故意漏了一些在虚线框外。对于Service层而言只是知道了接口UserDao和工厂类DaoFactory,通过工厂获得接口对象即可。

可以看到现在的代码仍然有一些缺点,Service层在调用工厂方法的时候,需要传入类型参数type。这就要求Service层必须了解每个参数的具体函数,也在一定程度上向Service层暴露了DAO内部实现细节。另外,工厂内部的createUserDao()方法的写法也有问题:每次新增一个UserDao实现类都需要修改工厂类的实现,这显然不是一个好的实现方式。

那么希望新增加一个UserDao实现类后不修改工厂类,该如何实现呢?

一个好的解决方式就是:配置文件 + 反射。


可配置的简单工厂

工厂方法具体想要使用哪一个实现类来创建接口,可以从配置文件中读取。这里配置文件使用最简单的properties文件,实际开发多用XML文件。新建一个配置文件DaoFacotory.properties,并将其和DaoFactory类放在同一个包下面

DaoFacotory.properties:

ImplClass=cn.hebut.hzh.dao.UserDaoJdbcImpl

工厂想要使用哪一个实现类创建接口对象,在配置文件中指定即可。

工厂类代码如下:

public class DaoFactory
{
    public static UserDao createUserDao()
    {
        Properties p = new Properties();
        InputStream in = null;
        try
        {
            in = DaoFactory.class.getResourceAsStream("DaoFactory.properties");
            p.load(in);
        } catch (IOException e)
        {
            e.printStackTrace();
        }finally
        {
            try
            {
                in.close();
            } catch (IOException e)
            {
                e.printStackTrace();
            }
        }

        UserDao userDao = null;
        try
        {
            //反射出具体接口对象
            userDao = (UserDao) Class.forName(p.getProperty("ImplClass")).newInstance();
        } catch (Exception e)
        {
            e.printStackTrace();
        }

        return userDao;
    }
}

第9行将DaoFactory类路径下的properties文件读到流里;第10行将文件流解析成Properties对象;第29行利用反射原理创建出配置文件中指定的实现类对象。

这样就Service层代码就变得很简单,不需要传入参数,代码如下:

public class UserService
{
    public UserDao userDao;
    /**
     * 仅为了说明情况
     */
    public UserService()
    {
        userDao = DaoFactory.createUserDao();
    } 
}

OK! 这样就实现了Service层和DAO层的完全解耦。DAO层再怎么变化都不会影响到Service层的代码。


简单总结一下。使用简单工厂设计模式好处:
1. 隔离变化,实现层与层之间的解耦;客户端只是通过工厂来获得接口对象,而不知道具体由谁来实现,也不知道如何实现。这些变化由简单工厂负责吸收屏蔽。
2. 组件中对象的创建由简单工厂负责统一管理和控制;

简单工厂核心是:选择实现。这里要注意,简单工厂的重点是在选择,实现已经做好了。就算实现再简单,也由具体的实现类来实现,而不是由简单工厂来实现。

OK,相信大家对简单工厂模式已经非常了解了,本篇文章到这也就结束了,谢谢大家观看~~~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值