The of Java design pattern -- bridging pattern

1. What is bridging mode?

Decouple an abstraction from its implementation so that the two can vary independently.

Bridge Pattern: decouple abstraction and implementation so that they can change independently.

Another explanation is that there are two (or more) independently changing dimensions in a class. We can make these two (or more) dimensions expand independently by combining.

It may sound profound. It doesn't matter. Let's explain it through examples.

2. Bridge mode definition

①,Abstraction

Abstract role: its main responsibility is to define the behavior of the role and save a reference to the implementation role. The role is generally an abstract class.

②,Implementor

Implementation role: it is an interface or abstract class that defines the necessary behaviors and properties of a role.

③,RefinedAbstraction

Fix Abstract role: it refers to the implementation role to modify the abstract role.

④,ConcreteImplementor

Materialized role: it implements methods and properties defined by an interface or abstract class.

3. General code implementation of bridge mode

Implementation class:

public interface Implementor {

    void doSomething();
}

Implementation class:

public class ConcreteImplementor1 implements Implementor{
    @Override
    public void doSomething() {
        // Specific business logic processing
    }
}
public class ConcreteImplementor2 implements Implementor{
    @Override
    public void doSomething() {
        // Specific business logic
    }
}

Two are defined here, and there may be multiple.

Abstract role:

public abstract class Abstraction {
    // Defines a reference to an implementation role
    private Implementor implementor;

    public Abstraction(Implementor implementor){
        this.implementor = implementor;
    }

    // Own behavior and attributes
    public void request(){
        this.implementor.doSomething();
    }

    // Get implemented roles
    public Implementor getImplementor(){
        return implementor;
    }
}

Fixed Abstract role:

public class RefinedAbstraction extends  Abstraction{
    // Override constructor
    public RefinedAbstraction(Implementor implementor){
        super(implementor);
    }

    // Fixed the behavior of the parent class
    @Override
    public void request() {
        super.request();
    }
}

Test:

public class BridgeClient {
    public static void main(String[] args) {
        // Define an implementation role
        Implementor implementor = new ConcreteImplementor1();
        // Define an abstract role
        Abstraction abstraction = new RefinedAbstraction(implementor);
        // Execution method
        abstraction.request();

    }
}

If our implementation role has many sub interfaces, then there are a lot of sub implementations. Pass a specific implementer in the constructor, and the code is also very clear.

4. Classic example of bridge mode - JDBC

When we first used JDBC to directly connect to the database, we would have this Code:

Class.forName("com.mysql.cj.jdbc.Driver");//Loading and registering JDBC drivers
String url = "jdbc:mysql://localhost:3306/sample_db?user=root&password=your_password";
Connection con = DriverManager.getConnection(url);
Statement stmt = con.createStatement();
String query = "select * from test";
ResultSet rs=stmt.executeQuery(query);
while(rs.next()) {
  rs.getString(1);
  rs.getInt(2);
}

If we want to replace MySQL database with Oracle database, we only need to replace com.mysql.cj.jdbc.Driver in the first line of code with oracle.jdbc.driver.OracleDriver.

This elegant way to achieve database switching is to use the bridge mode.

Let's first look at the Driver class:

package com.mysql.cj.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

This code Class.forName("com.mysql.cj.jdbc.Driver") has two functions:

① , ask the JVM to find and load the specified Driver class.

② . execute the static code of this class, that is, register the MySQL Driver in the DriverManager class.

Next, let's look at the DriverManager class:

public class DriverManager {
  private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();

  //...
  static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
  }
  //...

  public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException {
    if (driver != null) {
      registeredDrivers.addIfAbsent(new DriverInfo(driver));
    } else {
      throw new NullPointerException();
    }
  }

  public static Connection getConnection(String url, String user, String password) throws SQLException {
    java.util.Properties info = new java.util.Properties();
    if (user != null) {
      info.put("user", user);
    }
    if (password != null) {
      info.put("password", password);
    }
    return (getConnection(url, info, Reflection.getCallerClass()));
  }
  //...
}

After registering the specific driver implementation class (for example, com.mysql.cj.jdbc.Driver) with the DriverManager, all subsequent calls to the JDBC interface will be delegated to the specific driver implementation class for execution. The driver implementation classes all implement the same interface (java.sql.Driver), which is also the reason why the driver can be switched flexibly.

5. Advantages of bridging mode

① Separation of, abstraction and Implementation

This is also the main feature of bridge mode. It is a design mode proposed to solve the shortcomings of inheritance. In this mode, the implementation can be free from abstract constraints and no longer bound to a fixed abstraction level.

② Excellent expansion ability

Take a look at our example. Would you like to add an implementation? no problem! If you want to add abstraction, there is no problem! As long as the exposed interface layer allows such changes, we have minimized the possibility of changes.

③ . make details transparent to customers

Customers don't care about the implementation of details. It has been encapsulated by the abstraction layer through aggregation relations.

6. Bridging mode application scenario

① If a system needs to add more flexibility between the abstract and concrete roles of components, avoid establishing static inheritance relations between the two levels, and enable them to establish an association relationship at the abstract level through bridging mode.

② For those systems that do not want to use inheritance or the number of system classes increases sharply due to multi-level inheritance, the bridging mode is particularly suitable.

③ . a class has two independently changing dimensions, and both dimensions need to be extended.

Tags: Design Pattern

Posted on Tue, 23 Nov 2021 06:29:28 -0500 by jazzydog