首先介绍Java程序设计的两个重要思想DRY和OAOO:
DRY:Don’t Repeat Yourself 不要重复你自己(也叫做DIE:Duplication Is Evil 重复就是魔鬼)
OAOO: Only and Once Only 一次且仅一次
这两个思想略有区别:OAOO要求的是同样的代码只能出现一次且一次足矣;
DRY 要求的是逻辑、常量、标准、功能、服务不要重现重复,范围比OAOO更加广。
介绍这个设计模式前,先举个应用例子:
我们去吃午饭:1进入餐馆 ,2点菜 ,3吃掉
public class Party {
public void partyDinner(){
//do something
System.out.println("进入饭馆");
//do something
System.out.println("来一份菜");
//do something
System.out.println("吃掉");
}
}
但是,我们点菜不一样,有点鱼,有点肉的。若我们直接定义类 为 点肉 和 点鱼 的人进行服务
public class Party4Meat {
public void partyDinner(){
//do something
System.out.println("进入饭馆");
//do something
System.out.println("来一份肉");
//do something
System.out.println("吃掉");
}
}
public class Party4Fish {
public void partyDinner(){
//do something
System.out.println("进入饭馆");
//do something
System.out.println("来一份鱼");
//do something
System.out.println("吃掉");
}
}
我们不难发现我们重复进行定义了进”餐馆”和”吃掉”这两个逻辑。不仅如此,这样写出来的程序极难维护,当你的eat需要修改时,你要找到所有的eat逻辑进行修改。
可以看到,在这个例子中,不管点菜的需求怎么变,entry和eat都不会改变,变的只是order的逻辑。
为此我们引入模板方法来减少这种repeat!
让party中的三种逻辑变成三种方法,其中会发生变化的逻辑延迟到子类中去实现。即把party变成抽象父类,把order变成一个抽象的方法让子类去实现,同时,我们还需要在父类中定义一个逻辑方法,让我们拆分后的逻辑仍是原先的逻辑。
public abstract class Party {
/**
* 对外提供服务,并用来保持原有的逻辑
* @author majin
*/
public void partyDinner(){
entry();//entry a restaurant
order();//the method will be rewrite by subclass
eat();//eat your dinner
}
private void entry() {System.out.println("进餐馆");}
/**
* the method will have deferent realization in subclass
* @author majin
*/
protected abstract void order();
private void eat() {System.out.println("吃");}
}
//in subclass
public class Party4Meat extends Party{
@Override
protected void order() {
System.out.println("来一份肉");
}
}
//同样可定义其他的类,为不同点单的人服务
到此,我们使用这种设计模式到底产生了什么样的好处?
1.增加了程序的可维护性和可扩展性。
2.减少了代码的冗余,使代码更加容易阅读。
3.父类中某些算法的步骤延迟到子类中其实现。
4.父类提供了算法的框架、算法的控制流程,子类不能改变方法的执行流程,子类方法的调用有父类来决定。
5.为了增强封装性,可以把父类中的不想让子类修改的方法 设成 private 或者是 protected final
====================================================
那么,问题又来了,你把每一个要作的服务类型都定义出一个子类来,那我的服务类型很多怎么办啊?
为了解决子类泛滥的问题,我们引入 回调(callback)。
相信回调这个词,对大家来说都不会陌生。在c语言中,使用了函数指针来完成的回调;在c#中,使用了代理(delegate)来实现的……不一一列举了(别的我也不知道)。在我们大java中使用的是匿名内部类完成的回调。简单理解回调,在适当的时候调用一段逻辑的引用。
在数据库操作时,
1.get connection
2.get statement
3.excute and get result
一、二步对每次查询来说都是相同的,只有第三步的查询结果处理不一样。我们若是考虑上面的设计思路,那我们要创建的子类会有多少那并且你不可能设计出所有的可能处理。
import java.sql.ResultSet;
/**
* @author majin
*
* @param <T> 要返回的结果类型
*/
public interface SetresultHandler<T> {
public T hanle(ResultSet rs);
}
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* 对外提供jdbc的查询功能,查询结果的处理依赖于 回调函数ResultSetHandler.query()
* @author majin
*
*/
public class SimpleJDBCDemo {
public <T> T query(String queryStr, ResultSetHandler<T> handler) {
//url name password is need
String url=" ", user=" ", password=" ";
Connection connection = null;
Statement stmt = null;
ResultSet rs = null;
try {
//class is need
Class.forName("");
connection = DriverManager.getConnection(url, user, password);
stmt = connection.createStatement();
rs = stmt.executeQuery(queryStr);
} catch (Exception e) {
//do something and close resource
}
return handler.hanle(rs);//return handle result
}
}
//use SimpleJDBCDemo
public void main(String args[]){
new SimpleJDBCDemo().query("select * from emp", new ResultSetHandler<Boolean>() {
@Override
public Boolean hanle(ResultSet rs) {
//do something to rs
return true;
}
});
}
这样我们就解决了子类过多的烦恼。
相信,熟悉android开发的朋友,对上面这种简单的内部类不会陌生。
据说,这种 模板方法 + 回调 在Spring中使用非常的广泛。
===================================================
最后,总结一下,模板方法让我们减少了代码的冗余,增加了代码的重用性,使代码容易阅读和维护。但是,单纯的使用模板方法会让我们的类越来越多。于是,我们引入了回调—匿名内部类。但是,匿名内部类又带来另外的问题,当我们要实现的接口比较多、实现代码比较长时,匿名内部类的弊端就出现了,看得人头疼啊!那么,就需要我们适时的使用他们了。
最后,吐槽一下,UML咋加上去啊!