1. Summary of the foregoing
“Make up” ongoing: Design pattern series
2. Start with the bank transfer
When we conduct the transfer operation in the bank, the whole operation process can be simplified as account A deducts the fee, then account B increases the balance, and finally the transfer operation is successful.
Both operations are indispensable, and the order cannot be reversed.
Simply define a transfer interface ITransfer:
public interface ITransfer {
// Initiate the transfer first
void start(String amount);
// Account A will deduct the fee
void subtractionA(a);
// Account B increases the amount
void addB(a);
// Transfer completed
void end(a);
}
Copy the code
Then add an interface implementation class:
public class TransferImpl implements ITransfer {
@Override
public void start(String amount) {
System.out.println(String.format("Account A initiates A transfer to account B: % S dollars.", amount));
}
@Override
public void subtractionA(a) {
System.out.println("Account A has been successfully deducted.");
}
@Override
public void addB(a) {
System.out.println("Account B balance increased successfully");
}
@Override
public void end(a) {
System.out.println("Transfer completed"); }}Copy the code
Here’s a test class:
public class Test {
public static void main(String[] args) {
ITransfer transfer = new TransferImpl();
transfer.start("1000"); transfer.subtractionA(); transfer.addB(); transfer.end(); }}Copy the code
The final result of the run is as follows:
Account A starts transferring money to account B:1000Yuan. Account A successfully deducted the fee and account B successfully increased the balanceCopy the code
Looking back at this process, it is far from the requirements of high cohesion, let alone Demeter’s law, interface isolation.
If we want to do a transfer, we have to know these steps and know the order in which they are done. If something goes wrong, the transfer cannot be done, which is extremely inappropriate in object-oriented programming because it does not fulfill the single responsibility of a class.
So what to do? At this time, the bank counter appeared, we just need to tell the bank counter, the counter will directly help us complete the transfer operation.
Bank Counter:
public class BankCounter {
private ITransfer transfer = new TransferImpl();
// Transfer operation integration
public void transferAmount(String amount) { transfer.start(amount); transfer.subtractionA(); transfer.addB(); transfer.end(); }}Copy the code
Next modify the test class:
public class Test1 {
public static void main(String[] args) {
BankCounter counter = new BankCounter();
counter.transferAmount("1000"); }}Copy the code
And execution result just now, but the whole test class is simplified A lot, just care about and bank counter to interact, completely without worrying about their own account before A deduction, again add balance to account B, however, every time transfer so direct transfer is not safe, if the balance of accounts A grossly inadequate transfer fees, Then the transfer should not be successful.
Add a Balance verification class to verify the account Balance:
public class Balance {
Boolean checkBalance(a) {
System.out.println("Account balance verification successful");
return true; }}Copy the code
In this case, there is no need to change the test class, just modify the bank counter class:
public class BankCounter {
private ITransfer transfer = new TransferImpl();
private Balance balance = new Balance();
// Transfer operation integration
public void transferAmount(String amount) {
transfer.start(amount);
transfer.subtractionA();
// Add the balance check
if(balance.checkBalance()) { transfer.addB(); transfer.end(); }}}Copy the code
Only one balance verification class is added here, and the transfer process is modified. This process is completely transparent for us, and we do not need to care about the transfer process, which is handled by the bank counter for us.
There is no change in the high-level module, but the balance of the account has been checked. The interfaces and methods exposed to the subsystem are not changed, but the internal processing logic is changed. The calls of other sibling modules produce different results.
Isn’t it very simple? Yes, it’s the facade model or the appearance model.
3. Facade mode
3.1 define
Facade Pattern, also known as Facade Pattern, is a common encapsulation Pattern, which is defined as follows:
Provide a unified interface to a set of interfaces in a subsystem.Facadedefines a higher-level interface that makes the Communication between the outside of a subsystem and the inside of it must be through a unified object Facade mode provides a high-level interface that makes subsystems easier to use.
3.2 General class diagram
The facade pattern focuses on the “unified object”, that is, providing an interface to access the subsystem, except that this interface does not allow any behavior to access the subsystem, and its general class diagram:
Subsystem Classes is short for all the Classes of a Subsystem. It may represent a single class or a collection of dozens of objects. Let’s circle all of these objects into the subsystem category, no matter how many:
To put it simply, the facade object is the only channel for the outside world to access the interior of the subsystem. No matter how chaotic the interior of the subsystem is, as long as there are facade objects, you can achieve “the outside of the golden jade, the inside of the mess”. Let’s first define the role of facade mode.
- Facade role: This role knows all the functions and responsibilities of the subsystem. Typically, this role delegates all requests from the client to the appropriate subsystem, meaning that this role has no actual business logic and is just a delegate class.
- Subsystem Role: Can have one or more subsystems at the same time. Each subsystem is not a single class, but a collection of classes. The subsystem is unaware of the facade. To a subsystem, a facade is just another client.
3.3 Common Code
The subsystem:
//
public class ClassA {
public void doSomethingA(a) {
// Execute logic A}}public class ClassB {
public void doSomethingB(a) {
// Execute logic A}}public class ClassC {
public void doSomethingC(a) {
// Execute logic A}}Copy the code
The facade class:
public class Facade {
private ClassA classA = new ClassA();
private ClassB classB = new ClassB();
private ClassC classC = new ClassC();
public void methodA(a) {
this.classA.doSomethingA();
}
public void methodB(a) {
this.classB.doSomethingB();
}
public void methodC(a) {
this.classC.doSomethingC(); }}Copy the code
4. Pay attention to
It is important to note that the facade does not participate in the business logic within the subsystem.
How to understand this sentence? Here’s a simple example:
Modifying the generic code slightly above, call ClassA doSomethingA() on methodC() and then ClassC doSomethingC() as follows:
public class Facade {
private ClassA classA = new ClassA();
private ClassB classB = new ClassB();
private ClassC classC = new ClassC();
public void methodA(a) {
this.classA.doSomethingA();
}
public void methodB(a) {
this.classB.doSomethingB();
}
public void methodC(a) {
this.classA.doSomethingA();
this.classC.doSomethingC(); }}Copy the code
Very simple, just adding a doSomethingA() call to methodC(). Can you do that?
I’m sure in most of our daily development, we do this directly a lot of the time. What’s wrong with this?
Of course it does, because it allows facade objects to participate in the business logic. Facade objects simply provide a path to access subsystems. They should not and cannot participate in the concrete business logic, or else it creates a dependency problem: subsystems must depend on the facade to be accessed.
So what can be done in this case?
Create a wrapper class and provide it to the facade object:
public class Context {
private ClassA classA = new ClassA();
private ClassC classC = new ClassC();
// Complex business operations
public void complexMethod(a) {
this.classA.doSomethingA();
this.classC.doSomethingC(); }}Copy the code
The value of this wrapper class is that it generates a complexMethod() of business rules, which only depends on two related objects within a subsystem. The facade object accesses it to complete a complex business logic. Finally, we call the wrapper class directly when we call the facade pattern:
public class Facade1 {
private ClassA classA = new ClassA();
private ClassB classB = new ClassB();
private Context context = new Context();
public void methodA(a) {
this.classA.doSomethingA();
}
public void methodB(a) {
this.classB.doSomethingB();
}
public void methodC(a) {
this.context.complexMethod(); }}Copy the code
Through such an encapsulation, facade object was not involved in the business logic again, in the facade pattern, appearance character should be stable, it should not change often, once a system is in operation it should not be changed, it is a system of external interface, you changing how can ensure the stable operation of the other modules? However, the business logic changes constantly, and we’ve encapsulated its changes within the subsystem, so that no matter how you change it, it’s still the same facade, the same approach to the outside world — that’s what architects want most.
This article is constantly updated, you can search “Geek excavator” on wechat to read it in the first time, and the key words in reply have all kinds of tutorials PREPARED by me, welcome to read.