目录
模板方法模式有哪些应用?
在很多时候,我们的代码中可能会有一些公共的部分并且还有一些定制的部分,那么公共这部分就可以定义在一个父类中,然后将定制的部分实现在子类中。这样子类可以根据需要扩展或重写父类的方法,而不需要改变算法的结构。
概念
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤,这种类型的设计模式属于行为型模式。
模板方法设计模式之前是怎么写代码的?
代码示例
以一个试卷为例,每个学生都需要把试卷上的题目抄一遍并且选择答案,代码如下:
public class PaperA {
// 试题一
public void question1() {
System.out.println("第一题的题目是:1+1=?");
System.out.println("第一题的答案选择" + "A");
}
// 试题二
public void question2() {
System.out.println("第二题的题目是1+2=?");
System.out.println("第二题的答案选择" + "B");
}
// 试题三
public void question3() {
System.out.println("第三题的题目是1+3=?");
System.out.println("第三题的答案选择" + "C");
}
}
public class PaperB {
// 试题一
public void question1() {
System.out.println("第一题的题目是:1+1=?");
System.out.println("第一题的答案选择" + "B");
}
// 试题二
public void question2() {
System.out.println("第二题的题目是1+2=?");
System.out.println("第二题的答案选择" + "D");
}
// 试题三
public void question3() {
System.out.println("第三题的题目是1+3=?");
System.out.println("第三题的答案选择" + "A");
}
}
public class Client {
public static void main(String[] args) {
System.out.println("学生甲的试卷-------------");
PaperA paperA = new PaperA();
paperA.question1();
paperA.question2();
paperA.question3();
System.out.println("学生乙的试卷-------------");
PaperB paperB = new PaperB();
paperB.question1();
paperB.question2();
paperB.question3();
}
}
可以感觉到学生甲和学生乙两个抄试卷类非常类似,除了答案不同,没什么不一样的,假设老师突然要改代码,如果某人抄错了,那就糟糕了。
所以我们的解决办法是老师出一根试卷,打印多份,让学生写答案就行。也就是抽象出一个父类,让两个子类继承,公共的试题代码写到父类当中就可以,下面我来写写看。
// 抽象父类
public abstract class Paper {
public void question1() {
System.out.println("第一题的题目是1+1=?");
System.out.println("第一题的答案选择" + answer1());
}
// 子类需要重写的方法
public abstract String answer1();
public void question2() {
System.out.println("第二题的题目是1+2=?");
System.out.println("第二题的答案选择" + answer2());
}
// 子类需要重写的方法
public abstract String answer2();
public void question3() {
System.out.println("第三题的题目是1+3=?");
System.out.println("第三题的答案选择" + answer3());
}
// 子类需要重写的方法
public abstract String answer3();
}
public class PaperA extends Paper {
@Override
public String answer1() {
return "A";
}
@Override
public String answer2() {
return "B";
}
@Override
public String answer3() {
return "C";
}
}
public class PaperB extends Paper {
@Override
public String answer1() {
return "B";
}
@Override
public String answer2() {
return "D";
}
@Override
public String answer3() {
return "A";
}
}
客户端程序
public class Client {
public static void main(String[] args) {
System.out.println("学生甲的试卷-------------");
PaperA paperA = new PaperA();
paperA.question1();
paperA.question2();
paperA.question3();
System.out.println("学生乙的试卷-------------");
PaperB paperB = new PaperB();
paperB.question1();
paperB.question2();
paperB.question3();
}
}
代码结构图
模板方法设计模式样例
UML类图
代码示例
// 抽象类,定义模板方法
public abstract class AbstractClass {
// 模板方法,定义了算法的主要步骤
public final void templateMethod() {
primitiveOperation1(); // 调用第一个具体步骤
primitiveOperation2(); // 调用第二个具体步骤
}
// 抽象方法,由子类实现
protected abstract void primitiveOperation1();
// 抽象方法,由子类实现
protected abstract void primitiveOperation2();
}
// 子类A,实现抽象方法
public class ClassA extends AbstractClass {
@Override
protected void primitiveOperation1() {
// 实现具体步骤A1
System.out.println("ClassA primitiveOperation1");
}
@Override
protected void primitiveOperation2() {
// 实现具体步骤A2
System.out.println("ClassA primitiveOperation2");
}
}
// 子类B,实现抽象方法
public class ClassB extends AbstractClass {
@Override
protected void primitiveOperation1() {
// 实现具体步骤B1
System.out.println("ClassB primitiveOperation1");
}
@Override
protected void primitiveOperation2() {
// 实现具体步骤B2
System.out.println("ClassB primitiveOperation2");
}
}
客户端程序
// 客户端代码
public class Client {
public static void main(String[] args) {
AbstractClass classA = new ClassA();
classA.templateMethod(); // 输出:ClassA primitiveOperation1, ClassA primitiveOperation2
AbstractClass classB = new ClassB();
classB.templateMethod(); // 输出:ClassB primitiveOperation1, ClassB primitiveOperation2
}
}
AQS是怎么应用模板方法设计模式的?
AQS是一个抽象类,它内部定义了一些公共的方法,这些公共的方法有获取锁acquire(),还有释放锁release()、等方法。也就是把这个架子搭好了,你自己用的时候需要让子类继承这个抽象类然后重写tryAcquire(),tryRelease()方法,这就是一个典型的模板方法的应用。
AQS类中 , 公共方法 | 描述 |
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } |
独占获取的同步状态 它调用了子类重写的tryAcquire方法,如果获取成功,直接返回,否则接着走其他方法进度等待队列 |
public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } |
独占式释放同步状态 释放成功后,把同步队列中第一个节点内的线程唤醒 |
acquire()具体是什么流程呢?
就是if里的方法的执行顺序:
tryAcquire()--->addWaiter(Node.EXCLUSIVE)--------------------->acquireQueued(##,arg)-------------------------->selfInterrupt()
获取锁--------->获取失败后,请求锁的线程包装成Node,放入队列--->刚包装成Node的线程,让它尝试获取锁或挂起---->中断当前线程
整合一下上面的图:
Semaphore 的重写方法
public boolean tryAcquire(int permits) {
if (permits < 0) throw new IllegalArgumentException();
return sync.nonfairTryAcquireShared(permits) >= 0;
}
模板方式设计模式的优缺点
优点:尽可能的将重复的代码提升带父类中去,子类只描述特化的那一部分代码。大大降低代码的重复度。
缺点:父类与子类之间的调用关系复杂,增加了代码阅读难度;通过继承实现,如果父类添加新的抽象方法,所有子类都需要修改。
如果我的内容对你有帮助,请辛苦动动您的手指为我点赞,评论,收藏。感谢大家。