
Why design patterns are needed
In fact, we can complete the development work without a design pattern. But why do you need a design pattern? Makes you look awesome, yes, this is one. Make your code hierarchical, readable and easy to maintain. Let you have more time to fish and paddle like me.
Some people may say that you have made seven or eight of the things I completed with one class or method. Of course, the use of design patterns must also be considered. Design patterns are not recommended for some simple and stable businesses. Design patterns are mostly used in complex and changeable businesses or scenarios that require greater adaptability and extensibility. Don't design patterns for the sake of design patterns.
Next, let's discuss some principles of design patterns based on actual conditions.
open closed principle
public class Seller {
public BigDecimal sellCar(Car car) {
return car.getPrice();
}
}
The above simulates a salesperson in a 4S shop selling a car. Suddenly, the boss launched a promotion: a discount event would be launched on Double Eleven. Is it feasible to add a calculation to the sellCar method? This will inevitably affect the entire business and lead to discounts on all cars. No, no! So operate in Car? Then you change and change! Results Various logical process judgments. Only then did business requirements be achieved. If subsequent discounts end or are upgraded, you will have to make various changes. You find that a discount has left your code unrecognizable and bloated. It is said above that complex and changeable businesses can be solved by using design patterns. Then one of the most important principles of design patterns is the open-close principle. that is
A software model entity such as classes, modules, and functions should be open to extensions and closed to modifications. That is, we need to abstract business behaviors and use abstractions to build them. Specific businesses are solved through abstract implementations. Then we will build a DiscountCar to extend Cars. In this way, the specific logic will be implemented for whatever specific implementation of sellCar. It will not affect previous logic, and it will not affect other logic by changing the original code. Ensure interface reliability and stability. As follows:
public class DiscountCar extends Car{
private BigDecimal price;
private BigDecimal discount;
@Override
public BigDecimal getPrice() {
return price.multiply(discount);
}
}
Dependency Inversion principle
Also take the above example. After a series of discount activities, the business of the 4S store is booming. The boss suddenly wanted to expand his surroundings and squeeze sales at the same time. Let them sell glass water, antifreeze and the like while selling cars. This demand was of course thrown to the hard-working programmers. SellCar is too specific to meet the needs. In many cases, you will add a method to sell glass water and antifreeze. What if we increase the sales of rice in the future or even buy egg cakes? You can't keep adding methods. We need to consider this kind of issue. We can abstract all the scenes of selling things. Then we abstract the item sold into an abstract concept (java corresponds to the interface, and abstract the selling behavior into a sell method:
public interface Any {
String getName();
BigDecimal getPrice();
}
public class Seller {
public BigDecimal sell(Any any) {
return any.getPrice();
}
}
This way, you can process whatever the boss sells in the future through this method, just focus on the implementation of Any.
Single responsibility
After selling something in the 4S store, it found that it was not very attractive to customers. Suddenly, the boss, who had a more flexible mind, remembered a line from the movie: Is Shaolin Kung Fu combined with singing and dancing successful? Right, can you sell something like singing, dancing, and Rap? Of course, you don't want to play basketball. Don't break the car glass. But people are different. Some people can only sing, some can only dance, and some people may be good at singing, dancing, and even playing basketball. So in order to adapt to so many situations, we must isolate each skill and combine these skills based on different people.
public class Seller implements Sing, Jump, Rap {
public BigDecimal sell(Any any) {
return any.doSell();
}
@Override
public void sing() {
System.out.println("seller sing ");
}
@Override
public void jump() {
System.out.println("seller jumping ");
}
@Override
public void rap() {
System.out.println("seller raping ");
}
}
However, attention must be paid to moderation and segmentation according to business. Otherwise, it will lead to too many interfaces, which will increase development difficulty and code complexity.
law of Demeter
After developing the new sales method for some time, the boss wanted to see and test the effect of his bad idea. So I called a salesperson and asked him to provide a report to have a look. So how should a programmer implement the boss's report viewing function? It is likely that someone will write this:
public class Boss {
private Seller seller;
private Report report;
public void read() {
seller.apply(report);
}
}
At first glance, the function has been realized, but if you look closely, you will find that the logic is wrong. What's wrong? The boss already holds the report. If the boss already knows your performance, what would he ask you to do? This logic is definitely wrong! That is to say, Boss directly relies on Report; this Report should not be processed directly by Boss, but should be controlled by Seller.
public class Boss {
private Seller seller;
public void read(){
seller.apply();
}
}
public class Seller {
private Report report;
public void apply(){
report.show();
}
}
This maximization isolates the relationships between classes. Reduced coupling between classes. Demeter's rule was also named after the principle of least knowing.
interface segregation principle
With multiple specialized interfaces rather than a single master interface, the client should not rely on interfaces it does not need. The dependence of a class on a class should be based on the smallest interface.
Establish a single interface, do not create a huge and bloated interface and refine the interface as much as possible. Try to minimize methods in the interface and refine the interface as much as possible. Pay attention to the principle of moderation and be moderate. cannot abuse
Like the singing and dancing rap above, separation is the best.
Leary substitution principle
This is mainly about the inheritance relationship of classes. A more formal definition: If for every object o1 of type S, there is an object o2 of type T, so that the behavior of all programs P defined with T does not change when all objects o1 are replaced with o2, then type S is a subtype of type T.
In the eyes of the 4S store owner, as long as a newcomer can sell cars like a sales veteran in a sales position, he is a qualified salesperson. It feels like this definition is like a famous saying: No matter whether you are black or white, anyone who can catch mice is a good cat.
In a sense, the Richter substitution has the following contracts:
- Subclasses must fully implement the methods of the parent class. Subclasses can replace the parent class wherever the parent class appears.
- Subclasses can have their own personality definitions. The Richter substitution principle can be used positively, but not reversely. Where subclasses appear, the parent class may not be competent. Subclasses generally have personality than their parent classes.
- Input parameters can be enlarged when overriding or implementing methods of the parent class. If the owner of the 4S store stipulates that the discount on the price of basic cars can be at most 10% off, and there is no problem with a 5% discount on sales, the owner will definitely explain it to you for a 20% discount.
- The output can be narrowed when overriding or implementing methods of the parent class. Similarly, the 15W could only be sold to customers as a beggar version, but the sales results were changed and a flagship version was given. I'm afraid I won't be able to pass the probation period.
Composition/reuse principles
It requires that when software reuse, use association relationships such as composition or aggregation first, and then consider using inheritance relationships.
If inheritance relationships are to be used, the Richter substitution principle must be strictly followed. The principle of composite reuse and the principle of Richter substitution are complementary, and both are specific implementation specifications of the open-close principle.
summary
These seven design principles are principles that software design patterns must follow as much as possible, and the focus of each principle is different. Among them, the opening-closing principle is the general outline, which tells us to be open to extensions and close to modifications; the Richter substitution principle tells us not to destroy the inheritance system; the dependence inversion principle tells us to program for interfaces; the single responsibility principle tells us to implement classes. The responsibility is single; the interface isolation principle tells us to be concise and simple when designing interfaces; the Demeter rule tells us to reduce the degree of coupling; The principle of composite reuse tells us to prioritize reuse with composition or aggregation relationships and less reuse with inheritance relationships. In actual development, we can use design patterns based on our business, but it is very important not to be bound by these rules and regulations.
Comments0