Design pattern bridging pattern and its application in JDK

Bridging mode

definition

The bridge pattern is to separate the abstract part from its implementation part, so that they can change independently. It is an object structural mode, also known as Handle and
Body mode or interfce mode.

Usage scenario

We are all familiar with the bridge. As the name suggests, it is used to connect the two banks of the river. The bridge here is used to connect two independent structures, and the two connected structures can change independently. All other understandings will be easier as long as they are based on this level.

Here are some official instructions,

  • If a system needs to add more flexibility between abstraction and concretization, and avoid establishing static inheritance relationship between the two levels, they can establish an association relationship at the abstraction level through bridging mode.

  • "Abstract part" and "implementation part" can be extended independently without affecting each other in the way of inheritance. When the program runs, the object of an abstract subclass and the object of an implementation subclass can be dynamically combined, that is, the system needs to dynamically couple the abstract role and the implementation role.

  • A class has two (or more) dimensions that change independently, and both (or more) dimensions need to be extended independently

  • The bridging mode is especially suitable for those systems that do not want to use inheritance or the number of system classes increases sharply due to multi-layer inheritance.

advantage:

  • Separate the abstract interface and its implementation. The bridge pattern uses "association relationship between objects" to decouple the inherent binding relationship between abstraction and implementation, so that abstraction and implementation can change along their respective dimensions. The so-called changes of abstraction and implementation along their respective dimensions, that is, abstraction and implementation are no longer in the same inheritance hierarchy, but "subclass" them, so that they each have their own subclasses, so that any combination subclasses can obtain multi-dimensional combination objects.
  • In many cases, the bridge mode can replace the multi-layer inheritance scheme. The multi-layer inheritance scheme violates the "single responsibility principle", has poor reusability, and has a large number of classes. The bridge mode is a better solution than the multi-layer inheritance scheme, which greatly reduces the number of subclasses.
  • The bridging mode improves the scalability of the system. If one of the two change dimensions is extended arbitrarily, there is no need to modify the original system, which conforms to the "opening and closing principle".

Disadvantages:

  • It increases the difficulty of system understanding and design
  • It is necessary to correctly identify two independently changing dimensions in the system

uml diagram

The roles involved in the bridge model are:

  • Abstract role: abstract the given definition and save a reference to the implementation object.
  • Modified abstraction role: extend the abstraction role, change and modify the definition of abstraction by the parent class.
  • Implementer role: this role gives the interface to implement the role, but does not give a specific implementation. It must be pointed out that this interface is not necessarily the same as the interface definition of the abstract role. In fact, the two interfaces can be very different. The implementation role should only give the underlying operations, while the abstract role should only give the higher-level operations based on the underlying operations.
  • Concrete implementer role: this role gives the concrete implementation of the implementation role interface.

scene

Starbucks coffee has large cup, medium cup and super large cup, with original taste, milk and sugar

Analyzing the current business scenario, it is considered that the capacity of coffee can be regarded as an abstract Abstraction, and the taste of coffee is an implementer

Step 1: create the interface definition of the implementation part (dimension of coffee taste)

public interface ICoffeeAdditives {
    void addSomething();
}

Step 2: create the interface definition of the abstract part (dimension of coffee capacity)

//Abstraction
public abstract class Coffee {
    protected ICoffeeAdditives additives;
    public Coffee(ICoffeeAdditives additives){
        this.additives=additives;
    }
    public abstract void orderCoffee(int count);
}

Step 3: implement the interface icoffeeadditiones of the realization part (coffee taste dimension)

//Add milk
public class Milk implements ICoffeeAdditives {
    @Override
    public void addSomething() {
        System.out.println("Add milk");
    }
}
//Add sugar
public class Sugar implements ICoffeeAdditives {
    @Override
    public void addSomething() {
        System.out.println("Add sugar");
    }

Step 4: implement the interface Coffee of the abstract part (Coffee capacity dimension)

public class LargeCoffee extends Coffee {
    public LargeCoffee(ICoffeeAdditives additives) {
        super(additives);
    }
    @Override
    public void orderCoffee(int count) {
        additives.addSomething();
        System.out.println(String.format("large cafe%d cup",count));
    }
}
//Small cup
public class SmallCoffee extends Coffee {
    public SmallCoffee(ICoffeeAdditives additives) {
        super(additives);
    }

    @Override
    public void orderCoffee(int count) {
        additives.addSomething();
        System.out.println(String.format("Small cup of coffee%d cup",count));
    }
}

Step 5: client call

public class test {
    public static void main(String[] args) {
        //Order two large cups of coffee with milk
        Coffee largeWithMilk=new LargeCoffee(new Milk());
        largeWithMilk.orderCoffee(2);
        SmallCoffee smallCoffee = new SmallCoffee(new Sugar());
        smallCoffee.orderCoffee(1);
    }
}

result

By using the bridging mode, the two dimensions of coffee capacity and taste can change independently without interference with each other. Originally, we needed 2x2=4 classes, but now we only need 2 + 2 = 4 classes. Some students have to say, I'll go, and those base classes are similar to the original. How good is it? Don't worry. You'll know which one is better when there's a new demand. New demand: add a taste of honey. The number of classes that do not use the bridge mode is 2x3=6, while 2 + 3 = 5 is used. Which do you think is good.

Bridging mode of jdk source code

java.util.logging is the JDK's own log package. It can output logs to files, memory or console. Its function is similar to our commonly used log4j.
The Handler class and Formatter class in the package use the bridging mode in design. First look at the class relationship diagram:


Handle and Formatter are two abstract classes, which can change independently (with different subclasses); The handle class contains a reference to the Formatter class.

public abstract class Handler {
    private static final int offValue = Level.OFF.intValue();
    private final LogManager manager = LogManager.getLogManager();
    private volatile Filter filter;
    //Reference Formatter
    private volatile Formatter formatter;
    
    private volatile Level logLevel = Level.ALL;
    private volatile ErrorManager errorManager = new ErrorManager();
    private volatile String encoding;
    //Set Formatter
    public synchronized void setFormatter(Formatter newFormatter) throws SecurityException {
        checkPermission();
        // Check for a null pointer:
        newFormatter.getClass();
        formatter = newFormatter;
    }

Handle can easily replace different Formatter classes through the setFormatter method.

Introduction to Handle and Formatter

1. Handlej and implementation classes


The handle object can take information from a logger and output it to the console, file or call other APIs to send it to the network;
handle can be closed or opened by setLevel method;
handle usually uses LogManager to set its own attribute values such as Handler, Filter and Formatter.
*Note: Although the setFormatter method is provided in the parent class Handle, the method of configuring the value of each member variable is also provided in the child class streamhandle:

private void configure() {
        LogManager manager = LogManager.getLogManager();
        String cname = getClass().getName();
        setLevel(manager.getLevelProperty(cname +".level", Level.INFO));
        setFilter(manager.getFilterProperty(cname +".filter", null));
        setFormatter(manager.getFormatterProperty(cname +".formatter", new SimpleFormatter()));
        try {
            setEncoding(manager.getStringProperty(cname +".encoding", null));
        } catch (Exception ex) {
            try {
                setEncoding(null);
            } catch (Exception ex2) {
                // doing a setEncoding with null should always work.
                // assert false;
            }
        }
    }

2. Formatter and implementation classes


Formatter supports formatting log data; Usually, there is a formatter reference in each Handler. The formatter can convert the LogRecord object into a string.

SimpleFormatter Method of
 public synchronized String format(LogRecord record) {
        dat.setTime(record.getMillis());
        String source;
        if (record.getSourceClassName() != null) {
            source = record.getSourceClassName();
            if (record.getSourceMethodName() != null) {
               source += " " + record.getSourceMethodName();
            }
        } else {
            source = record.getLoggerName();
        }
        String message = formatMessage(record);
        String throwable = "";
        if (record.getThrown() != null) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            pw.println();
            record.getThrown().printStackTrace(pw);
            pw.close();
            throwable = sw.toString();
        }
        return String.format(format,
                             dat,
                             source,
                             record.getLoggerName(),
                             record.getLevel().getLocalizedLevelName(),
                             message,
                             throwable);
    }


Tags: Java

Posted on Thu, 14 Oct 2021 16:33:09 -0400 by Web For U