Bridge definition: separates abstraction and behavior, and combines each independently but dynamically.
Why use bridge mode
Generally, when an abstract class or interface has multiple concrete implementations (concrete subclass), the relationship between these concretes may be as follows:
1. These specific implementations happen to be parallel. As mentioned above, there are two concrete classes: square piles and circular piles; the piles in these two shapes are parallel, without conceptual repetition. Then we just need to use inheritance.
2. In practical application, it is often possible to overlap conceptually among these multiple concrete classes. Then we need to separate the common abstract and the common behavior. We originally planned to put it in one interface, but now we need to design two interfaces to place abstract and behavior respectively.
For example, as an example, there are two types of coffee: medium and large cups, and there are also different types of milk and milk. If simple inheritance is used, there is a concept overlap between these four specific implementations (million cup with milk without milk) because there are medium cups with milk, and there are medium cups without milk. If you are in the middle cup layer again, it is also in the middle cup. Implementing two more inheritances is obviously chaotic and has extremely poor scalability. Then we use Bridge mode to implement it.
How to implement bridge mode
The coffee mentioned above is an example. We originally planned to design only one interface (abstract class). After using the Bridge pattern, we need to separate the abstraction and behavior. Adding milk and not adding milk belong to behaviors. We abstract them into a special behavior interface.
Let's take a look at the interface code of the abstract part:
The code copy is as follows:
public abstract class Coffee{
CoffeeImp coffeeImp;
public void setCoffeeImp() {
this.CoffeeImp = CoffeeImpSingleton.getTheCoffeImp();
}
public CoffeeImp getCoffeeImp() {return this.CoffeeImp;}
public abstract void pourCoffee();
}
CoffeeImp is a behavioral interface with no milk added. See its code as follows:
The code copy is as follows:
public abstract class CoffeeImp{
public abstract void pourCoffeeImp();
}
Now we have two abstract classes, we inherit them respectively and implement concrete class:
The code copy is as follows:
//Middle Cup
public class MediumCoffee extends Coffee{
public MediumCoffee() {setCoffeeImp();}
public void pourCoffee(){
CoffeeImp coffeeImp = this.getCoffeeImp();
//We use the number of repetitions to explain whether to make a mid-cup or a large cup. Repeat twice is a mid-cup for (int i = 0; i < 2; i++){
coffeeImp.pourCoffeeImp();
}
}
}
The code copy is as follows:
//Big cup
public class SuperSizeCoffee extends Coffee{
public SuperSizeCoffee() {setCoffeeImp();}
public void pourCoffee(){
CoffeeImp coffeeImp = this.getCoffeeImp();
//We use the number of repetitions to explain whether to make a mid-cup or a large cup. Repeat 5 times to be a large cup for (int i = 0; i < 5; i++){
coffeeImp.pourCoffeeImp();
}
}
}
The above are the specific implementations of the medium cup and the large cup respectively. The following inherits the behavior CoffeeImp:
The code copy is as follows:
//Add milk
public class MilkCoffeeImp extends CoffeeImp{
MilkCoffeeImp() {}
public void pourCoffeeImp(){
System.out.println("Added delicious milk");
}
}
//No milk added
public class FragrantCoffeeImp extends CoffeeImp{
FragrantCoffeeImp() {}
public void pourCoffeeImp(){
System.out.println("Nothing added, fresh fragrance");
}
}
We have already set up the basic framework of the Bridge model. Don’t forget another sentence in the definition: Combining dynamically, we can drink at least four kinds of coffee now:
1. Add milk to medium cup
2. No milk added in the medium cup
3. Add milk to a large cup
4. Large cup without milk
Let’s see how it is dynamically combined. Before using it, we make a preparation work and design a singleton class to hold the current CoffeeImp:
The code copy is as follows:
public class CoffeeImpSingleton{
private static CoffeeImp coffeeImp;
public CoffeeImpSingleton(CoffeeImp coffeeImpIn)
{this.coffeeImp = coffeeImpIn;}
public static CoffeeImp getTheCoffeeImp(){
return coffeeImp;
}
}
Let’s see how the medium cup of milk and large cup of milk come out:
//Take out the milk
CoffeeImpSingleton coffeeImpSingleton = new CoffeeImpSingleton(new MilkCoffeeImp());
//Middle cup with milk
MediumCoffee mediumCoffee = new MediumCoffee();
mediumCoffee.pourCoffee();
//A large cup of milk
SuperSizeCoffee superSizeCoffee = new SuperSizeCoffee();
superSizeCoffee.pourCoffee();
Note: Execution classes of Bridge schema such as CoffeeImp and Coffee are one-to-one relationships, and correctly creating CoffeeImp is the key to this schema.
Application of Bridge mode in EJB
There is a Data Access Object (DAO) pattern in EJB, which separates business logic from specific data resources because different databases have different database operations. The behaviors that operate different databases are independently abstracted into a behavioral interface DAO, as follows:
1.Business Object (similar to Coffee)
Implement some abstract business operations: such as finding a user to place all orders. DAOImplementor is used for database operations.
2.Data Access Object (similar to CoffeeImp)
Some abstract operations on database resources.
3. DAOImplementor such as OrderDAOCS, OrderDAOOracle, OrderDAOSybase (similar to MilkCoffeeImp FragrantCoffeeImp)
Specific database operations, such as "INSERT INTO" and other statements, OrderDAOOracle is Oracle OrderDAOSybase is Sybase database.
4. Database (Cloudscape, Oracle, or Sybase database via JDBC API)