This is the third day of my participation in Gwen Challenge

Bridge pattern is also one of the more common design patterns of 23 patterns, in the classification of creation pattern, structural pattern and behavior pattern, bridge pattern is classified as creation pattern. At the heart of the whole bridging pattern is the separation of abstractions and implementations so that they can vary independently. It is realized by using combinatorial relation instead of inheritance relation, thus reducing the coupling degree of abstraction and realization of these two variable dimensions.

Application scenarios

A common use of the bridge pattern is substitution inheritance. As we know, inheritance has many advantages, such as abstraction, encapsulation, polymorphism, etc., the parent class encapsulates the common, and the child class implements the characteristics. Inheritance is a great way to reuse code (encapsulation), but it is also a major disadvantage of inheritance. Because the parent class owns methods, the subclass inherits them, whether the subclass needs them or not, this means that inheritance is intrusive (the parent code invents the subclass) and can cause subclass bloat. Therefore, in design patterns, it is a principle to use composition/aggregation in preference to inheritance.

In real life we may see the word bridge in router configuration called routing bridge. In fact, the concept is similar. Router bridging is actually two routers connecting one router to the other router. Both routers can provide wifi signals externally. Terminals (mobile phones, computers) and subclasses can use the wifi signals from these two routers. Bridging in code is two abstract interfaces, one of which is connected to the other, both of which have corresponding implementation classes, so that you can program cartesian product combinations to increase utility and extensibility.

I think this design pattern should be considered if cartesian product situations occur in programming

Code sample

Let’s list an actual scene to realize with the code, and actually feel the actual application of the scene. When we buy milk tea, we can choose to have taste, and the capacity is the large cup and the small cup. For example, there are hot milk tea and iced milk tea, corresponding to large cups and small cups. So you have the cartesian product.

  • A large cup of hot milk tea
  • Small cups of hot milk tea
  • A large cup of cold milk tea
  • Small cups of cold milk tea

So let’s do it if-else first.

public class MilkTeaController {
    private Logger logger = LoggerFactory.getLogger(MilkTeaController.class);

    /** * Have a cup of milk tea **@paramType 1 hot 2 cold *@paramModeType 1 large cup 2 small cup *@return* /
    public boolean getMilkTea(int type, int modeType) {
        if (1 == type) {
            / / hot milk tea
            if (1 == modeType) {
                logger.info("Have a hot, large cup of milk tea.");
            } else if (2 == modeType) {
                logger.info("Have a hot little cup of milk tea."); }}if (2 == type) {
            / / cool tea with milk
            if (1 == modeType) {
                logger.info("Have a cold, large cup of milk tea.");
            } else if (2 == modeType) {
                logger.info("A small, cold cup of milk tea."); }}return true; }}Copy the code

There are many examples of code written this way. It is absolutely fine to write normal business like this, but if you want to extend it you will write more if-else. And then there was no way to maintain it. Some people argue that we’re interface oriented and we can abstract at one level and not have so many if-else’s. Write like this:

  1. Abstract a milk tea class
public interface IMilkTea {
    // Order milk tea
    void orderMilkTea(int modeType);
}
Copy the code
  1. Hot milk tea
public class HotMilkTea implements IMilkTea {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void orderMilkTea(int modeType) {
        if (1 == modeType) {
            logger.info("Have a hot, large cup of milk tea.");
        } else if (2 == modeType) {
            logger.info("Have a hot little cup of milk tea."); }}}Copy the code
  1. Cool milk tea
public class ColdMilkTea implements IMilkTea {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void orderMilkTea(int modeType) {
        if (1 == modeType) {
            logger.info("Have a cold, large cup of milk tea.");
        } else if (2 == modeType) {
            logger.info("A small, cold cup of milk tea."); }}}Copy the code

Writing it this way, I can only say: HMM, there’s something inside. But the transformation is not complete. Let’s modify the bridge mode to see what the convenience is:

  1. Let’s do our first abstraction, abstracting the concept of hot or cold, as follows:
public abstract class HotOrCold {

    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    public BigOrSmall bigOrSmall;

    public HotOrCold(BigOrSmall bigOrSmall) {
        this.bigOrSmall = bigOrSmall;
    }
    // Order making
    public abstract void OperationOrder(a);

}
Copy the code

The above abstract class involves an interface object. And this is the heart of the whole bridge, the idea of a bridge.

  1. Let’s move on to the second abstraction. To abstract the size of the cup, code:
public interface BigOrSmall {
    int selectMode(a);
}
Copy the code
  1. Next we implement the concrete abstraction
public class ColdMilkTea2 extends HotOrCold {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    public ColdMilkTea2(BigOrSmall bigOrSmall) {
        super(bigOrSmall);
    }

    @Override
    public void OperationOrder(a) {
        System.out.print("Cool milk tea -"); bigOrSmall.selectMode(); }}Copy the code
public class HotMilkTea2 extends HotOrCold {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    public HotMilkTea2(BigOrSmall bigOrSmall) {
        super(bigOrSmall);
    }

    @Override
    public void OperationOrder(a) {
        System.out.print("Hot milk tea -"); bigOrSmall.selectMode(); }}Copy the code
public class BigCup implements BigOrSmall {
    @Override
    public int selectMode(a) {
        System.out.println("Mug");
        return 1; }}Copy the code
public class SmallCup implements BigOrSmall {
    @Override
    public int selectMode(a) {
        System.out.println("Small");
        return 2; }}Copy the code

Ok, now that we’ve defined it, let’s test it with a client:

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

        HotOrCold milkOrder = new HotMilkTea2(new BigCup());
        milkOrder.OperationOrder();
        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
        HotOrCold milkOrder2 = new HotMilkTea2(new SmallCup());
        milkOrder2.OperationOrder();
        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
        HotOrCold milkOrder3 = new ColdMilkTea2(new BigCup());
        milkOrder3.OperationOrder();
        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
        HotOrCold milkOrder4 = new ColdMilkTea2(newSmallCup()); milkOrder4.OperationOrder(); }}Copy the code

The output

Hot milk tea – Large hot milk tea – Small cold milk tea – Large cold milk tea – Small

In this way, if medium cup milk tea is introduced in the future, there is no need to change the original logic and only need to add a class:

public class MediumCup implements BigOrSmall {
    @Override
    public int selectMode(a) {
        System.out.println("The cup");
        return 3; }}Copy the code

You can meet your needs very well.

Role in mode

Using our example above, we summarize the composition of the roles in the bridge mode

role meaning
Abstraction of characters Defines an abstract class that contains a reference to the implementation object
Refined Abstraction character Is a subclass of an abstract role that implements the business methods in the parent class and implements the business methods in the role through combinatorial calls
Implementor the role Defines interfaces that implement roles for extended abstract role invocation
Concrete Implementor role The concrete implementation of the role interface is given

Advantages and disadvantages of bridge mode

advantages
  • Decouple the inherent binding between abstraction and implementation so that abstraction and implementation can vary along their respective dimensions.
disadvantages
  • The introduction of bridge pattern will increase the difficulty of system understanding and design, because the aggregation association relationship is established in the abstraction layer, requiring developers to design and program for abstraction.

Application of bridge pattern in framework

The Driver class in the JDBC connection database that we often use uses a distorted bridge pattern. Let’s start with the Driver interface class:

This is an interface, the following specific implementation can have mysql driver or SQLSever driver. Mysql driver = driver;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver(a) throws SQLException {}static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!"); }}}Copy the code

The registerDriver method in DriverManager is called to register the driver. When the driver registration is complete, we will start calling the getConnection method in DriverManager. Let’s look at the geConnection method:

You can see that the Connection object needs to be returned. In Java, the same operation interface is provided to each database through Connection, which can be thought of as an abstract class. We use the same method to manipulate different databases, but MySQL has its own ConnectionImpl class, and Oracle has its own implementation class. Here drivers and connections are bridged using the DriverManager class, rather than using composition relationships as we mentioned above.