Template Method概述

Template Method - 图1

UML概述

Template Method - 图2

玩具代码案例 - 基于模板模式的登录服务

模板主体

模板模式的主体提供了稳定的业务流程,不稳定的逻辑由子类来实现

  1. package online.javabook.gof.behavioral.patterns9.template2.login.service;
  2. public abstract class LoginService {
  3. // template method
  4. public void login(String userName, String password) {
  5. boolean isOK = checkAccount(userName, password);
  6. if(isOK) {
  7. handlerLogin(userName);
  8. }else{
  9. handlerError(userName);
  10. }
  11. }
  12. protected abstract boolean checkAccount(String userName, String password);
  13. protected abstract void handlerLogin(String userName);
  14. protected abstract void handlerError(String userName);
  15. }

模板实现

FacebookLoginService

package online.javabook.gof.behavioral.patterns9.template2.login.template;

import online.javabook.gof.behavioral.patterns9.template2.login.service.LoginService;

public class FacebookLoginService extends LoginService {
    @Override
    protected boolean checkAccount(String userName, String password) {
        System.out.println("Check Facebook account");
        return true;
    }

    @Override
    protected void handlerLogin(String userName) {
        System.out.println("send a Facebook login message");
    }

    @Override
    protected void handlerError(String userName) {
        System.out.println("send a Facebook error message");
    }
}

GoogleLoginService

package online.javabook.gof.behavioral.patterns9.template2.login.template;

import online.javabook.gof.behavioral.patterns9.template2.login.service.LoginService;

public class GoogleLoginService extends LoginService {
    @Override
    protected boolean checkAccount(String userName, String password) {
        System.out.println("Check Google account");
        return true;
    }

    @Override
    protected void handlerLogin(String userName) {
        System.out.println("send a Google login message");
    }

    @Override
    protected void handlerError(String userName) {
        System.out.println("send a Google error message");
    }
}

YouTubeLoginService

package online.javabook.gof.behavioral.patterns9.template2.login.template;

import online.javabook.gof.behavioral.patterns9.template2.login.service.LoginService;

public class YouTubeLoginService extends LoginService {
    @Override
    protected boolean checkAccount(String userName, String password) {
        System.out.println("Check YouTube account");
        return true;
    }

    @Override
    protected void handlerLogin(String userName) {
        System.out.println("send a youtube login message");
    }

    @Override
    protected void handlerError(String userName) {
        System.out.println("send a youtube error message");
    }
}

不基于模板模式的实现

Main

package online.javabook.gof.behavioral.patterns9.template2.login.app.bad;

public class Main {
    public static void main(String[] args) {

        String social = "google";
        String userName = "xxx";
        String password = "yyy";

        switch (social) {
            case "google":{
                boolean isOK = checkGoogleAccount(userName, password);
                if(isOK) {
                    handlerGoogleLogin(userName);
                }else{
                    handlerGoogleError(userName);
                }
                break;
            }
            case "facebook":{
                boolean isOK = checkFacebookAccount(userName, password);
                if(isOK) {
                    handlerFacebookLogin(userName);
                }else{
                    handlerFacebookError(userName);
                }
                break;
            }
            case "youtube":{
                boolean isOK = checkYoutubeAccount(userName, password);
                if(isOK) {
                    handlerYoutubeLogin(userName);
                }else{
                    handlerYoutubeError(userName);
                }
                break;
            }
            default:
                throw new IllegalStateException("Unexpected value: " + social);
        }
    }

    // -----------------------------------------------------

    protected static boolean checkFacebookAccount(String userName, String password) {
        System.out.println("Check Facebook account");
        return true;
    }

    protected static void handlerFacebookLogin(String userName) {
        System.out.println("send a Facebook login message");
    }

    protected static void handlerFacebookError(String userName) {
        System.out.println("send a Facebook error message");
    }

    // -----------------------------------------------------

    protected static boolean checkGoogleAccount(String userName, String password) {
        System.out.println("Check Google account");
        return true;
    }

    protected static void handlerGoogleLogin(String userName) {
        System.out.println("send a Google login message");
    }

    protected static void handlerGoogleError(String userName) {
        System.out.println("send a Google error message");
    }
    // -----------------------------------------------------

    protected static boolean checkYoutubeAccount(String userName, String password) {
        System.out.println("Check YouTube account");
        return true;
    }

    protected static void handlerYoutubeLogin(String userName) {
        System.out.println("send a youtube login message");
    }

    protected static void handlerYoutubeError(String userName) {
        System.out.println("send a youtube error message");
    }
}

Console

Check Google account
send a Google login message

基于模板模式的实现

Main

package online.javabook.gof.behavioral.patterns9.template2.login.app.good;

import online.javabook.gof.behavioral.patterns9.template2.login.service.LoginService;
import online.javabook.gof.behavioral.patterns9.template2.login.template.FacebookLoginService;
import online.javabook.gof.behavioral.patterns9.template2.login.template.GoogleLoginService;
import online.javabook.gof.behavioral.patterns9.template2.login.template.YouTubeLoginService;

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {

        String loginType = "google";
        Map<String, LoginService> loginServiceRegister = new HashMap<>();
        loginServiceRegister.put("google", new GoogleLoginService());
        loginServiceRegister.put("facebook", new FacebookLoginService());
        loginServiceRegister.put("youtube", new YouTubeLoginService());

        LoginService loginService = loginServiceRegister.get(loginType);
        loginService.login("xxx", "yyy");
    }
}

Console

Check Google account
send a Google login message

显示世界中的模板模式

Hadoop中的Mapper和Reducer类

mapper和reducer中的run方法定义好了程序的主体处理逻辑,中间的每一步要做什么和怎么做,比如setup,map/reduce, cleanup的调用和循环处理。 但是具体setup,map/reduce, cleanup怎么做就交给了子类来实现。就好像定义了一个代码表格,只要无脑的填写这个代码表格中的逻辑就可以了,具体底层做了什么不用关心。

package org.apache.hadoop.mapreduce;

import java.io.IOException;

import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.RawComparator;
import org.apache.hadoop.io.compress.CompressionCodec;
import org.apache.hadoop.mapreduce.task.MapContextImpl;

@InterfaceAudience.Public
@InterfaceStability.Stable
public class Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> {

  /**
   * The <code>Context</code> passed on to the {@link Mapper} implementations.
   */
  public abstract class Context
    implements MapContext<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {
  }

  /**
   * Called once at the beginning of the task.
   */
  protected void setup(Context context
                       ) throws IOException, InterruptedException {
    // NOTHING
  }

  /**
   * Called once for each key/value pair in the input split. Most applications
   * should override this, but the default is the identity function.
   */
  @SuppressWarnings("unchecked")
  protected void map(KEYIN key, VALUEIN value, 
                     Context context) throws IOException, InterruptedException {
    context.write((KEYOUT) key, (VALUEOUT) value);
  }

  /**
   * Called once at the end of the task.
   */
  protected void cleanup(Context context
                         ) throws IOException, InterruptedException {
    // NOTHING
  }

  /**
   * Expert users can override this method for more complete control over the
   * execution of the Mapper.
   * @param context
   * @throws IOException
   */
  public void run(Context context) throws IOException, InterruptedException {
    setup(context);
    try {
      while (context.nextKeyValue()) {
        map(context.getCurrentKey(), context.getCurrentValue(), context);
      }
    } finally {
      cleanup(context);
    }
  }
}
package org.apache.hadoop.mapreduce;

import java.io.IOException;

import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.mapreduce.task.annotation.Checkpointable;

import java.util.Iterator;

@Checkpointable
@InterfaceAudience.Public
@InterfaceStability.Stable
public class Reducer<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {

  /**
   * The <code>Context</code> passed on to the {@link Reducer} implementations.
   */
  public abstract class Context 
    implements ReduceContext<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {
  }

  /**
   * Called once at the start of the task.
   */
  protected void setup(Context context
                       ) throws IOException, InterruptedException {
    // NOTHING
  }

  /**
   * This method is called once for each key. Most applications will define
   * their reduce class by overriding this method. The default implementation
   * is an identity function.
   */
  @SuppressWarnings("unchecked")
  protected void reduce(KEYIN key, Iterable<VALUEIN> values, Context context
                        ) throws IOException, InterruptedException {
    for(VALUEIN value: values) {
      context.write((KEYOUT) key, (VALUEOUT) value);
    }
  }

  /**
   * Called once at the end of the task.
   */
  protected void cleanup(Context context
                         ) throws IOException, InterruptedException {
    // NOTHING
  }

  /**
   * Advanced application writers can use the 
   * {@link #run(org.apache.hadoop.mapreduce.Reducer.Context)} method to
   * control how the reduce task works.
   */
  public void run(Context context) throws IOException, InterruptedException {
    setup(context);
    try {
      while (context.nextKey()) {
        reduce(context.getCurrentKey(), context.getValues(), context);
        // If a back up store is used, reset it
        Iterator<VALUEIN> iter = context.getValues().iterator();
        if(iter instanceof ReduceContext.ValueIterator) {
          ((ReduceContext.ValueIterator<VALUEIN>)iter).resetBackupStore();        
        }
      }
    } finally {
      cleanup(context);
    }
  }
}