模版方法模式
我先问个问题,把大象放冰箱,总共分几步?你一定脱口而出:三步!第一步把冰箱门打开,第二步把大象放进去,第三部把冰箱门关上。这道题是不是太简单了?但当我们考虑细节的时候就没这么简单了。
假如你的冰箱门加了锁,那么第一步开门时就需要开锁。第二步把大象放进去也有细节要考虑。如果你的冰箱是卧式的,那么大象需要躺在里面。如果你的冰箱是立式的,那么大象可以站在里面。像这种主体逻辑一样,细节实现不同的场景,我们可以考虑使用模版方法模式。
在软件建模中,把大象放冰箱是一个算法,我们将其定义为一个模版方法。模版方法用一系列抽象的操作定义一个算法。就像我们例子中的三步,打开冰箱、大象放进去、关上冰箱门。具体如何开门和关门,如何放大象进去,则在子类中实现。冰箱不同,采用的方式自然也不同。
模版方法中定义操作的方法和先后步骤。而真正的操作方法实现则在子类中。
1. 实现模版方法
我们看看用代码如何实现把大象放冰箱。为了便于理解,我们尽量少引入类,我们假设冰箱自身有个行为是把大象放进来
冰箱抽象类 Fridge
:
public abstract class Fridge {
public void placeElephant(){
System.out.println("开始装大象");
openDoor();
putElephant();
closeDoor();
System.out.println("结束");
}
public abstract void openDoor();
public abstract void putElephant();
public abstract void closeDoor();
}
Fridge
类中定义了一个模版方法 placeELephan, 里面按照顺序调用 openDoor、putELephant、closeDoor。这三个方法留待子类实现。
下面是两个具体的冰箱实现类代码。
立式冰箱类 VerticalFridge
:
public class VerticalFridge extends Fridge{
@Override
public void openDoor() {
System.out.println("打开立式冰箱门");
}
@Override
public void putElephant() {
System.out.println("将大象站着放进去");
}
@Override
public void closeDoor() {
System.out.println("关上立式冰箱门");
}
}
卧式冰箱 HorizontalFridge
:
public class HorizontalFridge extends Fridge{
@Override
public void openDoor() {
System.out.println("打开卧式冰箱门");
}
@Override
public void putElephant() {
System.out.println("将大象躺着放进去");
}
@Override
public void closeDoor() {
System.out.println("关上卧式冰箱门");
}
}
两个冰箱子类各自实现三个抽象方法。
客户端代码:
public class Client {
public static void main(String[] args) {
Fridge fridge;
fridge = new HorizontalFridge();
fridge.placeElephant();
System.out.println("---------I'm a line-----------");
fridge = new VerticalFridge();
fridge.placeElephant();
}
}
客户端代码中,两个不同的子类分表调用了placeElephant 方法,输出如下:
开始装大象
打开卧式冰箱门
将大象躺着放进去
关上卧式冰箱门
结束
---------I'm a line-----------
开始装大象
打开立式冰箱门
将大象站着放进去
关上立式冰箱门
结束
两个子类开始和结束的步骤一样,中间步骤的顺序也一样。这些逻辑在父类中实现。每个步骤具体的逻辑则在子类中各自实现。
类图:
2. 模版方法优缺点
2.1 优点
分离了算法中变和不变的部分。不变的部分定义在父类的模版方法中。变的部分通过子类实现。不变的算法部分可以被充分复用。当变的部分有新需求时,可以定义新的子类。从而实现了开闭原则。
2.2 缺点
模版意味着死板,我们设定好模版就必须按照模版的一、二、三步来执行。如果我们想调换顺序,或者增加几步就很难做到。除非定义新的模版。或者很小心的改动已有模版,避免影响现有程序逻辑。但这已经违反了开闭原则。
3. 模版方法适用场景
如果我们发现一系列的算法,主干一样,只是在局部的实现上有区别。此时我们可以考虑使用模版方法。把算法主干及不变的部分提炼出来,在父类中实现。抽象出变化部分的方法,交由不同的子类自己去实现。
4.小结
一般来说我们都是在子类中调用父类的方法。而模版方法恰恰相反,是父类的方法中调用子类的实现。正是因为这样,我们才能把不变的行为抽象到父类中,变化的部分留给子类实现。此外还有一类方法叫做钩子方法,它不是抽象的方法,父类有其缺省实现,一般是空方法。但是子类也可以通过重写去覆盖。
- 还没有人评论,欢迎说说您的想法!