设计模式:行为型-模板模式
目录
- 第一章 模板模式介绍
- 第二章 模板模式实现
- 2.1、抽象父类
- 2.2、具体子类
- 2.3、测试类
- 第三章 模板模式应用
项目地址:https://gitee.com/caochenlei/design-pattern
第一章 模板模式介绍
模板模式的介绍:
在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。
例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。
这样的例子在生活中还有很多,例如,一个人每天会起床、吃饭、做事、睡觉等,其中“做事”的内容每天可能不同。我们把这些规定了流程或格式的实例定义成模板,允许使用者根据自己的需求去更新它,例如,简历模板、论文模板、Word 中模板文件等。
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板,它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。
模板模式的优点:
- 它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
- 它在父类中提取了公共的部分代码,便于代码复用。
- 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
模板模式的缺点:
- 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统的复杂度。
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
- 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。
模板模式的场景:
- 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。
- 当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
- 当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展。
模板模式的角色:
抽象父类/抽象模板(Abstract Class): 抽象模板类,负责给出一个算法的轮廓和骨架,它由一个模板方法和若干个基本方法构成。
- 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
基本方法:是整个算法中的一个步骤,包含以下几种类型。
- 抽象方法:在抽象类中给出声明,由具体子类实现。
- 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
- 钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。
具体子类/具体实现(Concrete Class): 具体实现类,实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。
第二章 模板模式实现
2.1、抽象父类
Game
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
//模板
public final void play() {
//初始化游戏
initialize();
//开始游戏
startPlay();
//结束游戏
endPlay();
}
}
2.2、具体子类
Football
public class Football extends Game {
@Override
void initialize() {
System.out.println("Football Game Initialized.");
}
@Override
void startPlay() {
System.out.println("Football Game Started.");
}
@Override
void endPlay() {
System.out.println("Football Game Finished.");
}
}
Basketball
public class Basketball extends Game {
@Override
void initialize() {
System.out.println("Basketball Game Initialized.");
}
@Override
void startPlay() {
System.out.println("Basketball Game Started.");
}
@Override
void endPlay() {
System.out.println("Basketball Game Finished.");
}
}
2.3、测试类
Client
public class Client {
public static void main(String[] args) {
Game g1 = new Football();
g1.play();
Game g2 = new Basketball();
g2.play();
}
}
Football Game Initialized.
Football Game Started.
Football Game Finished.
Basketball Game Initialized.
Basketball Game Started.
Basketball Game Finished.
第三章 模板模式应用
在 Servlet 中,每一个 Servlet 类都必须要实现 HttpServlet 接口。GenericServlet 是个通用的、不特定于任何协议的 Servlet,它实现了 Servlet 接口。而 HttpServlet 继承于 GenericServlet,为 Servlet 接口提供了处理 HTTP 协议的通用实现,所以我们定义 Servlet 时只需要继承 HttpServlet 即可。
public abstract class HttpServlet extends GenericServlet {
//以上代码部分省略...
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
//省略...
}
protected void doHead(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
//省略...
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
//省略...
}
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
//省略...
}
protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
//省略...
}
protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
//省略...
}
protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
//省略...
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
//以下部分代码省略...
}
在 HttpServlet 的 service 方法中,首先获取请求的方法名,然后根据方法名调用对应的 doXXX 方法,比如说请求方法为 GET,那么就去调用 doGet 方法;请求方法为 POST,那么就去调用 doPost 方法。
这里,HttpServlet 就相当于定义了一套处理 HTTP 请求的模板。service 方法为模板方法,定义了处理 HTTP 请求的基本流程;doXXX 等方法为基本方法,根据请求方法做相应的处理,子类可重写这些方法。HttpServletRequest 中的 Method 则起到钩子方法的作用。
比如实现一个输出 Hello World 的简单 Servlet,代码如下:
public class HelloWorld extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<h1>Hello World!</h1>");
}
}
还没有评论,来说两句吧...