Design Patterns
The Foundation of Reusable Object-Oriented Software
Design pattern is a set of repeated use, most people know, classification and cataloguing, code design experience summary. Design patterns are used to reuse code, make it easier for others to understand, and ensure code reliability. Undoubtedly, design patterns are win-win for others and systems. Design patterns make code compilation truly engineering. Design patterns are the cornerstone of software engineering, just like building blocks. Reasonable application of design patterns in projects can solve many problems perfectly. Each pattern has corresponding principles to correspond to it. Each pattern describes a problem that occurs repeatedly around us and the core solution of the problem. This is also the reason why it can be widely used. This chapter is Java Beauty [from novice to expert] series of design patterns, we will combine theory with practice to learn this chapter, hope that the majority of program enthusiasts, learn design patterns, to be an excellent software engineer!
If you have any questions during reading, please contact: egg.
Email: xtfggef@gmail.com microblog: http://weibo.com/xtfggef
Classification of Design Patterns
Overall, design patterns fall into three categories:
There are five types of creative mode: factory method mode, abstract factory mode, singleton mode, builder mode and prototype mode.
There are seven structural modes: adapter mode, decorator mode, agent mode, appearance mode, bridge mode, combination mode and hedonic mode.
There are eleven behavioral modes: strategy mode, template method mode, observer mode, iteration sub-mode, responsibility chain mode, command mode, memo mode, status mode, visitor mode, mediator mode and interpreter mode.
There are actually two types: concurrent mode and thread pool mode. Use a picture to describe it as a whole:
Six Principles of Design Patterns
1. Open Close Principle
The Open-Close Principle is that it is open to expansion and closed to modification. When the program needs to be expanded, we can not modify the original code to achieve a hot-swap effect. So in a word: in order to make the program scalable, easy to maintain and upgrade. To achieve this effect, we need to use interfaces and abstract classes, which we will mention later in the specific design.
2. Liskov Substitution Principle
One of the basic principles of object-oriented design is Liskov Substitution Principle LSP. According to Richter's substitution principle, subclasses can appear wherever base classes can appear. LSP is the cornerstone of inheritance and reuse. Only when derivative classes can replace base classes and the functions of software units are not affected, can base classes be reused, and derivative classes can add new behaviors on the basis of base classes. Richter's substitution principle is a supplement to the "open-close" principle. The key step to realize the "open-close" principle is abstraction. The inheritance relationship between base class and subclass is the concrete realization of abstraction, so Richter's substitution principle is the specification of concrete steps to realize abstraction. From Baidu Encyclopedias
3. Dependence Inversion Principle
This is the basis of the Open-Close Principle. Specific content: True programming of interfaces depends on abstraction rather than on concrete.
4. Interface Segregation Principle
This principle means that it is better to use multiple isolated interfaces than to use a single interface. It also means reducing the coupling between classes. From here we can see that the design pattern is actually the design idea of a software, from large software. Framework In order to upgrade and maintain conveniently. So there are many times above: reducing dependency and coupling.
5. Demeter Principle
Why is the least known principle? That is to say, an entity should interact with other entities as little as possible, so that the system function modules are relatively independent.
6. Composite Reuse Principle
The principle is to try to use synthesis/aggregation rather than inheritance.
Design Patterns in Java 23
Starting from this section, we introduce the concepts and application scenarios of 23 design patterns in Java in detail, and analyze their characteristics and principles of design patterns.
1. Factory Method
The factory method model is divided into three types:
11. The common factory pattern is to build a factory class, and create instances of some classes that implement the same interface. First, look at the diagram:
Examples are as follows: (Let's give an example of sending e-mails and short messages)
First, create a common interface between the two:
Second, create implementation classes:
Finally, plant construction class:
Let's come. test Next:
Output: this is sms sender!
22. The multiple factory method pattern is an improvement on the common factory method pattern. In the common factory method pattern, if the string passed is wrong, the object can not be created correctly. The multiple factory method pattern provides multiple factory methods to create objects separately. Diagram:
Modify the above code and change the SendFactory class as follows:
The test classes are as follows:
Output: this is mail ender!
33. Static factory method mode. Set the methods in the above multiple factory method modes as static, and call them directly without creating instances.
Output: this is mail ender!
Generally speaking, factory mode is suitable: when a large number of products need to be created and have a common interface, they can be created through factory method mode. Among the three modes mentioned above, the first one is that if the incoming string is incorrect, the object can not be created correctly. The third one does not need to instantiate the chemical plant class as compared with the second one. Therefore, in most cases, we will choose the third mode, the static factory method mode.
2. Abstract Factory
The factory method pattern has a problem that class creation depends on factory class, that is to say, if you want to expand the program, you must modify the factory class, which violates the closure principle. So, from the design point of view, there are some problems, how to solve them? The abstract factory pattern is used to create multiple factory classes, so that once new functions are needed, new factory classes can be added directly without modifying the previous code. Because Abstract factories are not easy to understand, let's look at the diagram first, and then with the code, it's easier to understand.
See examples:
Two implementation classes:
Two factory classes:
Providing an interface:
Test class:
In fact, the advantage of this mode is that if you want to add a function: send timely information, you just need to do an implementation class, implement Sender interface, and a factory class, implement Provider interface, OK, no need to change the existing code. In this way, the expansion is better!
3. Singleton
Singleton is a common design pattern. In Java applications, a singleton object ensures that only one instance of the object exists in a JVM. Such a model has several advantages:
1. Some classes are created frequently. For some large objects, this is a huge system overhead.
2. The new operator is omitted, which reduces the frequency of system memory usage and the pressure of GC.
3. Some classes, such as the core trading engine of an exchange, control the trading process. If this class can create more than one, the system is completely chaotic. (For example, when multiple commanders are in command at the same time in an army, they are bound to be in a mess), so only using the singleton mode can the core transaction server control the whole process independently.
First, let's write a simple singleton class:
This class can satisfy the basic requirements, but if we put it into a multi-threaded environment without thread security protection, there will be problems. How to solve them? We first think of adding synchronized keywords to the getInstance method, as follows:
However, the synchronized keyword locks the object. This usage will degrade in performance because every call to getInstance() locks the object. In fact, it is only necessary to lock the object when it is first created, and then it is not needed. Therefore, this place needs to be improved. Let's change it to the following one:
It seems to solve the problem mentioned earlier, adding synchronized keywords to the inside, that is to say, there is no need to lock when calling, only when instance is null, and when creating objects, it needs to lock, and the performance has been improved. However, this situation may be problematic, as follows: creating objects and assigning operations in Java instructions are done separately, that is, instance = new Singleton(); statements are executed in two steps. However, JVM does not guarantee the order of these two operations, that is to say, it is possible for JVM to allocate space for the new Singleton instance, then assign it directly to the instance member, and then initialize the Singleton instance. This may lead to errors. Let's take threads A and B as examples.
Threads a > A and B enter the first if judgment at the same time
B > A first enters the synchronized block, because instance is null, it executes instance = new Singleton();
C > Because of the optimization mechanism within the JVM, the JVM first draws some blank memory allocated to the Singleton instance and assigns it to the instance member (note that the JVM does not start initializing the instance at this time), then A leaves the synchronized block.
D > B enters the synchronized block. Because instance is not null at this time, it immediately leaves the synchronized block and returns the result to the program calling the method.
E > At this point, the B thread intends to use the Singleton instance, but finds that it has not been initialized, and the error occurs.
So it is possible for the program to make mistakes. In fact, the running process of the program is very complicated. From this point, we can see that, especially in the multi-threaded environment, the program is more difficult and challenging. We further optimize the program:
In fact, the singleton mode uses internal classes to maintain the implementation of singletons, and the internal mechanism of JVM can ensure that when a class is loaded, the loading process of this class is threaded mutually exclusive. So when we first call getInstance, JVM can help us ensure that instances are created only once, and that the memory assigned to instances is initialized, so that we don't have to worry about the above problems. At the same time, this method only uses the mutex mechanism in the first call, which solves the problem of low performance. So let's summarize a perfect singleton model for the time being:
In fact, it is perfect, and not necessarily, if an exception is thrown in the constructor, the instance will never be created and error will occur. So, there is no perfect thing, we can only choose the most suitable implementation method for our application scenario according to the actual situation. Others do this: because we only need to synchronize when creating classes, it is also possible to separate the creation from getInstance() and add synchronized keywords to the creation alone:
Considering performance, the whole program only needs to create an instance once, so performance will not have any impact.
Supplementary: Synchronized updating of attributes for individual objects by "shadow instances"
Through the study of the singleton model, we can learn that:
1. The singleton model is easy to understand, but it is still difficult to implement.
2. The synchronized keyword locks the object. When it is used, it must be used in the right place. (Note the objects and processes that need to be locked, sometimes not the whole object and the whole process need to be locked.)
At this point, the singleton mode has been basically finished. At the end, the author suddenly thought of another problem, that is, using the static method of classes to achieve the effect of singleton mode is also feasible. What is the difference between the two?
First, static classes cannot implement interfaces. (From a class point of view, yes, but that destroys the static state. Because static modification is not allowed in the interface, it is non-static even if implemented.
Secondly, singletons can be delayed initialization, and static classes are usually initialized at the first load. The reason for delayed loading is that some classes are relatively large, so delayed loading helps improve performance.
Third, a singleton class can be inherited and its methods can be overridden. However, the internal methods of static classes are static and cannot be overridden.
Finally, the singleton class is more flexible. After all, it is only a common Java class. As long as it meets the basic needs of the singleton, you can implement some other functions as you like, but the static class is not. From the above generalizations, we can basically see the difference between the two, but on the other hand, the singleton pattern that we finally implemented above is implemented by a static class internally. Therefore, the two are closely related, but the level we consider is different. The combination of the two ideas can create a perfect solution, just like HashMap using array + linked list to achieve, in fact, many things in life are like this, using different methods to deal with problems, there are always advantages and disadvantages, the most perfect way is to combine the advantages of each method, in order to solve the best problem!
4. Builder Model
The factory class pattern provides the pattern of creating a single class, while the builder pattern manages all kinds of products together to create composite objects. The so-called composite objects refer to a class with different attributes. In fact, the builder pattern is the combination of the former abstract factory pattern and the last Test. Let's look at the code:
As before, a Sender interface, two implementation classes MailSender and SmithSender. Finally, the categories of builders are as follows:
Test class:
From this point of view, the builder pattern integrates many functions into a class that can create more complex things. So the difference from the engineering model is that the factory model focuses on creating a single product, while the builder model focuses on creating conforming objects and parts. Therefore, the choice of factory mode or builder mode depends on the actual situation.
5. Prototype
Although the prototype pattern is a creation pattern, it has nothing to do with the engineering pattern. From the name, we can see that the idea of this pattern is to copy and clone an object as a prototype, and produce a new object similar to the original object. This summary will explain through the replication of the object. In Java, clone() is used to replicate objects. First, a prototype class is created.
Simply, a prototype class only needs to implement Cloneable interface and override clone method, where clone method can be changed to any name, because Cloneable interface is an empty interface, you can arbitrarily define the method name of implementation class, such as cloneA or cloneB, because the emphasis here is super.clone(), which calls clone() method of Object, and clone() method of Object. In class, clone() is native. How to implement it? I'll go into another article about interpreting calls to native methods in Java, but I won't go into it here. Here, I will talk about shallow and deep replication of objects. First of all, we need to understand the concept of deep and shallow replication of objects.
Shallow replication: After an object is replicated, the variables of the basic data type are recreated, and the reference type refers to the original object.
Deep copy: After copying an object, both the basic data type and the reference type are recreated. Simply put, deep copy is completely and thoroughly duplicated, while shallow copy is not thoroughly duplicated.
Here, write an example of deep and shallow replication:
In order to achieve deep replication, it is necessary to read the binary input of the current object in the form of stream, and then write out the corresponding object of binary data.
Because of the long length of the article, in order to make it easier for readers to read, I will put the other introductions I have received into another article (maybe two articles). Thank you for your valuable comments and suggestions!
If you have any questions, please contact: egg
Email:xtfggef@gmail.com. http://weibo.com/xtfggef