设计模式 模版方法模式 展现程序员的一天

继续设计模式~ 模版方法模式

老套路,先看下定义:定义了一个算法的骨架,而将一些步骤延迟到子类中,模版方法使得子类可以在不改变算法结构的情况下,重新定义算法的步骤。

简单看下定义,模版方法定义了一个算法的步骤,并且允许子类为一个或多个步骤提供实现。定义还算清晰,下面来个例子展示下本公司的上班情况(纯属娱乐,如有雷同,请对号入座)。简单描述一下:本公司有程序猿、测试、HR、项目经理等人,下面使用模版方法模式,记录下所有人员的上班情况:

首先来个超类,超类中定义了一个workOneDay方法,设置为作为算法的骨架:

package com.zhy.pattern.template;

public abstract class Worker
{
    protected String name;

    public Worker(String name)
    {
        this.name = name;
    }

    /**
     * 记录一天的工作
     */
    public final void workOneDay()
    {

        System.out.println("-----------------work start ---------------");
        enterCompany();
        computerOn();
        work();
        computerOff();
        exitCompany();
        System.out.println("-----------------work end ---------------");

    }

    /**
     * 工作
     */
    public abstract void work();

    /**
     * 关闭电脑
     */
    private void computerOff()
    {
        System.out.println(name + "关闭电脑");
    }

    /**
     * 打开电脑
     */
    private void computerOn()
    {
        System.out.println(name + "打开电脑");
    }

    /**
     * 进入公司
     */
    public void enterCompany()
    {
        System.out.println(name + "进入公司");
    }

    /**
     * 离开公司
     */
    public void exitCompany()
    {
        System.out.println(name + "离开公司");
    }

}

定义了一个上班(算法)的骨架,包含以下步骤:

a、进入公司

b、打开电脑

c、上班情况

d、关闭电脑

e、离开公司

可以看到,a、b、d、e我们在超类中已经实现,子类仅实现work这个抽象方法,记录每天的上班情况。下面各类屌丝入场:

程序猿:

package com.zhy.pattern.template;

public class ITWorker extends Worker
{

    public ITWorker(String name)
    {
        super(name);
    }

    @Override
    public void work()
    {
        System.out.println(name + "写程序-测bug-fix bug");
    }

}

HR:

package com.zhy.pattern.template;

public class HRWorker extends Worker
{

    public HRWorker(String name)
    {
        super(name);
    }

    @Override
    public void work()
    {
        System.out.println(name + "看简历-打电话-接电话");
    }

}

测试人员:

package com.zhy.pattern.template;

public class QAWorker extends Worker
{

    public QAWorker(String name)
    {
        super(name);
    }

    @Override
    public void work()
    {
        System.out.println(name + "写测试用例-提交bug-写测试用例");
    }

}

项目经理:

package com.zhy.pattern.template;

public class ManagerWorker extends Worker
{

    public ManagerWorker(String name)
    {
        super(name);
    }

    @Override
    public void work()
    {
        System.out.println(name + "打dota...");
    }

}

下面我们测试下:

package com.zhy.pattern.template;

public class Test
{
    public static void main(String[] args)
    {

        Worker it1 = new ITWorker("鸿洋");
        it1.workOneDay();
        Worker it2 = new ITWorker("老张");
        it2.workOneDay();
        Worker hr = new HRWorker("迪迪");
        hr.workOneDay();
        Worker qa = new QAWorker("老李");
        qa.workOneDay();
        Worker pm = new ManagerWorker("坑货");
        pm.workOneDay();

    }
}

输出结果:

-----------------work start ---------------
鸿洋进入公司
鸿洋打开电脑
鸿洋写程序-测bug-fix bug
鸿洋关闭电脑
鸿洋离开公司
-----------------work end ---------------
-----------------work start ---------------
迪迪进入公司
迪迪打开电脑
迪迪看简历-打电话-接电话
迪迪关闭电脑
迪迪离开公司
-----------------work end ---------------
-----------------work start ---------------
老李进入公司
老李打开电脑
老李写测试用例-提交bug-写测试用例
老李关闭电脑
老李离开公司
-----------------work end ---------------
-----------------work start ---------------
坑货进入公司
坑货打开电脑
坑货打dota...
坑货关闭电脑
坑货离开公司
-----------------work end ---------------

好了,恭喜你,又学会一个设计模式,模版方法模式。

下面看下模版方法模式类图,和我们程序的类图:

模版方式里面也可以可选的设置钩子,比如现在希望记录程序员离开公司的时间,我们就可以在超类中添加一个钩子:

public boolean isNeedPrintDate()
    {
        return false;
    }
    /**
     * 离开公司
     */
    public void exitCompany()
    {
        if (isNeedPrintDate())
        {
            System.out.print(new Date().toLocaleString()+"-->");
        }
        System.out.println(name + "离开公司");
    }

超类中添加了一个isNeedPrintDate方法,且默认返回false,不打印时间。如果某子类需要调用打印时间,可以复写改钩子方法,返回true,比如,程序猿复写了这个方法:

package com.zhy.pattern.template;

public class ITWorker extends Worker
{

    public ITWorker(String name)
    {
        super(name);
    }

    @Override
    public void work()
    {
        System.out.println(name + "写程序-测bug-fix bug");
    }

    @Override
    public boolean isNeedPrintDate()
    {
        return true;
    }
    
}

最后再看下测试结果:

-----------------work start ---------------
鸿洋进入公司
鸿洋打开电脑
鸿洋写程序-测bug-fix bug
鸿洋关闭电脑
2014-5-19 19:17:05-->鸿洋离开公司
-----------------work end ---------------

好了,关于钩子,超类中可提供默认实现或者空实现,子类可覆盖或者不覆盖,具体根据需求来定。

最近恰好,再写一个爬虫程序,用到了模版方法模式,给大家分享下:

需求分析:程序需要对特定的20个网站进行抓取数据;每个网站页面返回的结果数据不同,url不同,参数不同等;但是抓取的过程是一致的。

于是我就这样的设计:

a、定义一个规则Rule类(包含了:url,params,request_method,以及返回哪块数据【根据选择器】)

b、通过Rule进行抓取数据

c、对数据进行处理

我把上面3个步骤定义了算法的骨架,b为超类实现,a、c由子类实现:

package com.zhy.pattern.template;

public abstract class AbsExtractInfo
{

    
    /**
     * 抓取的算法骨架
     */
    public void extract()
    {
        Rule rule = generateRule() ;
        List<Element> eles = getInfosByRule(rule);
        dealResult(eles);
    }
    
    /**
     * 生成一个Rule
     * @return
     */
    public abstract Rule generateRule();
    
    /**
     * 抓取的实现
     * @param rule
     * @return
     */
    private List<Element> getInfosByRule(Rule rule)
    {
        // the implements omitted 
    }
    /**
     * 处理抓取的结果
     * @param results
     */
    public void dealResult(List<Element> results);
}

其中GenerateRule这个方法,恰好是工厂模式中的抽象方法模式(定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法模式把类实例化的过程推迟到子类),如果你忘记了,可以查看设计模式 工厂模式 从卖肉夹馍说起

好了,就到这里,最后欢迎大家留言。

 

 

[java]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. package com.zhy.pattern.template;  
  2.   
  3. public class QAWorker extends Worker  
  4. {  
  5.   
  6.     public QAWorker(String name)  
  7.     {  
  8.         super(name);  
  9.     }  
  10.   
  11.     @Override  
  12.     public void work()  
  13.     {  
  14.         System.out.println(name + "写测试用例-提交bug-写测试用例");  
  15.     }  
  16.   
  17. }  

项目经理:

 

 

[java]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. package com.zhy.pattern.template;  
  2.   
  3. public class ManagerWorker extends Worker  
  4. {  
  5.   
  6.     public ManagerWorker(String name)  
  7.     {  
  8.         super(name);  
  9.     }  
  10.   
  11.     @Override  
  12.     public void work()  
  13.     {  
  14.         System.out.println(name + "打dota...");  
  15.     }  
  16.   
  17. }  

下面我们测试下:

 

 

[java]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. package com.zhy.pattern.template;  
  2.   
  3. public class Test  
  4. {  
  5.     public static void main(String[] args)  
  6.     {  
  7.   
  8.         Worker it1 = new ITWorker("鸿洋");  
  9.         it1.workOneDay();  
  10.         Worker it2 = new ITWorker("老张");  
  11.         it2.workOneDay();  
  12.         Worker hr = new HRWorker("迪迪");  
  13.         hr.workOneDay();  
  14.         Worker qa = new QAWorker("老李");  
  15.         qa.workOneDay();  
  16.         Worker pm = new ManagerWorker("坑货");  
  17.         pm.workOneDay();  
  18.   
  19.     }  
  20. }  

输出结果:

 

 

[java]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. -----------------work start ---------------  
  2. 鸿洋进入公司  
  3. 鸿洋打开电脑  
  4. 鸿洋写程序-测bug-fix bug  
  5. 鸿洋关闭电脑  
  6. 鸿洋离开公司  
  7. -----------------work end ---------------  
  8. -----------------work start ---------------  
  9. 迪迪进入公司  
  10. 迪迪打开电脑  
  11. 迪迪看简历-打电话-接电话  
  12. 迪迪关闭电脑  
  13. 迪迪离开公司  
  14. -----------------work end ---------------  
  15. -----------------work start ---------------  
  16. 老李进入公司  
  17. 老李打开电脑  
  18. 老李写测试用例-提交bug-写测试用例  
  19. 老李关闭电脑  
  20. 老李离开公司  
  21. -----------------work end ---------------  
  22. -----------------work start ---------------  
  23. 坑货进入公司  
  24. 坑货打开电脑  
  25. 坑货打dota...  
  26. 坑货关闭电脑  
  27. 坑货离开公司  
  28. -----------------work end ---------------  

好了,恭喜你,又学会一个设计模式,模版方法模式。

下面看下模版方法模式类图,和我们程序的类图:

模版方式里面也可以可选的设置钩子,比如现在希望记录程序员离开公司的时间,我们就可以在超类中添加一个钩子:

 

[java]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. public boolean isNeedPrintDate()  
  2.     {  
  3.         return false;  
  4.     }  
  5.     /** 
  6.      * 离开公司 
  7.      */  
  8.     public void exitCompany()  
  9.     {  
  10.         if (isNeedPrintDate())  
  11.         {  
  12.             System.out.print(new Date().toLocaleString()+"-->");  
  13.         }  
  14.         System.out.println(name + "离开公司");  
  15.     }  

超类中添加了一个isNeedPrintDate方法,且默认返回false,不打印时间。如果某子类需要调用打印时间,可以复写改钩子方法,返回true,比如,程序猿复写了这个方法:

 

 

[java]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. package com.zhy.pattern.template;  
  2.   
  3. public class ITWorker extends Worker  
  4. {  
  5.   
  6.     public ITWorker(String name)  
  7.     {  
  8.         super(name);  
  9.     }  
  10.   
  11.     @Override  
  12.     public void work()  
  13.     {  
  14.         System.out.println(name + "写程序-测bug-fix bug");  
  15.     }  
  16.   
  17.     @Override  
  18.     public boolean isNeedPrintDate()  
  19.     {  
  20.         return true;  
  21.     }  
  22.       
  23. }  
最后再看下测试结果:

 

 

[java]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. -----------------work start ---------------  
  2. 鸿洋进入公司  
  3. 鸿洋打开电脑  
  4. 鸿洋写程序-测bug-fix bug  
  5. 鸿洋关闭电脑  
  6. 2014-5-19 19:17:05-->鸿洋离开公司  
  7. -----------------work end ---------------  

 

好了,关于钩子,超类中可提供默认实现或者空实现,子类可覆盖或者不覆盖,具体根据需求来定。


最近恰好,再写一个爬虫程序,用到了模版方法模式,给大家分享下:

需求分析:程序需要对特定的20个网站进行抓取数据;每个网站页面返回的结果数据不同,url不同,参数不同等;但是抓取的过程是一致的。

于是我就这样的设计:

a、定义一个规则Rule类(包含了:url,params,request_method,以及返回哪块数据【根据选择器】)

b、通过Rule进行抓取数据

c、对数据进行处理

我把上面3个步骤定义了算法的骨架,b为超类实现,a、c由子类实现:

 

[java]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. package com.zhy.pattern.template;  
  2.   
  3. public abstract class AbsExtractInfo  
  4. {  
  5.   
  6.       
  7.     /** 
  8.      * 抓取的算法骨架 
  9.      */  
  10.     public void extract()  
  11.     {  
  12.         Rule rule = generateRule() ;  
  13.         List<Element> eles = getInfosByRule(rule);  
  14.         dealResult(eles);  
  15.     }  
  16.       
  17.     /** 
  18.      * 生成一个Rule 
  19.      * @return 
  20.      */  
  21.     public abstract Rule generateRule();  
  22.       
  23.     /** 
  24.      * 抓取的实现 
  25.      * @param rule 
  26.      * @return 
  27.      */  
  28.     private List<Element> getInfosByRule(Rule rule)  
  29.     {  
  30.         // the implements omitted   
  31.     }  
  32.     /** 
  33.      * 处理抓取的结果 
  34.      * @param results 
  35.      */  
  36.     public void dealResult(List<Element> results);  
  37. }  

 

其中GenerateRule这个方法,恰好是工厂模式中的抽象方法模式(定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法模式把类实例化的过程推迟到子类),如果你忘记了,可以查看设计模式 工厂模式 从卖肉夹馍说起

好了,就到这里,最后欢迎大家留言。

 

 

 

 
25
 
1
 
我的同类文章
 
猜你在找
查看评论
12楼  baozhonghaha2016-03-28 10:20发表 [回复]
恭喜,恭喜。。。大神新婚快乐。。
11楼  无泪无悔902016-02-05 15:28发表 [回复]
看到坑货是打dota的,我瞬间觉得我的人生有了目标
10楼  corffen2016-01-30 16:19发表 [回复]
看到坑货二字 我终于忍不住要评论了 嘎嘎嘎
9楼  jacksgun2015-11-12 19:36发表 [回复]
这个例子太逗了。看的我都笑了
8楼  _ontheway_2015-08-17 17:18发表 [回复]
学习了,这模式挺常用的,不过现在才知道这也是一种设计模式
7楼  牛迁迁2015-08-17 09:49发表 [回复]
学习了,理解的不错。
6楼  carbit_tianpan2015-06-14 21:46发表 [回复]
真的很受用
5楼  oilover2015-06-13 22:05发表 [回复]
写的很好!受教了!
4楼  大梦未觉2014-12-25 09:03发表 [回复]
不错,写得挺简洁明了的
3楼  superdcj2014-11-27 11:31发表 [回复]
代码36行少了一个abstract修饰符
public void dealResult(List<Element> results);
2楼  dodoniao2014-09-18 13:37发表 [回复]
可否把爬虫的代码
放到一个公共的地方,大家学习下?
谢谢了
1楼  while-living2014-07-23 17:53发表 [回复]
你英文名叫dota吧。支持支持,但感觉就算知道有这模式,对于要在什么时候用还是不清楚啊,这个只能靠经验了吧。
 
发表评论
  • 用 户 名:
  • firelightdragon
  • 评论内容:
  • 插入代码
  •   
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
 
 
 
 
个人资料
 
鸿洋_
 
 
5  2  1
    • 访问:6861386次
    • 积分:36662
    • 等级:  
    • 排名:第64名
    • 原创:178篇
    • 转载:0篇
    • 译文:6篇
    • 评论:9506条
我的微信公众号
    • 长期为您推荐优秀博文、开源项目、视频等,进入还有好玩的等着你,欢迎扫一扫。 
联系方式

  • QQ群:
    • 429757068
    • 264950424
    • 463081660
    • 请勿重复加群,Thx
我的微博
博客专栏
 HTML5 & CSS3 实战 

文章:11篇

阅读:102483
 设计模式融入生活 

文章:10篇

阅读:62944
 Android 精彩案例 

文章:67篇

阅读:3046828
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值