对一个问题的思考
假设有functionA和functionB,他们的的大部分方法付相同:步骤一 步骤二 步骤四,而小部分方法不同:步骤三A和 步骤三B,而且步骤四依赖步骤三的结果。那么如何重构这部分代码呢
funcitona(){
//步骤一
//步骤二
//步骤三A
String result = "xxx"
//步骤四,使用result
}
funcitonb(){
//步骤一
//步骤二
//步骤三B
String result = "yyy"
//步骤四,使用result
}
使用回调函数重构代码
Java
public class MyClass {
public void functionTemplate(Consumer<String> stepThree) {
// 步骤一
// 步骤二
String result = stepThree.apply();
// 步骤四,使用result
}
public void functionA(String param) {
functionTemplate(() -> {
return param;
});
}
public void functionB(String param) {
functionTemplate(() -> {
return param;
});
}
public static main(String[] args){
MyClass myclass = new MyClass();
myclass.functionA("xxx");
myclass.functionB("yyy");
}
}
Groovy
class MyClass {
def functionTemplate(Closure stepThree) {
// 步骤一
// 步骤二
def result = stepThree()
// 步骤四,使用result
}
def functionA(String param) {
functionTemplate {
// 步骤三A
return param
}
}
def functionB(String param) {
functionTemplate {
// 步骤三B
return param
}
}
public static main(String[] args){
MyClass myclass = new MyClass();
myclass.functionA("xxx");
myclass.functionB("yyy");
}
}
PHP
class MyClass {
public function functionTemplate($stepThree) {
// 步骤一
// 步骤二
$result = $stepThree();
// 步骤四,使用$result
}
public function functionA($param) {
$this->functionTemplate(function() {
// 步骤三A
return $param;
});
}
public function functionB($param) {
$this->functionTemplate(function() {
// 步骤三B
return $param;
});
}
public static main(String[] args){
MyClass myclass = new MyClass();
myclass.functionA("xxx");
myclass.functionB("yyy");
}
}
其他方式
模板方法模式
abstract class TemplateMethod {
void templateExecute() {
// 步骤一
// 步骤二
specificStep()
// 步骤四
}
abstract void specificStep()
}
class FunctionA extends TemplateMethod {
void specificStep() {
// 步骤三A
}
}
class FunctionB extends TemplateMethod {
void specificStep() {
// 步骤三B
}
}
TemplateMethod functionA = new FunctionA();
functionA.templateExecute();
TemplateMethod functionB = new FunctionB();
functionB.templateExecute();
策略模式
interface StepStrategy {
void stepThree()
}
class StepThreeA implements StepStrategy {
void stepThree() {
// 步骤三A
}
}
class StepThreeB implements StepStrategy {
void stepThree() {
// 步骤三B
}
}
void executeFunction(StepStrategy strategy) {
// 步骤一
// 步骤二
strategy.stepThree()
// 步骤四
}
// 使用
executeFunction(new StepThreeA()) // 执行 functiona
executeFunction(new StepThreeB()) // 执行 functionb
可以看到模板方法和策略模式还是有些不同的:
1)调用的方法不同:模板方法调用的是具体的继承类;策略方法调用的是统一的策略
2)组合的方式不同:模板方法在父类中声明抽象方法来定义算法的可变部分,子类必须实现这些方法,实例方法来定义不变的部分,再由子类继承不变的部分,同时实现可变的部分;继承方法将不同的定义一个接口声明可变的部分,并由具体类将实现不变的部分,通过上下文类来使用这些算法。
3)一个是继承,一个是组合:模板方法模式通常通过继承来实现,子类继承父类并重写父类中声明为抽象的方法;策略模式通常通过组合来实现,策略类实现相同的接口,客户端持有策略接口的引用,并可以在运行时切换策略。
4)灵活性:策略模式提供了更高的灵活性,因为可以在运行时切换算法;模板方法模式则更注重于算法的结构和步骤的固定
策略模式适用的场景
行为切换:当系统中有许多类,这些类的区别仅在于它们的行为不同。使用策略模式,可以动态地让用户对象在这些行为中选择一个行为。
算法选择:系统中需要动态地在几种算法中选择一种。例如,一个支付系统可能需要根据不同的国家/地区选择不同的支付算法。
模板方法模式适用的场景
算法骨架:当有一个算法的通用骨架,但各个步骤的实现可能不同,你希望通过子类来提供具体实现。
避免代码重复:各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复。例如,多个不同类型的报告生成器可能会共享某些步骤,但在某些步骤上有所不同。
举个例子
假设你正在开发一个电子商务平台,你需要实现一个促销模块来处理不同类型的折扣策略,如“满减”、“打折”、“返现”等。这时,你可以使用策略模式,为每种折扣策略定义一个策略类,并在运行时动态选择使用哪一个。
另一方面,如果你正在开发一个数据分析软件,其中大部分的数据处理流程是固定的,但某些步骤,如数据清洗或格式化,可能因数据源而异。在这种情况下,你可以使用模板方法模式,定义一个算法的基本结构,并允许子类实现这些特定的步骤。
策略模式侧重于行为的多样性和可互换性,而模板方法模式侧重于算法结构的一致性和步骤的定制化。