模板方法(学习笔记)

  1. 意图

  定义一个操做中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类能够不改变一个算法的结构便可重定义该算法的某些特定步骤java

  2. 动机

   假设正在开发一款分析公司文档的数据挖掘程序。用户须要向程序输入各类格式(PDF、DOC或CSV)的文档,程序则会从这些文档中抽取出有意义的数据,并以统一的格式将其返回给用户。一段时间后,你发现这三个类包含了许多类似的代码。尽管这些类处理不一样数据格式的代码彻底不一样,可是数据处理和分析的代码却彻底同样。怎样能在保持算法结构完整的状况下去除重复代码呢?另外,还有一个与使用这些类的客户端代码相关的问题:客户端代码中包含许多条件语句,以根据不一样的处理对象类型选择合适的处理过程。若是全部处理数据的类都拥有相同的接口或基类 那么你就能够去除客户端代码中的条件语句 转而使用多态机制来在处理对象上调用函数算法

 

  模板方法模式建议将算法分解为一系列步骤,而后将这些步骤改写为方法,最后在 “模板方法” 中依次调用这些方法。步骤能够是抽象的,也能够有一些默认的实现。为了可以使用算法,客户端须要自行提供子类并实现全部的抽象步骤。若有必要还需重写一些步骤(但这一步中不包括模板方法自身)。有两种类型的步骤网络

  • 抽象步骤必须由各个子类来实现
  • 可选步骤已有一些默认实现 但仍可在须要时进行重写

  还有另外一种名为钩子的步骤钩子是内容为空的可选步骤即便不重写钩子模板方法也能工做钩子一般放置在算法重要步骤的先后为子类提供额外的算法扩展点 框架

  3. 适用性

  • 一次性的实现一个算法的不变部分,并将可变的行为留给子类来实现
  • 各子类中公共的行为应该被提取出来并集中到一个公共父类中以免代码重复。
  • 控制子类扩展。模板方法只在特定点调用钩子操做,这样只容许在这些点进行扩展

  4. 结构

   

  5. 效果

  模板方法是一种代码复用的基本技术。它们在类库中尤其重要,提取了类库中的公共行为。模板方法致使了一种反向的控制结构,这种结构有时被称为好莱坞法则,即一个父类调用一个子类的操做,而不是相反函数

  模板方法调用下列类型的操做:post

  1. 具体操做(ConcreteClass或对客户类的操做)this

  2. 具体的AbstractClass的操做spa

  3. 原语操做(AbstractClass中定义抽象的原语操做,具体的子类将重定义它们以实现一个算法的各步骤).net

  4. 工厂方法3d

  5. 钩子操做,它提供了缺省行为,子类能够在必要时进行扩展。钩子操做在一般状况下是空操做  

  6. 代码实现  

  本例中模版方法模式定义了一个可与社交网络协做的算法与特定社交网络相匹配的子类将根据社交网络所提供的API来实现这些步骤

  networks/Network.java: 基础社交网络类

package template_method.networks;

/**
 * @author GaoMing
 * @date 2021/7/25 - 21:47
 * Base class of social network.
 */
public abstract class Network {
    String userName;
    String password;

    Network() {}

    /**
     * Publish the data to whatever network.
     */
    public boolean post(String message) {
        // Authenticate before posting. Every network uses a different
        // authentication method.
        if (logIn(this.userName, this.password)) {
            // Send the post data.
            boolean result =  sendData(message.getBytes());
            logOut();
            return result;
        }
        return false;
    }

    abstract boolean logIn(String userName, String password);
    abstract boolean sendData(byte[] data);
    abstract void logOut();
}

  networks/Facebook.java: 具体社交网络

package template_method.networks;

/**
 * @author GaoMing
 * @date 2021/7/25 - 21:47
 */
public class Facebook extends Network {
    public Facebook(String userName, String password) {
        this.userName = userName;
        this.password = password;
    }

    public boolean logIn(String userName, String password) {
        System.out.println("\nChecking user's parameters");
        System.out.println("Name: " + this.userName);
        System.out.print("Password: ");
        for (int i = 0; i < this.password.length(); i++) {
            System.out.print("*");
        }
        simulateNetworkLatency();
        System.out.println("\n\nLogIn success on Facebook");
        return true;
    }

    public boolean sendData(byte[] data) {
        boolean messagePosted = true;
        if (messagePosted) {
            System.out.println("Message: '" + new String(data) + "' was posted on Facebook");
            return true;
        } else {
            return false;
        }
    }

    public void logOut() {
        System.out.println("User: '" + userName + "' was logged out from Facebook");
    }

    private void simulateNetworkLatency() {
        try {
            int i = 0;
            System.out.println();
            while (i < 10) {
                System.out.print(".");
                Thread.sleep(500);
                i++;
            }
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

  networks/Twitter.java: 另外一个社交网络

package template_method.networks;

/**
 * @author GaoMing
 * @date 2021/7/25 - 21:47
 */
public class Twitter extends Network{
    public Twitter(String userName, String password) {
        this.userName = userName;
        this.password = password;
    }

    public boolean logIn(String userName, String password) {
        System.out.println("\nChecking user's parameters");
        System.out.println("Name: " + this.userName);
        System.out.print("Password: ");
        for (int i = 0; i < this.password.length(); i++) {
            System.out.print("*");
        }
        simulateNetworkLatency();
        System.out.println("\n\nLogIn success on Twitter");
        return true;
    }

    public boolean sendData(byte[] data) {
        boolean messagePosted = true;
        if (messagePosted) {
            System.out.println("Message: '" + new String(data) + "' was posted on Twitter");
            return true;
        } else {
            return false;
        }
    }

    public void logOut() {
        System.out.println("User: '" + userName + "' was logged out from Twitter");
    }

    private void simulateNetworkLatency() {
        try {
            int i = 0;
            System.out.println();
            while (i < 10) {
                System.out.print(".");
                Thread.sleep(500);
                i++;
            }
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

  Demo.java: 客户端代码

package template_method;

import template_method.networks.Network;
import template_method.networks.Twitter;
import template_method.networks.Facebook;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author GaoMing
 * @date 2021/7/25 - 21:47
 */
public class Demo {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        Network network = null;
        System.out.print("Input user name: ");
        String userName = reader.readLine();
        System.out.print("Input password: ");
        String password = reader.readLine();

        // Enter the message.
        System.out.print("Input message: ");
        String message = reader.readLine();

        System.out.println("\nChoose social network for posting message.\n" +
                "1 - Facebook\n" +
                "2 - Twitter");
        int choice = Integer.parseInt(reader.readLine());

        // Create proper network object and send the message.
        if (choice == 1) {
            network = new Facebook(userName, password);
        } else if (choice == 2) {
            network = new Twitter(userName, password);
        }
        network.post(message);
    }
}

  运行结果    

Input user name: Jhonatan
Input password: qswe
Input message: Hello, World!

Choose social network for posting message.
1 - Facebook
2 - Twitter
2

Checking user's parameters
Name: Jhonatan
Password: ****
..........

LogIn success on Twitter
Message: 'Hello, World!' was posted on Twitter
User: 'Jhonatan' was logged out from Twitter

 

  7. 与其余模式的关系

  • 工厂方法模式是模板方法模式的一种特殊形式。同时,工厂方法能够做为一个大型模板方法中的一个步骤
  • 模板方法基于继承机制:它容许你经过扩展子类中的部份内容来改变部分算法。策略模式基于组合机制:能够经过对相应行为提供不一样的策略来改变对象的部分行为。模板方法在类层次上运做,所以它是静态的。策略在对象层次上运做,所以容许在运行时切换行为

  8. 已知应用  

  使用示例:模版方法模式在Java框架中很常见。开发者一般使用它来向框架用户提供经过继承实现的、对标准功能进行扩展的简单方式
  这里是一些核心 Java 程序库中模版方法的示例:
  java.io.InputStream、java.io.OutputStream、java.io.Reader和java.io.Writer的全部非抽象方法
  java.util.AbstractList、java.util.AbstractSet和java.util.AbstractMap的全部非抽象方法
  javax.servlet.http.HttpServlet,全部默认发送HTTP 405 “方法不容许” 错误响应的do­XXX()方法。你可随时对其进行重写

  识别方法 模版方法能够经过行为方法来识别 该方法已有一个在基类中定义的 “默认 行为

相关文章
相关标签/搜索