preface
I suggest reading this article first [design mode] 7 principles of software design and 23 design modes (I)
Concept of aggregation and combination
- See this article for details. It's written in great detail Summary of relationship meaning represented by UML class diagram and various lines
I Bridging mode
Bridge Pattern, also known as Bridge Pattern, uses composition instead of inheritance to establish the relationship between classes, decouple the abstract and concrete implementation, so that they can change independently.
- Is a very simple pattern. It only uses common functions such as aggregation, inheritance and rewriting between classes, but it provides a very clear and stable architecture.
- The bridging mode is also realized through combination. The essence of decoupling is to reduce the association between objects. Inheritance is a kind of "strong association". Once inheritance is adopted, the subclass will have all the exposed methods and attributes of the parent class. Some may not be required by the subclass, but "combination" is different. Combination is a kind of "weak association", which only holds an object and the functions owned by the object are not mine, It doesn't have a strong relationship with me. Therefore, in fact, in many scenarios, we can decouple the strong association between inherited objects through composition.
- Programming principle: use more combination and less inheritance.
II Applicable scenarios of bridge mode
When there are two or more dimensional changes in a class, the bridging mode can be used to decouple these dimensions and stabilize the high-level code structure. The bridging mode is generally applicable to the following scenarios:
-
Scenarios where interfaces or abstract classes are unstable
-
When a class has two or more independently changing dimensions (such as the following), and these dimensions need to be extended independently.
Taking computers as an example, there are two dimensions of change: computer type (desktop, notebook and tablet), computer brand (Lenovo, Shenzhou, Dell and ASUS)
-
When you do not want to use inheritance, or the number of classes increases sharply due to multi-layer inheritance, you can consider using the bridge mode
PS: one of the purposes of bridging mode is to replace inheritance
III Bridging mode role
-
Implementer (implementation role): it is an interface or abstract class that defines the necessary behaviors (Methods) and properties of the role.
Define the interface that the role needs to implement. This interface does not have to be completely consistent with the interface of Abstraction. In fact, the two interfaces can be completely different. Generally speaking, the implementer interface only provides basic operations, while the interface defined by Abstraction may do more and more complex operations. The Implementor interface declares these basic operations, and the specific implementation is handed over to its subclasses. Through the association relationship, you can not only have your own methods in the Abstraction, but also call the methods defined in the implementer to use the association relationship to replace the inheritance relationship.
-
Concreteimplementer (concrete implementation role) is the concrete implementation of implementer by implementing the methods and properties defined by implementer. Different implementations can be provided in different concreteimplementers.
-
Abstraction (Abstract role): generally an abstract class. This class holds objects of implementer (implementation role) type and passes in a concreteimplementer through the construction method. It has an association (combination) relationship with the implementer. It can define both abstract business methods and its own specific business methods.
-
Refinedabstractio (extended abstract role): inherit Abstraction, pass in a concreteimplementer reference through the construction method, and expand by overriding the Abstraction method and calling the concrete business method of concreteimplementer
IV Implementation of bridge mode
Firstly, "message type" is a dimension, such as "send e-mail and send SMS". Then the message can have "emergency message, ordinary message" according to "message urgency".
Implementor (implementation role)
Message interface class
/** * Message interface class */ public interface IMessage { void send(String content,String toUser); }
Concreteimplementer (concrete implementation role)
Two implementation classes are mail message and SMS message
/** * SMS message */ public class SmsMessage implements IMessage { @Override public void send(String content, String toUser) { System.out.println(String.format("SMS news->%s: %s",toUser,content)); } }
/** * Mail message */ public class EmailMessage implements IMessage { @Override public void send(String content, String toUser) { System.out.println(String.format("Mail message->%s: %s", toUser, content)); } }
If you need to send different short messages according to ordinary messages and emergency messages, you generally modify the SmsMessage class directly and inherit the emergency message UrgentMessage class and ordinary message class CommonMsg respectively:
//Inheriting ordinary message classes to implement IMessage interface public class SmsMessage extends CommonMsg implements IMessage {}
- Java is single inheritance. Either ordinary messages and emergency messages are set as interfaces, or as a combination, emergency messages and ordinary messages are respectively used as member variables for the specific implementation of message types. However, no matter what form it is, the original SmsMessage class needs to be modified.
Therefore, it is necessary to use the bridging pattern to separate the abstraction (message type) from the implementation (message urgency).
Abstraction (Abstract role)
Create an abstract class to integrate IMessage
/** * Abstract class, holding IMessage reference */ public abstract class AbstractMessage { private IMessage iMessage; public AbstractMessage(IMessage iMessage) { this.iMessage = iMessage; } public void sendMessage(String content,String toUser){ this.iMessage.send(content,toUser); } }
Refinedabstractio (extended abstract role)
Messages with ordinary urgency
/** * Messages with ordinary urgency */ public class CommonMsg extends AbstractMessage { public CommonMsg(IMessage iMessage) { super(iMessage); } @Override public void sendMessage(String content, String toUser) { this.doSomething(); super.sendMessage(content, toUser); } private void doSomething() { System.out.println("This is just ordinary news"); } }
Message class with urgency
/** * Message class with urgency */ public class UrgentMessage extends AbstractMessage{ public UrgentMessage(IMessage iMessage) { super(iMessage); } @Override public void sendMessage(String content, String toUser) { doSomething(); super.sendMessage(content, toUser); } private void doSomething() { System.out.println("This is an urgent message. Please send it first"); } }
To add other urgency levels, just create another class directly, which is very convenient.
Calling end
public class Client { public static void main(String[] args) { //Declare message type dimension - mailbox IMessage emailMessage = new EmailMessage(); //Emergency mail message AbstractMessage abstractMessage = new UrgentMessage(emailMessage); abstractMessage.sendMessage("Hello!", "Zhang Sanfeng"); System.out.println("---------------------------"); //Ordinary mail message abstractMessage = new CommonMsg(emailMessage); abstractMessage.sendMessage("Hello!", "Guo Jing"); System.out.println("---------------------------"); //Declare message type dimension - SMS IMessage smsMessage = new SmsMessage(); //Ordinary SMS message abstractMessage = new UrgentMessage(smsMessage); abstractMessage.sendMessage("Hello!", "Zhang Sanfeng"); System.out.println("---------------------------"); //Ordinary SMS message abstractMessage = new CommonMsg(smsMessage); abstractMessage.sendMessage("Hello!", "Guo Jing"); } }
results of enforcement
V summary
1. Advantages and disadvantages of bridging mode
advantage:
- Bridging mode can replace the scheme of multi-layer inheritance. It can also greatly reduce the number of subclasses, so as to reduce the cost of maintaining and managing classes.
Multi layer inheritance violates the principle of single responsibility, has poor reusability, and has a large number of classes.
- To expand one of the two change dimensions arbitrarily, you only need to add a new class without modifying the code, which improves the expansibility and conforms to the opening and closing principle.
- Coupling is realized through combination rather than inheritance, which is in line with the principle of synthetic reuse.
Disadvantages:
- It increases the difficulty of understanding and design of the system (this is also the commonality of most design patterns)
- It is necessary to correctly identify each independently changing dimension in the system
2. Different bridging, decoration and adapter modes
- Decorator mode: mainly to add new functions to the original class without changing the original class
- Adapter mode: it is mainly used to convert the call of the client class to the target class in a compatible manner, so that the client class can call the target class in its desired way (the target class can not be directly modified to the expected appearance of the client class)
- Bridging mode: it is mainly used to split a class with multiple dimensions to reduce complexity so that each dimension can be extended and maintained independently, and then use the functions of these dimensions through aggregation.