preface

Main contents of this paper:

  • Introducing appearance mode
  • The sample
    • Their tea
    • Go to the teahouse for tea
  • Summary of appearance patterns
  • Typical application of appearance patterns
    • Appearance patterns in Spring JDBC
    • Appearance mode in Mybatis
    • Appearance mode in Tomcat
    • Appearance patterns in SLF4J

The appearance model

Appearance pattern is a structural design pattern that is frequently used. It simplifies the interaction between client and subsystem by introducing an appearance role, provides a unified entrance for complex subsystem call, reduces the coupling degree between subsystem and client, and makes client call very convenient.

Appearance pattern, also known as facade pattern, is an object structure pattern. Appearance pattern is a concrete realization of Demeter’s rule. By introducing a new appearance role, the complexity of the original system can be reduced, and the coupling degree between customer class and subsystem can be reduced.

The appearance mode contains the following two roles:

Facade: its methods can be invoked on the client side, where the functions and responsibilities of the related subsystems (one or more) can be known; Under normal circumstances, it delegates all requests from the client to the appropriate subsystem object for processing.

SubSystem (SubSystem Role) : Within a software system there may be one or more SubSystem roles. Each SubSystem may not be a single class, but a collection of classes that implement the functions of the SubSystem; Each subsystem can be called directly by the client, or by the facade role, which handles requests passed by the facade class; The subsystem is unaware of the facade; to the subsystem, the facade role is just another client.

The purpose of the facade pattern is not to add a new functional interface to the subsystem, but rather to allow the external to reduce the interaction with multiple modules within the subsystem, loose coupling, and thus make it easier for the external to use the subsystem.

The essence of the facade pattern is to encapsulate interaction and simplify invocation.

The sample

You need Water to make tea

public class Water { private int temperature; // private int capacity; / / capacity of the publicWater() { this.temperature = 0; this.capacity = 10; } // omit... }Copy the code

Tea is made from tea leaves called TeaLeaf

public class TeaLeaf { private String teaName; / / to omit... }Copy the code

To boil water, you need to use a kettle to heat the water

Public class KettleService {public void waterBurning(String who, Water Water, int burnTime) { Int finalTermperature = math.min (100, water.gettemperature () + burnTime * 20); water.setTemperature(finalTermperature); System.out.println(who +"Use a kettle to boil water at the final temperature of+ finalTermperature); }}Copy the code

To make tea, the boiled water is brewed with tea leaves, and finally a cup of tea is obtained

public class TeasetService {
    public Teawater makeTeaWater(String who, Water water, TeaLeaf teaLeaf) {
        String teawater = "One cup capacity is" + water.getCapacity() + ", temperature is" + water.getTemperature() + "The" + teaLeaf.getTeaName() + "Tea";
        System.out.println(who + "Bubble" + teawater);
        returnnew Teawater(teawater); }}Copy the code

Tea drinking water

public class Man {
    private String name;
    public Man(String name) {
        this.name = name;
    }
    public void drink(Teawater teawater) {
        System.out.println(name + "Drink"+ teawater.getTeaWater()); }}Copy the code

Make your own tea

Zhang SAN and Li Si make tea and drink respectively. They need to prepare tea sets, tea leaves and water. They also need to boil water and make tea

public class Main {
    public static void main(String[] args) {
        Man zhangsan = new Man("Zhang");
        KettleService kettleService1 = new KettleService();
        TeasetService teasetService1 = new TeasetService();
        Water water1 = new Water();
        TeaLeaf teaLeaf1 = new TeaLeaf("West Lake Longjing");
        kettleService1.waterBurning(zhangsan.getName(), water1, 4);
        Teawater teawater1 = teasetService1.makeTeaWater(zhangsan.getName(), water1, teaLeaf1);
        zhangsan.drink(teawater1);
        System.out.println();

        Man lisi = new Man("Bill");
        KettleService kettleService2 = new KettleService();
        TeasetService teasetService2 = new TeasetService();
        Water water2 = new Water(10, 15);
        TeaLeaf teaLeaf2 = new TeaLeaf(biluochun); kettleService2.waterBurning(lisi.getName(), water2, 4); Teawater teawater2 = teasetService2.makeTeaWater(lisi.getName(), water2, teaLeaf2); lisi.drink(teawater2); }}Copy the code

The output is

Zhang SAN uses a kettle to boil water, and the final temperature is 80. Zhang SAN makes a cup of West Lake Longjing tea with a capacity of 10 and a temperature of 80. Finally, the water temperature was 90 and Li Si made a cup of Biluochun tea with a capacity of 15 and a temperature of 90Copy the code

Make your own tea drinking pattern diagram

Go to the teahouse for tea

Teahouse. Teahouse has different set meals

public class TeaHouseFacade {
    private String name;
    private TeasetService teasetService;
    private KettleService kettleService;

    public TeaHouseFacade(String name) {
        this.name = name;
        this.teasetService = new TeasetService();
        this.kettleService = new KettleService();
    }

    public Teawater makeTea(int teaNumber) {
        switch (teaNumber) {
            case 1:
                Water water1 = new Water();
                TeaLeaf teaLeaf1 = new TeaLeaf("West Lake Longjing");
                kettleService.waterBurning(this.name, water1, 4);
                Teawater teawater1 = teasetService.makeTeaWater(this.name, water1, teaLeaf1);
                return teawater1;
            case 2:
                Water water2 = new Water(10, 15);
                TeaLeaf teaLeaf2 = new TeaLeaf(biluochun);
                kettleService.waterBurning(this.name, water2, 4);
                Teawater teawater2 = teasetService.makeTeaWater(this.name, water2, teaLeaf2);
                return teawater2;
            default:
                Water water3 = new Water();
                TeaLeaf teaLeaf3 = new TeaLeaf("Bad name");
                kettleService.waterBurning(this.name, water3, 5);
                Teawater teawater3 = teasetService.makeTeaWater(this.name, water3, teaLeaf3);
                returnteawater3; }}}Copy the code

When Zhang SAN and Li Si order tea, they only need to tell the teahouse the set number. Water and tea are prepared by the teahouse, and the operation of boiling water and making tea is uniformly completed by the teahouse

public class Test {
    public static void main(String[] args) {
        TeaHouseFacade teaHouseFacade = new TeaHouseFacade(Lao She Teahouse);

        Man zhangsan = new Man("Zhang");
        Teawater teawater = teaHouseFacade.makeTea(1);
        zhangsan.drink(teawater);
        System.out.println();

        Man lisi = new Man("Bill"); Teawater teawater1 = teaHouseFacade.makeTea(2); lisi.drink(teawater1); }}Copy the code

The output is

Zhang SAN drank a cup of West Lake Longjing tea with a capacity of 10 and a temperature of 80. Lao She teahouse used a kettle to boil water. Li Si drank a cup of Biluochun tea with a capacity of 15 and a temperature of 90 in Lao She teahouseCopy the code

Pattern diagram of drinking tea in teahouse

Summary of appearance patterns

The main benefits of appearance mode are as follows:

  • It shields subsystem components from the client, reducing the number of objects the client needs to process, and making the subsystem easier to use. By introducing the facade pattern, the client code becomes simple and has few objects associated with it.
  • It implements loose coupling between the subsystem and the client, so that changes to the subsystem do not affect the client that calls it, and only need to adjust the appearance class.
  • Changes in one subsystem have no effect on the other subsystems, and changes within the subsystem do not affect appearance objects.

The main disadvantages of appearance mode are as follows:

  • It is not a good idea to restrict clients from directly using subsystem classes, and limiting client access to subsystem classes too much reduces variability and flexibility.
  • If not designed properly, adding a new subsystem may require modifying the source code of the appearance class, violating the open close principle.

Applicable scenarios:

  • Facade patterns are used when you want to provide a simple entry point to a complex set of subsystems.
  • There are significant dependencies between client programs and multiple subsystems. The introduction of facade classes can decouple the subsystem from the client, thus improving the independence and portability of the subsystem.
  • In the hierarchical structure, the appearance pattern can be used to define the entrance of each layer in the system. The connection between layers is not directly generated, but the connection is established through the appearance class to reduce the coupling degree between layers.

A typical application of source code analysis appearance patterns

Appearance patterns in Spring JDBC

Check the org. Springframework. JDBC. Support. JdbcUtils

public abstract class JdbcUtils {
    public static void closeConnection(Connection con) {
		if(con ! = null) { try { con.close(); } catch (SQLException ex) { logger.debug("Could not close JDBC Connection", ex);
			}
			catch (Throwable ex) {
				// We don't trust the JDBC driver: It might throw RuntimeException or Error. logger.debug("Unexpected exception on closing JDBC Connection", ex); } } } public static Object getResultSetValue(ResultSet rs, int index, Class
       requiredType) throws SQLException { if (requiredType == null) { return getResultSetValue(rs, index); } Object value = null; boolean wasNullCheck = false; // Explicitly extract typed value, as far as possible. if (String.class.equals(requiredType)) { value = rs.getString(index); } else if (boolean.class.equals(requiredType) || Boolean.class.equals(requiredType)) { value = rs.getBoolean(index); wasNullCheck = true; } else if (byte.class.equals(requiredType) || Byte.class.equals(requiredType)) { value = rs.getByte(index); wasNullCheck = true; } else if (short.class.equals(requiredType) || Short.class.equals(requiredType)) { value = rs.getShort(index); wasNullCheck = true; } else if (int.class.equals(requiredType) || Integer.class.equals(requiredType)) { value = rs.getInt(index); wasNullCheck = true; } else if (long.class.equals(requiredType) || Long.class.equals(requiredType)) { value = rs.getLong(index); wasNullCheck = true; } else if (float.class.equals(requiredType) || Float.class.equals(requiredType)) { value = rs.getFloat(index); wasNullCheck = true; } else if (double.class.equals(requiredType) || Double.class.equals(requiredType) || Number.class.equals(requiredType)) { value = rs.getDouble(index); wasNullCheck = true; } else if (byte[].class.equals(requiredType)) { value = rs.getBytes(index); } else if (java.sql.Date.class.equals(requiredType)) { value = rs.getDate(index); } else if (java.sql.Time.class.equals(requiredType)) { value = rs.getTime(index); } else if (java.sql.Timestamp.class.equals(requiredType) || java.util.Date.class.equals(requiredType)) { value = rs.getTimestamp(index); } else if (BigDecimal.class.equals(requiredType)) { value = rs.getBigDecimal(index); } else if (Blob.class.equals(requiredType)) { value = rs.getBlob(index); } else if (Clob.class.equals(requiredType)) { value = rs.getClob(index); } else { // Some unknown type desired -> rely on getObject. value = getResultSetValue(rs, index); } if (wasNullCheck && value ! = null && rs.wasNull()) { value = null; } return value; } / /... Omit... }Copy the code

This utility class mainly encapsulates native JDBC

Appearance mode in Mybatis

Check the org. Apache. Ibatis. Session. Begin with the new method in the Configuration class

public class Configuration {
	public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
	    executorType = executorType == null ? defaultExecutorType : executorType;
	    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
	    Executor executor;
	    if (ExecutorType.BATCH == executorType) {
	      executor = new BatchExecutor(this, transaction);
	    } else if (ExecutorType.REUSE == executorType) {
	      executor = new ReuseExecutor(this, transaction);
	    } else {
	      executor = new SimpleExecutor(this, transaction);
	    }
	    if (cacheEnabled) {
	      executor = new CachingExecutor(executor);
	    }
	    executor = (Executor) interceptorChain.pluginAll(executor);
	    return executor;
	}
	
	public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
	      ResultHandler resultHandler, BoundSql boundSql) {
	    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
	    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
	    returnresultSetHandler; } / /... Omit... }Copy the code

This class encapsulates operations that create objects

Appearance mode in Tomcat

Tomcat source code makes extensive use of many appearance patterns

Org. Apache. Catalina. Connector. Request and org. Apache. Catalina. The RequestFacade both class implements the interface to it

Calling getRequest() in the Request actually gets the object of the RequestFacade

protected RequestFacade facade = null;

public HttpServletRequest getRequest() {
    if (facade == null) {
        facade = new RequestFacade(this);
    }
    return facade;
}
Copy the code

The operations that are considered subsystems are wrapped in the RequestFacade

public class RequestFacade implements HttpServletRequest {
    /**
     * The wrapped request.
     */
    protected Request request = null;
    
    @Override
    public Object getAttribute(String name) {
        if (request == null) {
            throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
        }
        returnrequest.getAttribute(name); } / /... Omit... }Copy the code

Appearance patterns in SLF4J

SLF4J is a simple logging facade framework that abstracts various logging frameworks such as Logback, Log4j, Commons-Logging, and the JDK’s built-in logging implementation interfaces. It enables users to use the logging framework they want at deployment time.

SLF4J does not replace any logging framework, it is merely a facade pattern for standard logging frameworks. If there is no logging framework other than SLF4J under the classpath, the default is to output logs to the console.

The logging framework Logback is an improved version of Log4j that supports SLF4J natively (because it was developed by the same author), so the combination of Logback and SLF4J is the best choice for a logging framework and is faster than SLF4J plus other logging frameworks. And the Logback configuration can be EITHER XML or Groovy code.

SLF4J’s HelloWorld looks like this:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World"); }}Copy the code

The following figure shows the binding call relationship between SLF4J and the logging framework

The application layer calls slf4J-api.jar, and slf4J-api.jar calls different JAR packages for processing according to the logging framework it is bound to

Debug + Memory analysis Java logging framework: SLF4J functions and implementation principles

Recommended reading

Design patterns and typical application of design patterns | | simple factory pattern factory method pattern and a typical application of design patterns | the abstract factory pattern and a typical application of design pattern model and typical application design | | builders prototype model and typical applications

Visit my personal blog: laijianfeng.org