看懂源码从设计模式开始 —— 模版模式

在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方式使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。通俗的讲,就是当你的程序中,有多个类,方法是相同的,只是具体实现有差异时,就可以把这多个类使用模版方法模式进行封装,也就是将通用的步骤抽离出来,定义成一套可以多次使用的模版。

要点

  1. 模版方法中只定义了算法的步骤,把这些步骤的具体实现延迟到子类中进行。

  2. 模板方法的抽象类可以定义具体方法、抽象方法和钩子;抽象方法交给子类去实现。

  3. 钩子是一种方法,它在抽象类中不做事,或者只做默认的事情,子类可以选择要不要覆盖它。

 

模版方法模式类图


 

在java源码中:在j.U.C包中,针对ReentrantLock和ReenTrantReadWriteLock就使用了模版方法将通用的部分,封住出了AQS,针对上图AQS就是AbstractClass,而ConcreteClass就分别是ReentrantLock和ReenTrantReadWriteLock,因为ReenTrantReadWriteLock中,写锁的实现和ReenTrantLock的中的锁实现基本是一致的。

实例分析


 

作为一个程序猿,晚上加班是不可避免的,有的时候加班熬不住了,就需要借助外物来提提神,有的人喜欢喝茶提神,有的人喜欢喝咖啡来提神,这个时候就需要设计两个类,一个是茶,一个是咖啡,代码如下:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
//咖啡类public class Coffee {     void prepareRecipe() {        boilWater();        brewCoffeeGrinds();        pourInCup();        addSugarAndMilk();    }     //把水煮沸    public void boilWater() {        System.out.println("Boiling water");    }     //用沸水冲泡咖啡    public void brewCoffeeGrinds() {        System.out.println("Dripping Coffee through filter");    }     //把咖啡倒进杯子    public void pourInCup() {        System.out.println("Pouring into cup");    }     //加糖和牛奶    public void addSugarAndMilk() {        System.out.println("Adding Sugar and Milk");    }}
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
//茶类public class Tea {     void prepareRecipe() {        boilWater();        steepTeaBag();        pourInCup();        addLemon();    }        //把水煮沸    public void boilWater() {        System.out.println("Boiling water");    }     //泡茶叶    public void steepTeaBag() {        System.out.println("Steeping the tea");    }     //把茶倒进杯子    public void pourInCup() {        System.out.println("Pouring into cup");    }     //加柠檬    public void addLemon() {        System.out.println("Adding Lemon");    }}

从这两个类中可以看出,其实泡茶和泡咖啡都需要把水煮沸,倒进杯子,只是一个是加咖啡,牛奶,一个是加茶叶、加柠檬,根据模版模式,可以将这两个对象进行处理,抽离一个超类作为模版

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
//这是一个模板public abstract class drinks {     //将这个方法声明为final,不希望子类覆盖这个方法    final void prepareRecipe() {        boilWater();        brew();        pourInCup();        addCondiments();    }     //通用的方法直接在父类中实现,在这个例子中,就是烧水和倒进杯子    public void boilWater() {        System.out.println("Boiling water");    }     public void pourInCup() {        System.out.println("Pouring into cup");    }     //需要由子类的去实现的方法,定义为抽象方法    abstract void brew();     abstract void addCondiments(); }

在重新定义茶和咖啡的类:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
//新的茶类继承饮料public class Tea extends Drinks {     //将父类中的抽象方法具体化,来满足自己的实现    @Override    void brew() {        System.out.println("Steeping the tea");    }     @Override    void addCondiments() {        System.out.println("Adding Lemon");    }}
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
//新的咖啡类继承自模板public class Coffee extends Drinks {     //将父类中的抽象方法具体化,来满足自己的实现    @Override    void brew() {        System.out.println("Dripping Coffee through filter");    }     @Override    void addCondiments() {        System.out.println("Adding Sugar and Milk");    }}

但是本人在喝茶的时候,不喜欢放柠檬片,只想保留茶的原汁原味,但是其他人,可能和我的口味不一样,那么这时就需要根据个人喜好去决定到底要不要加柠檬片,这个时候就可以使用,模版方法中的钩子,那么什么是钩子呢?

钩子是一种被声明在抽象类中的方法,但只有空和默认的实现。钩子的存在,可以让子类有能力对算法中不同点进行挂钩。需不需要挂钩由子类自行决定。

重新定义一下模板类:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
//这是一个模板public abstract class Drinks {     //将这个方法声明为final,不希望子类覆盖这个方法    final void prepareRecipe() {        boilWater();        brew();        pourInCup();        if (customerDecision()) {            addCondiments();        }     }     //通用的方法直接在父类中实现,在这个例子中,就是烧水和倒进杯子    public void boilWater() {        System.out.println("Boiling water");    }     public void pourInCup() {        System.out.println("Pouring into cup");    }     //需要由子类的去实现的方法,定义为抽象方法    abstract void brew();     abstract void addCondiments();     //这是一个钩子,子类可以决定是否覆盖这个方法    boolean customerDecision() {        return true;    } }

在修改一下茶的类,加入钩子的实现

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
//新的茶类继承饮料public class Tea extends Drinks {     //将父类中的抽象方法具体化,来满足自己的实现    @Override    void brew() {        System.out.println("Steeping the tea");    }     @Override    void addCondiments() {        System.out.println("Adding Lemon");    }     //覆盖钩子实现自己的定义    @Override    boolean customerDecision() {        String answer = getUserImput();        if (answer.toLowerCase().startsWith("y")) {            return true;        }         return false;     }     private String getUserImput() {        String answer = null;        System.out.println("需要加柠檬片嘛?(y/n)");        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        try {            answer = in.readLine();        } catch (Exception e) {            System.out.println(e.getMessage());        }        if (answer == null) {            return "no";        }        return answer;    }}

最和来一个测试方法:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
public class DrinksTest {     public static void main(String[] args) {        //创建茶和咖啡        Tea tea = new Tea();        Coffee coffee = new Coffee();        System.out.println("prepare Tea:");        tea.prepareRecipe();        System.out.println("prepare Coffee:");        coffee.prepareRecipe();    }}

本文部分内存参考《Head First 设计模式》

品略图书馆 http://www.pinlue.com/

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值