面向接口编程



用户注册的例子
我们先看看更进一步的需求:实现一个用户注册信息持久化的类。
功能:
保存用户注册的信息;
根据用户的名称获得该注册用户。
虽然功能简单,但它对持久化方式的要求却非常的灵活:
在内存中持久化,供测试、演示使用。
如果用户的数据很少,将用户信息持据化到文本文件中。
如果用户信息很多,并需要一些灵活的查询,则需要使用JDBC技术将用将用户信息持久化到数据库中。
面对企业复杂关联的数据,甚至需要使用持久层框架来实现用户信息的持久化,比如: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 defaultAdmin = new User("Moxie","pass");
users.put(defaultAdmin.getName(),defaultAdmin);
}
public MemoryUserPersist (){

}
public void saveUser(User user){
users.put(user.getName(),user);
}
public User LoadUser(String userName){
return (User)users.get(userName);
}
}
用户持久化类完成之后,我们就可以在客户端UserRegister中使用它了。例如:用户注册时,UserRegister代码片断如下:
MemoryUserPersist userPersist = new MemoryUserPersist ();
userPersist.saveUser(user);
可是,现在如果要在文本文件中持久化User,又该如何实现呢?实现一个TextUserPersist类,这个并不困难。但客户端代码将面临重大灾难:找到所有使用过MemoryUserPersist的客户端类,将他们中的MemoryUserPersist逐个手工修改为 TextUserPersist,并且重新编译,当然以前的测试也必须全部从头来过!
人生的浩劫只是刚刚开始,因为根据前面的需求我们至少要分别实现四种持久化方式!这时,你一定和我一样在期待着救世主的早日降临——接口(Interface)。
面向接口编程
什么是接口?
接口定义了行为的协议,这些行为在继承接口的类中实现。
接口定义了很多方法,但是没有实现它们。类履行接口协议并实现所有定义在接口中的方法。
接口是一种只有声明没有实现的特殊类。

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

接口的缺点:
设计的复杂性略有增加
(用户持久化类)重构第一步——面向接口编程
设计用户持久化类的接口UserDao,代码如下:
public interface UserDao {
public void save(User user);
public User load(String name);
}
具体的持久化来必须要继承UserDao接口,并实现它的所有方法。我们还是首先实现内存持久化的用户类:
public class MemoryUserDao implements UserDao{
private static Map users = new HashMap();;
static{
User user = new User("Moxie","pass");
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)模式出场了!
重构第二步——工厂(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实现类名,将它设置为你需要更换的持久化类名即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值