简单工厂模式

一  引入

 我们先看看一个简单需求:实现一个用户注册信息持久化的类。
功能:
1、 保存用户注册的信息;
2、 根据用户的名称获得该注册用户。
虽然功能简单,但它对持久化方式的要求却非常的灵活:
1、 在内存中持久化,供测试、演示使用。
2、 如果用户的数据很少,将用户信息持久化到文本文件中。
3、 如果用户信息很多,并需要一些灵活的查询,则需要使用JDBC技术将用将用户信息持久化到数据库中。
4、 面对企业复杂关联的数据,甚至需要使用持久层框架来实现用户信息的持久化,比如:iBATIS、Hibernate等。
如何去设计、实现我们这个持久化类呢?
我们遵循软件开发的原则“首先让它跑起来,再去优化(重构)它”,我们首先实现最简单的在内存中持久化用户信息。
既然我们要保存和取得用户信息,首先应该设计用户类。代码如下:

====================User.java==============================

public class User {  
    private Long id;  
    private String name;  
    private String password;  
    private String group;  
      
    public User(String name,String password){  
        this.name = name;  
        this.password = password;  
    }  
    //相应的get/set方法  
    ………
} 
持久化类有两个方法,分别在内存中保存和获取User对象。代码如下:

====================MemoryUserPersist.java ========================

public class MemoryUserPersist {  
    private static Map users = new HashMap(); 
    //给一个默认键值对 
    static{  
        User defaultUser = new User("jack","test123");  
        users.put(defaultUser.getName(),defaultUser);  
    }  
    public MemoryUserPersist (){  
          
    }  
    //将信息保存到Map中(内存存储)  
    public void saveUser(User user){  
        users.put(user.getName(),user); //用用户名做键值  
    }  
    public User LoadUser(String userName){  
        return (User)users.get(userName);  
    }  
}
用户持久化类完成之后,我们就可以在客户端UserRegister中使用它了。例如:用户注册时,UserRegister代码片断如下:

MemoryUserPersistuserPersist = new MemoryUserPersist ();
userPersist.saveUser(user);

      可是,现在如果要在文本文件中持久化User,又该如何实现呢?实现一个TextUserPersist类,这个并不困难。但客户端代码将面临重大灾难:找到所有使用过MemoryUserPersist的客户端类,将他们中的MemoryUserPersist逐个手工修改为 TextUserPersist,并且重新编译,当然以前的测试也必须全部从头来过!
      人生的浩劫只是刚刚开始,因为根据前面的需求我们至少要分别实现四种持久化方式!这时,你一定和我一样在期待着救世主的早日降临——接口(Interface)。

二   面向接口编程

什么是接口?

(1)接口定义了行为的协议,这些行为在继承接口的类中实现。
(2)接口定义了很多方法,但是没有实现它们。类履行接口协议并实现所有定义在接口中的方法。
(3)接口是一种只有声明没有实现的特殊类。

接口的优点:
(1)Client不必知道其使用对象的具体所属类。
(2)一个对象可以很容易地被(实现了相同接口的)的另一个对象所替换。
(3)对象间的连接不必硬绑定(hardwire)到一个具体类的对象上,因此增加了灵活性。
(4)松散藕合(loosens coupling)。
(5)增加了重用的可能性。


接口的缺点:
设计的复杂性略有增加

(用户持久化类)重构第一步——面向接口编程
1、 设计用户持久化类的接口UserDao,代码如下:
public interface UserDao {  
    public void save(User user);  
    public User load(String name);  
}

2、 具体的持久化来必须要继承UserDao接口,并实现它的所有方法。我们还是首先实现内存持久化的用户类:  

public class MemoryUserDao implements UserDao{  
    private static Map users = new HashMap();;  
    static{  
        User user = new User("jack","test123");  
        users.put(user.getName(),user);  
    }  
  
    public void save(User user) {  
        users.put(user.getId(),user);  
    }  
  
    public User load(String name) {  
        return (User)users.get(name);  
    }  
}
MemoryUserDao的实现代码和上面的MemoryUserPersist基本相同,唯一区别是MemoryUserDao类继承了UserDao接口,它的save()和load()方法是实现接口的方法。
这时,客户端UserRegister的代码又该如何实现呢?
UserDao userDao= new MemoryUserDao();
userDao.save(user);
(注:面向对象“多态”的特性)
如果我们再切换到文本的持久化实现TextUserDao,客户端代码仍然需要手工修改。虽然我们已经使用了面向对象的多态技术,对象userDao方法的执行都是针对接口的调用,但userDao对象的创建却依赖于具体的实现类,比如上面MemoryUserDao。这样我们并没有完全实现前面所说的“Client不必知道其使用对象的具体所属类”。
如何解决客户端对象依赖具体实现类的问题呢?
下面该是我们的工厂(Factory)模式出场了!
(spring 实现用户调用某个类的时候真正的不用再实例化某个具体的类,而是仅仅调用它的实现接口就可以完成所要达到的目的!)


三 重构第二步——工厂(Factory)模式

我们使用一个工厂类来实现userDao对象的创建,这样客户端只要知道这一个工厂类就可以了,不用依赖任何具体的UserDao实现。创建userDao对象的工厂类UserDaoFactory代码如下:  

public class UserDaoFactory {
   public static UserDao createUserDao(){
       return new MemoryUserDao();
    }
}

客户端UserRegister代码片断如下:

UserDao userDao= UserDaoFactory. CreateUserDao();

userDao.save(user);
现在如果再要更换持久化方式,比如使用文本文件持久化用户信息。就算有再多的客户代码调用了用户持久化对象我们都不用担心了。因为客户端和用户持久化对象的具体实现完全解耦。我们唯一要修改的只是一个UserDaoFactory类。


重构第三步——工厂(Factory)模式的改进
      到这里人生的浩劫已经得到了拯救。但我们仍不满足,因为假如将内存持久化改为文本文件持久化仍然有着硬编码的存在——UserDaoFactory类的修改。代码的修改就意味着重新编译、打包、部署甚至引入新的Bug。所以,我们不满足,因为它还不够完美!

      如何才是我们心目中的完美方案?至少要消除更换持久化方式时带来的硬编码。具体实现类的可配置不正是我们需要的吗?我们在一个属性文件中配置UserDao的实现类,例如:在属性文件中可以这样配置:userDao= com.test.MemoryUserDao。UserDao的工厂类将从这个属性文件中取得UserDao实现类的全名,再通过Class.forName(className).newInstance()语句来自动创建一个UserDao接口的具体实例。UserDaoFactory代码如下:

public class UserDaoFactory {  
    public static UserDao createUserDao(){  
        String className = "";   
        // 从属性文件中取得这个UserDao的实现类全名。  
        UserDao userDao = null;  
        try {  
            userDao = (UserDao)Class.forName(className).newInstance();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }   
        return userDao;  
    }
}  
      通过对工厂模式的优化,我们的方案已近乎完美。如果现在要更换持久化方式,不需要再做任何的手工编码,只要修改配置文件中的userDao实现类名,将它设置为你需要更换的持久化类名即可。
      我们终于可以松下一口气了?不,矛盾仍然存在。我们引入了接口,引入了工厂模式,让我们的系统高度的灵活和可配置,同时也给开发带来了一些复杂度:1、本来只有一个实现类,后来却要为这个实现类引入了一个接口。2、引入了一个接口,却还需要额外开发一个对应的工厂类。3、工厂类过多时,管理、维护非常困难。比如:当UserDao的实现类是JdbcUserDao,它使用JDBC技术来实现用户信息从持久化。也许要在取得JdbcUserDao实例时传入数据库Connection,这是仍少UserDaoFactory的硬编码。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

御前两把刀刀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值