连接两个模板方法模式?

昨天在客户现场,和香港一家公司的技术人员讨论了一个比较奇怪的技术方案。晚上仔细想了一下,发现这个方案可以用设计模式的概念抽象成下面的问题:

有两个已存在的模板方法(Template Method)模式,分别实现了一个特定的算法,算法的每个步骤可以自行定义,但算法的流程已经固定下来了。那么,在不允许改变模板方法,只允许改变特定步骤的情况下,如何把这两个算法连接起来,完成一个新的功能流程呢?

例如,下图左边的算法A包含3个顺序执行的步骤,算法B包含4个步骤。如果只允许改变ConcreteClassA和ConcreteClassB中的PrimitiveOperation...,该如何实现A1、B1、A2、A3、B2、B3、B4这样的流程?或另一种流程(但A和B各自的流程顺序总保持不变)?甚至是省略某些步骤的流程,如A1、A2、B1、A3、B4?

显然,两组算法之间需要某种同步手段。我们的解决方案是,用一组Agent类来定义新的流程,同时令其作为A和B两种算法的中介。这组Agent类本身大致相当于一个策略(Strategy)模式。抽象的Agent类包含一个Process方法,具体的Agent类在Process方法中定义并执行特定的流程。同时,Agent类中保存了算法A和算法B的进展状态信息(如算法已进展到哪一个步骤等)。

在具体实现层面,Process方法是一个在后台一直运行的服务程序的主线程,它的基本思路是,根据特定的流程定义,定时查看算法A和算法B的状态,判断是否可以让算法A或算法B进入下一个步骤。Agent本身有点像一个算法闸门,闸门开的时候,算法就可以继续,反之就必须阻塞。

为了实现闸门功能,Agent类提供了一个CanContinue方法,具体的算法步骤可通过该方法查询自己是否要继续执行。

有了Agent类,实现具体的算法步骤时,我们需要为PrimitiveOperation...增添以下功能:在执行前通过Agent类的CanContinue方法查询是否可执行此操作,如果可以就执行,如果不可以就阻塞(根据模板方法的实现方式不同,阻塞操作也有不同:或者用Sleep方式挂起,或者向模板方法返回当前步骤无法执行,请稍后再试的信息),每个步骤执行完毕后,就通过Agent类完成状态更新操作。

保存算法状态是这种思路的核心。为了保存全系统唯一的状态信息,Agent显然也应是一个单件类。此外,如果算法A、算法B、Agent不是部署在一台计算机上,或者算法A、B的执行需要很长时间,中间甚至需要用户的参与,我们就必须将算法的状态保存在数据库、文件等永久存储中,以确保算法的完整性。所以,用来保存算法状态的类PersistentState就与一个操作数据库的持久层相当类似。——这种使用持久层的做法,大概和教科书里讲过的,确保分布式事务完整性的算法差不多。

整个思路如下图所示:

是不是太抽象了,我一直没讲具体的应用。到底是什么样的系统才“不允许改变模板方法”呢?其实,我们碰到的是系统集成项目经常遇到的问题。我们要用两套成熟的软件平台为客户搭建系统,一套平台负责后台服务,实现了可定制的工作流管理功能;一套平台负责前台数据处理,有用户参与,流程固定,但可以定制数据的输出格式。这两套平台都用类似于插件的方式提供了二次开发接口,我们可以自己实现插件完成具体环节的特定操作,但定义并驱动功能流程的是平台本身而不是插件。

在这种情况下,平台控制整个流程,它类似于模板方法,我们只能开发特定的操作环节,类似于实现PrimitiveOperation...,如果前台和后台两个平台谁也不认识谁,不能相互兼容,平台本身又是大公司成熟的软件产品,无法修改,我们要完成整个项目,并且要根据用户的要求实现特定的流程——这不就是如何连接两个模板方法模式的问题吗?

我也不大知道我们的解决方案是不是个好的选择,反正我们成功地把前、后台两个系统连接了起来,还可以根据用户的需要随时修改业务流程,这个结果总是好的嘛。要说起这件事的起因,也挺让人生气的,明明可以选择相互兼容的前、后台系统,客户为了摆平派系间的利益关系,偏要买两套不兼容的软件(老美的软件,价格还挺贵),同时还给我们出了这么一个难题。不过,系统集成项目最吸引人的地方就是要解决不同系统间的平滑连接问题,问题越难,程序员自然越有干劲。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,我可以帮你实现这个模板方法模式的Java代码。 首先,我们需要一个抽象类 `AbstractDB`,它包含了模板方法 `execute()`,以及四个具体方法 `coonDB()`、`openDB()`、`useDB()` 和 `closeDB()`,代码如下: ```java public abstract class AbstractDB { public final void execute() { coonDB(); openDB(); useDB(); closeDB(); } protected abstract void coonDB(); protected void openDB() { System.out.println("打开数据库"); } protected void useDB() { System.out.println("使用数据库"); } protected void closeDB() { System.out.println("关闭数据库"); } } ``` 其中,`coonDB()` 方法是抽象方法,需要在子类中实现;其他三个方法是具体方法,可以在子类中覆盖或者不覆盖。 接下来,我们需要两个子类 `SQLServerDB` 和 `OracleDB`,它们分别实现了 `coonDB()` 方法,代码如下: ```java public class SQLServerDB extends AbstractDB { @Override protected void coonDB() { System.out.println("连接 SQL Server 数据库"); } } public class OracleDB extends AbstractDB { @Override protected void coonDB() { System.out.println("连接 Oracle 数据库"); } } ``` 最后,我们可以在主函数中创建两个对象,分别执行它们的 `execute()` 方法,来验证模板方法模式的实现,代码如下: ```java public static void main(String[] args) { AbstractDB sqlServerDB = new SQLServerDB(); sqlServerDB.execute(); System.out.println("---------------"); AbstractDB oracleDB = new OracleDB(); oracleDB.execute(); } ``` 输出结果如下: ``` 连接 SQL Server 数据库 打开数据库 使用数据库 关闭数据库 --------------- 连接 Oracle 数据库 打开数据库 使用数据库 关闭数据库 ``` 可以看到,不同类型的数据库在执行模板方法时,只需要实现 `coonDB()` 方法,其他的具体方法都可以重用。这就是模板方法模式的优势所在。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值