一:初始模板方法模式
包括:定义,结构,参考实现
二:体会模板方法模式
包括:场景问题,不用模式解决方案,使用模式解决方案
三:理解模板方法模式
包括:认识模板方法模式,模板的写法,Java回调与模板方法模式,典型应用:排序,实现通用的增删改查,模板方法的优缺点。
四:思考模板方法模式
包括:模板模式的本质,对设计原则的体现,何时选用
一:初始模板方法模式
1,定义
定义一个操作中的算法的骨架(父类),而将一些步骤延迟到子类,模板方法视子类可以不改变一个算法的结构,既可以重定义该算法的某些特定步骤。
2,结构和说明
AbstractClass:抽象类,用来定义算法骨架和原语操作(就是抽象的操作,必有由子类提供实现),在这个类里面,你可以提供算法中通用的实现。
ConcreteClass: 具体的实现类。用来实现算法骨架中某些特定的步骤,完成跟特定子类相关的功能。
二:体会模板方法模式
1,登录控制
详见代码template.example3和template.example2
问题:
两种登录实现太相似了,现在是完全分开的,今后扩展功能,那么两个模块都要修改,很麻烦的,也有很多相似的地方,
1),重复或相似代码太多,2)扩展不方便
使用模式解决:代码详见example4
三:理解模板方法模式
1,功能
在于固定算法骨架,而让具体算法实现可扩展。
在设计框架级功能的时候非常有用,框架定义了算法的步骤,在合适的点让开发人员进行扩展,实现具体的算法,比如DAO的具体增删改查
额外的好处:控制子类的扩展,因为在父类 里面定义了算法的步骤,只是某几个固定的点才回调用被子类实现的方法,因此也就允许这几个点来扩展功能,这些可以被子类覆盖以扩展功能的方法通常称为钩子方法
2,为何不是接口
首先要清楚接口和抽象类的关系
接口:特殊的抽象类,所有的方法是抽象的方法,所有的属性都是静态属性
抽象类:特殊的类:可以包含抽象方法(也可以没有),可以定义公共的功能方法(既要约束子类的行为,又要为子类提供公共的功能)
模板方法需要固定定义算法骨架,这个骨架算是一个公共的行为,但是具体的实现步骤可能是不同的,恰好符合抽象类的原则。
把模板实现成为抽象类,为每一个子类提供公共的功能,就是定义了算法骨架,同时把模板里面需要子类扩展的具体步骤的算法定义成抽象方法,要求子类去实现这些方法,这就约束了子类的行为。
3,变与不变
分析程序中那些公能是可变的,那些功能是不变的,把不变的部分抽象出来,进行公共的实现,把变化的部分分离出去,用接口进行隔离,或用抽象类约束子类的行为。
4,好莱坞法则
不要找我们,我们会联系你
模板方法很好的体现这一点,作为父类的模板会在需要的时候,调用子类的相应的方法,也就是父类找子类,而不是让子类找父类。
这是一种反向的控制结构
5,java的动态绑定采用的是后期绑定,对于出现子类覆盖父类的方法的情况,在编译时看数据类型,运行时看实际的对象类型(new 操作符后跟的构造方法是哪个类的),一句话:new谁调用谁的方法。
其实在写父类的时候,它调用的是父类的抽象方法,只是在运行的时候被动态的绑定到子类的实现方法
6,扩展登录控制
见代码exmaple2下的NormalLogin2类下;
7,模板的写法代码详见example5
通常在模板包含如下操作类型
- 模板方法,就是定义算法的骨架的方法
- 具体操作,在模板中直接实现某些具体步骤的方法,通常这些步骤的实现方法是固定的,而且是不怎么变化,因此可以当做公共功能实现在模板里面,如果不需要给子类访问的方法,设置private,如果需要子类访问,但是不能被子类覆盖和改变的设置为protected final;
- 具体的Abstract Class操作,在模板中实现某些公共的功能,可以提供给子类使用,一般不是具体的算法步骤实现,只是一些辅助的功能。
- 原语操作:就是在模板中定义的抽象操作,通常是模板方法需要调用的操作,是必须的操作,而且自类还没有办法确定下如何实现,需要子类真正来实现的方法。
- 构子操作,在模板中定义,并提供默认的实现操作,这些方法通常被视为可扩展的点,但不是必须的,子类可以有选择的覆盖这些方法,已提供新的实现来扩展功能。
- Factory Method: 在模板方法中,如果需要得到某种对象实例的话,可以考虑通过工厂方法来获取,把具体创建对象的实现延迟到子类
8,java回调和模板方法模式
模板方法一个目的,就在于让其他类来扩展或具体实现在模板中算法骨架的某一些算法步骤。在标准的模板方法中主要使用继承的方式,来让父类在运行期间可以调用到子类的方法。
java回调技术通过回调在接口定义的方法,调用到具体的实现类的方法,其本质通洋是利用java动态绑定技术,在这种实现中,可以不把实现类写成单独的类,可以使用匿名内部类来实现回调方法。
9,两种方式的比较
- 使用继承的方式,抽象方法和具体实现的关系,是在编译期间静态决定的,是类级关系。使用java回调,这个关系是在运行期间动态的决定的,是对象级关系。
- 使用回调技术更加灵活,因为java单继承
回调是通过委托的方式来组合功能,它耦合强度低,比如某些模板实现方法,在回调的时候可以不调用模板的方法 - 使用继承方式更简单,y因为父类提供了实现子类方法,如果子类不想扩展,那就不用管了,如果使用回调,必须把所有可能使用到的功能都要定义进去。
10,典型的应用排序
在java.util包有Collection类,它里面实现了对列表排序的功能,它提供了一个静态的sort方法,接受一个列表和一个Comparator接口的实例,步骤如下代码见example7
11,实现通用的增删改查
12,优缺点
- 实现代码的复用
- 算法骨架不容易升级
四,思考模板方法模式
1,本质
固定算法骨架
2,对设计原则的体现
很好的体现开闭原则(对新增开放,对修改关闭,变得分离出去,不变的写到模板方法中)和里式替换原则(能够实现统一的算法骨架,通过切换不同的实现来切换不同的功能)
3,何时选用
- 需要固定定义算法骨架
- 各个子类具有公共行为,应该抽取出来,集中到一个公共类实现,避免代码重复
- 需要控制子类扩展的情况。模板方法会在特定的点调用子类的方法,这样只允许在这些点进行扩展