7 principles of design patterns
The reason why some people say design patterns are dead is because frameworks like Spring help you manage classes and objects, and let you write code that focuses on what you implement, not design. Let’s start with the seven principles of design patterns:
-
Open – close principle
-
Single responsibility principle
-
Rely on the inversion principle
-
Principle of least knowledge
-
Interface Isolation Principle
-
Synthesis/polymerization reuse principle
-
Richter’s substitution rule says that wherever a base class can appear, a subclass must appear
Dependency inversion
Suppose we design a car: design the wheels first, then design the chassis according to the size of the wheels, then design the body according to the chassis, and finally design the whole car according to the body. Here comes a “dependency” relationship: the car depends on the body, the body depends on the chassis, and the chassis depends on the wheels.
The design looks fine, but it’s not maintainable.
Let’s say after the design is finished, the boss suddenly says that we need to change the wheel design of the car to a bigger size based on market demand. This was a pain in the ass: we designed the chassis according to the size of the wheels, and when the size of the wheels changed, the design of the chassis had to be changed; Just as we designed the body based on the chassis, so the body had to be changed, so did the design of the car — almost the whole design had to be changed!
Let’s think about it another way. We first design the general appearance of the car, then according to the appearance of the car to design the body, according to the body to design the chassis, and finally according to the chassis to design the wheels. At this point, the dependence is reversed: the wheels depend on the chassis, the chassis depends on the body, the body depends on the car.
At this time, the boss said to change the design of the wheel, we only need to change the design of the wheel, and do not need to move the chassis, body, car design. This is the dependence inversion principle — the original high-rise building depends on the bottom of the building “inverted” over, become the bottom of the building depends on the high-rise building. The tall building decides what is needed, and the bottom fulfils those needs, but the high building does not manage how the bottom fulfils those needs. This will not appear in front of the “pull a start the whole body” situation.
Inversion of Control
Is a kind of code design idea that relies on inversion principle. The specific method adopted is known as Dependency Injection. In fact, these concepts will feel foggy at first contact. To put it bluntly, the relationship between these concepts is as follows:
To understand these concepts, let’s use the car example above. Only this time in code. Let’s define four classes: car, body, chassis, and tire. And then we initialize the car, and then we run the car. The code structure is as follows:
Thus, as in the first example above, the superstructure depends on the substructure — each class constructor calls the constructor of the underlying code directly. Suppose we need to change the Tire class to have a dynamic size instead of 30 all the time. We need to change it like this:
Since we changed the tire definition, we need to make the following changes in order for the program to work:
As you can see, just to change the tire constructor, this design needs to change the constructors of the entire upper class! In software engineering, such a design would be almost unmaintainable — in a real engineering project, some classes might be at the bottom of thousands of classes, and it would be too expensive to maintain software if every time we changed this class, we had to change all the classes that depend on it.
So we need inversion of control (IoC), where the top controls the bottom, instead of the bottom controlling the top. We use Dependency Injection to achieve inversion of control. The so-called dependency injection is to pass the lower class as a parameter to the upper class to realize the “control” of the upper class to the lower class. Here we rewrite the definition of the car class using dependency injection passed by the constructor:
Here, we change the tire size to dynamic. Also, in order to make the whole system run smoothly, we need to make the following modifications:
See yet? Here I only need to modify the tire class, not any of the other upper classes. This is obviously easier code to maintain. Moreover, in practical engineering, this design pattern is also conducive to collaboration and unit testing among different groups. For example, four different groups can develop these four classes respectively, so as long as the interfaces are defined, the four different groups can develop at the same time without being restricted by each other. For unit tests, if we were to write a unit test of Car, we could Mock the Framework class into Car, rather than new the Framework, Bottom, and Tire all over again.
Here we do dependency injection by passing in the constructor. There are actually two other methods: Setter pass and interface pass. I won’t talk about it here, but the idea is the same, it’s all about inversion of control.
IoC Container
In fact, in the example above, the code that initializes the car class occurs in the inversion of control container.
Obviously you should have noticed that, with dependency injection, a lot of new writing is inevitable during initialization. Here the IoC container solves this problem. This container automatically initializes your code, so you only need to maintain a Configuration (either XML or a piece of code) instead of having to write a large chunk of initialization code each time you initialize a car.
This is the first benefit of introducing an IoC Container. The second benefit of an IoC Container is that we don’t need to know the details when we create the instance. In the example above, we manually create a car instance from the bottom to the top:
The process, we need to know the whole Car/Framework/Bottom/Tire is how to define the class constructor, step by step to the new/injection. The IoC Container does this the other way around. It starts at the top and works its way down to the bottom to find dependencies and then works its way up (a bit like depth-first traversal) :
IoC Containers can hide specific instance creation details directly.
This is the clearest article I’ve seen that says inversion of control. When you understand it, don’t worry about the framework, but the design itself, so start with the principles of design patterns.
The last
These are some of the things that the interviewer should ask during the interview. These include basics, Java collections, JVMS, multi-threaded concurrency, Spring principles, microservices, Netty and RPC, Kafka, diaries, design patterns, Java algorithms, databases, Zookeeper, distributed caching, data structures, and more.