16.2 解 决 方 案

16.2.1 使用模板方法模式来解决问题

用来解决上述问题的一个合理的解决方案就是模板方法模式。那么什么是模板方法模式呢?

1.模板方法模式的定义

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

2.应用模板方法模式来解决问题的思路

仔细分析上面的问题,重复或相似代码太多、扩展不方便,出现这些问题的原因在哪里?主要就是两个实现是完全分开、相互独立的,没有从整体上进行控制。如果把两个模块合起来看,就会发现,那些重复或相似的代码应该被抽取出来,做成公共的功能,而不同的登录控制就可以去扩展这些公共的功能。这样一来,扩展的时候,如果出现有相同的功能,直接扩展公共功能就可以了。

使用模板方法模式,就可以很好地实现上面的思路。分析上面两个登录控制模块,会发现它们在实现上有着大致相同的步骤,只是在每步具体的实现上,略微有些不同。因此,可以把这些运算步骤看做是算法的骨架,把具体的不同的步骤实现延迟到子类去实现,这样就可以通过子类来提供不同的功能实现了。

经过分析总结,登录控制大致的逻辑判断步骤如下。

(1)根据登录人员的编号去获取相应的数据。

(2)获取对登录人员填写的密码数据进行加密后的数据,如果不需要加密,那就直接返回登录人员填写的密码数据。

(3)判断登录人员填写的数据和从数据库中获取的数据是否匹配。

在这三个步骤里面,第一个和第三个步骤是必不可少的,而第二个步骤是可选的。那么就可以定义一个父类,在其中定义一个方法来定义这个算法骨架,这个方法就是模板方法,然后把父类无法确定的实现,延迟到具体的子类来实现就可以了。

通过这样的方式,如果要修改加密的算法,那就在模板的子类里面重新覆盖实现加密的方法就可以了,完全不需要去改变父类的算法结构,即可重新定义这些特定的步骤。

16.2.2 模板方法模式的结构和说明

模板方法模式的结构如图16.1所示。

图片

图16.1 模板方法模式的结构示意图

■ AbstractClass:抽象类。用来定义算法骨架和原语操作,具体的子类通过重定义这些原语操作来实现一个算法的各个步骤。在这个类里面,还可以提供算法中通用的实现。

■ ConcreteClass:具体实现类。用来实现算法骨架中的某些步骤,完成与特定子类相关的功能。

16.2.3 模板方法模式示例代码

(1)先来看看AbstractClass的写法。示例代码如下:

adc37c8a0c8a45269bd73df24de6f21c

799a169be9224380b16df0c0993777d4

(2)再看看具体实现类的写法。示例代码如下:

a6739974c7ff43b08b567004534b23de

16.2.4 使用模板方法模式重写示例

要使用模板方法模式来实现前面的示例,需要按照模板方法模式的定义和结构,定义出一个抽象的父类,在这个父类中定义模板方法,这个模板方法应该实现进行登录控制的整体的算法步骤。对于公共的功能,就放到这个父类中实现,而这个父类无法决定的功能,就延迟到子类去实现。

这样一来,两种登录控制就做为这个父类的子类,分别实现自己需要的功能。此时系统的结构如图16.2所示。

图片

图16.2 使用模板方法模式实现示例的结构示意图

(1)为了把原来的两种登录控制统一起来,首先需要把封装登录控制所需要的数据模型统一起来,不再区分是用户编号还是工作人员编号,而统一称为登录人员编号,并且将其他用不上的数据删除,这样直接使用一个数据模型就可以了。当然,如果各个子类实现需要其他的数据,还可以自行扩展。示例代码如下:

095da0707ede4516965bc0d075f693bf

(2)接下来定义公共的登录控制算法骨架。示例代码如下:

814dc6b62e8c45bebe3ca1d8bca37729

e35da8ae9d0b4f1f8cacb5f0cafcab98

f546c5ee44f94f45be337b16974191ab

6caeaefe863c40faae9a860b3fede65e

(3)实现新的普通用户登录控制的逻辑处理。示例代码如下:

be1b1611fb9e4d1f9fe3a8569c7bb72d

(4)实现新的工作人员登录控制的逻辑处理。示例代码如下:

b01268b8a4a44b9eb69c610991207a4c

通过上面的示例,可以看出来,把原来的实现改成使用模板方法模式来实现也并不困难。写个客户端测试一下,以便更好地体会。示例代码如下:

6431a777ac564cf5bf99a653af33c8c3

bf1fa5c07a764c5cb65b1a00ac4c850f

运行结果示例如下:

e46f9406a5a94658919e30c981c89bf0

当然,你可以使用不同的测试数据来测试这个示例。