Design mode (Patterns)
--the basis of reusable object-oriented software
Design pattern is a set of reusable, most known, categorized purposes, code design experience Summary. Design patterns are used in order to reuse code, make code easier to understand, and ensure code reliability. There is no doubt that design patterns in others in the system are multi-win, design patterns so that code is really project, design patterns are the cornerstone of software project, like a block of bricks and tiles in the building. The rational use of design patterns in the project can perfectly solve a lot of problems, each of which has its corresponding principle in the present, each model describes a recurring problem around us, and the core solution of the problem, which is why it can be widely used. This chapter is the beauty of Java [from rookie to master evolution] series of design patterns, we will be a combination of theory and practice in this chapter of learning, hope that the vast number of program enthusiasts, learn the design model, do a good software project teacher!
In the process of reading no matter what the problem, please contact: egg.
Email: [Email protected] Weibo:http://weibo.com/xtfggef
if reproduced, please indicate the source: http://blog.csdn.net/zhangerqing
I. Classification of design Patterns
In general, design patterns fall into three broad categories:
Create five types of models: Factory method mode, abstract Factory mode, singleton mode, builder mode, prototype mode.
Structure mode, a total of seven kinds: Adapter mode, adorner mode, proxy mode, appearance mode, bridging mode, combined mode, enjoy the meta-mode.
Behavioral patterns, a total of 11 kinds: Strategy mode, template method mode, observer mode, iteration sub-mode, responsibility chain mode, Command mode, Memo mode, state mode, interview mode, mediator mode, interpreter mode.
In fact, there are two classes: the concurrency pattern and the thread pool pattern. Use a picture to describe the general description:
The six principles of design pattern
1. Opening and closing principle (Open Close Principle)
The opening and closing principle is to say open to expansion, to change the closure . When the program needs to expand, can not change the original code, to achieve a hot plug effect. So a nutshell is: In order to make the program good extensibility, easy to maintain and upgrade. To achieve this, we need to use interfaces and abstract classes, which we will mention later in the detailed design.
2. The principle of substitution on the Richter scale (Liskov Substitution Principle)
The Richter substitution principle (Liskov Substitution Principle LSP) is one of the fundamental principles of object-oriented design. In the principle of substitution of the Richter scale, the subclass must be able to appear wherever the base class can appear. LSP is the cornerstone of inheritance reuse, only when the derivative class can replace the base class, the function of the Software unit is not affected, the base class talent is really reused, and the derived class can also add new behavior on the basis of the base class. The principle of substitution on the Richter scale is a supplement to the principle of "open-closed". The key step in implementing the "open-close" principle is abstraction. The inheritance relationship between the base class and the subclass is the detailed implementation of abstraction, so the principle of the Richter substitution is the specification of the detailed steps to achieve abstraction. --from Baidu Encyclopedia
3. Dependence reversal principle (dependence inversion Principle)
This is the basis of the open and close principle, Details: TRUE interface programming, relying on abstraction and not dependent on the details.
4. Interface Isolation principle (Interface segregation Principle)
This principle means that using multiple isolated interfaces is better than using a single interface. or a reduction of the coupling between the class meaning, from here we see, in fact, the design pattern is a software design idea, from the large software architecture, in order to upgrade and maintenance convenience. So there are multiple occurrences: fewer dependencies, less coupling.
5, Dimitri Law (least known principle) (Demeter Principle)
Why is it called the least known principle, that is to say: an entity should be as small as possible interaction with other entities, so that the system function module is relatively independent.
6. Synthetic multiplexing principles (Composite reuse Principle)
The principle is to use composition/aggregation as much as possible, rather than using inheritance.
Third, Java 23 design mode
Starting from this piece, we introduce in detail the concepts of 23 design Patterns in Java, application scenarios and so on, combined with their characteristics and the principles of design patterns.
1. Factory method Mode (Factory methods)
The factory method pattern is divided into three types:
11, the Common factory model , is to establish a factory class, the implementation of the same interface for some classes to create instances. First look at the diagram:
The ratio is as follows: (we give a sample of sending email and SMS)
First, create a common interface for both:
Public interface Sender {public void Send ();}
Second, create the implementation class:
public class MailSender implements Sender {@Overridepublic void Send () {System.out.println ("This is mailsender!");}}
public class Smssender implements Sender {@Overridepublic void Send () {System.out.println ("This is SMS Sender!");}}
Finally, build the factory class:
public class Sendfactory {public Sender produce (String type) {if ("Mail". Equals (Type)} {return new MailSender ();} else if ("SMS". Equals (Type)) {return new Smssender ();} else {System.out.println ("Please enter the correct type!"); return null;}}}
Let's take a test:
public class Factorytest {public static void main (string[] args) {Sendfactory factory = new Sendfactory (); Sender sender = factory.produce ("sms"); sender. Send ();}}
Output: This is SMS sender!
22, multiple factory method mode , is the improvement of the common factory method mode, in the normal factory method mode, assume that the passed string error, the object is not created correctly, and multiple factory method pattern is to provide multiple factory methods, create the object respectively. Diagram:
Modify the above code to modify the next Sendfactory class, such as the following:
public class Sendfactory {public Sender producemail () {return new MailSender ();} Public Sender producesms () {return new Smssender ();}}
Test classes such as the following:
public class Factorytest {public static void main (string[] args) {Sendfactory factory = new Sendfactory (); Sender sender = Factory.producemail (); sender. Send ();}}
Output: This is mailsender!
33, Static Factory method mode , the above method of multiple factory methods to static, do not need to create an instance, the direct call can be.
public class Sendfactory {public static Sender Producemail () {return new MailSender ();} public static Sender Producesms () {return new Smssender ();}}
public class Factorytest {public static void main (string[] args) {Sender sender = Sendfactory.producemail (); sender. Send ();}}
Output: This is mailsender!
On the whole, the factory model is suitable: when a large number of products need to be created, and have a common interface, can be created through the factory method mode. In the above three modes, the first assumes that the passed-in string is incorrect, the object cannot be created correctly, the third one is relative to the other, does not require an instance of the chemical plant class, so, in most cases, we will choose the third-static factory method mode.
2. Abstract Factory mode (Factory)
Factory method Mode There is a problem is that the creation of the class depends on the factory class, that is, if you want to expand the program, the factory class must be changed, which violates the closure principle, so from the design point of view, there are certain problems, how to solve? Use the abstract Factory mode to create multiple factory classes, so that once you have to add new functionality, you can add new factory classes directly, without needing to change the previous code. Because the abstract factory is not very well understood, we first look at the diagram, and then the code, it is easier to understand.
Take a look at the examples:
Public interface Sender {public void Send ();}
Two implementation classes:
public class MailSender implements Sender {@Overridepublic void Send () {System.out.println ("This is mailsender!");}}
public class Smssender implements Sender {@Overridepublic void Send () {System.out.println ("This is SMS Sender!");}}
Two factory classes:
public class Sendmailfactory implements Provider {@Overridepublic Sender produce () {return new MailSender ();}}
public class Sendsmsfactory implements provider{@Overridepublic Sender produce () {return new Smssender ();}}
In providing an interface:
Public interface Provider {public Sender produce ();}
Test class:
public class Test {public static void main (string[] args) {Provider Provider = new Sendmailfactory (); Sender sender = provider.produce (); sender. Send ();}}
In fact, the advantage of this model is that if you want to add a function now: Send timely information, you just need to do an implementation class, to implement the sender interface, the same time to do a factory class, the implementation of the provider interface, OK, no need to modify the ready-made code. To do so, expand better!
3. Single case mode (Singleton)
A singleton object (Singleton) is a frequently used design pattern. In Java applications, singleton objects are guaranteed to exist in a single JVM with only one instance of the object. This model has several advantages:
1, some classes are created more frequently, for some large objects, this is a very large system overhead.
2, eliminate the new operator, reduce the use of system memory frequency, reduce the GC pressure.
3. Some classes, such as the exchange's core trading engine, control the trading process, assuming that the class can create multiple words, the system is completely chaotic. (for example, an army has multiple commanders at the same time commanding, will certainly be mess), so only the use of single-case mode, the ability to ensure that core trading server independently control the entire process.
First, let's write a simple singleton class:
public class Singleton {/* Holds private static instances, prevents references, and is assigned null here to implement delay loading */private static Singleton instance = null;/* Private construction method to prevent instantiation */private Singleton () {}/* static project method, creating instance */public static Singleton getinstance () {if (instance = null) {instance = NE W Singleton ();} return instance;} /* Assume that the object is used for serialization to ensure that the object remains consistent before and after serialization */public object Readresolve () {return instance;}}
This class can meet the basic requirements, but, like this no thread-safe class, suppose we put it into multi-threaded environment, there will be a failure, how to solve? We will first think about adding Synchronizedkeyword to the GetInstance method, such as the following:
public static synchronized Singleton getinstance () {if (instance = = null) {instance = new Singleton ();} return instance;}
However, Synchronizedkeyword lock is this object, this method of use, in performance will be reduced, because each call getinstance (), the object is locked, in fact, only the first time to create the object need to lock, and then do not need, so, This place needs to be improved. Let's change to the following:
public static Singleton getinstance () {if (instance = = null) {synchronized (instance) {if (instance = = null) {instance = n EW Singleton ();}}} return instance;}
Seems to overcome the problem mentioned earlier, the Synchronizedkeyword added to the internal, that is, when the call is not required to lock, only in the instance is null, and create the object when the need to lock, performance has a certain increase. However, there may be problems with this, see the following: creating objects and assigning operations in Java directives is done separately, that is, instance = new Singleton (); The statement is run in two steps. However, the JVM does not guarantee the sequencing of the two operations, which means that it is possible for the JVM to allocate space for the new singleton instance and then assign the value directly to the instance member before initializing the singleton instance. This can be a mistake, we take a, b two threads as an example:
A>a, B Threads Enter the first if inference at the same time
B>a first enters the synchronized block because instance is null, so it runs instance = new Singleton ();
C> because of the optimization mechanism inside 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), and a leaves the synchronized block.
D>b enters the synchronized block because instance is not NULL at this time, so it immediately leaves the synchronized block and returns the result to the program that called the method.
E> this time the B thread intends to use the singleton instance, but finds that it is not initialized, and then an error occurs.
So the program is still possible error, in fact the program is very complex in the process of operation, from this point we can see, especially in the multi-threaded environment of the program is more difficult and challenging. We have further optimized the program:
private static class singletonfactory{ private static Singleton instance = new Singleton (); } public static Singleton getinstance () { return singletonfactory.instance;
The reality is that the singleton pattern uses an internal class to maintain the implementation of the Singleton, and the mechanism inside the JVM guarantees that when a class is loaded, the loading process of this class is mutually exclusive. So when we first call getinstance, the JVM can help us ensure that instance is created only once, and that the memory that is assigned to instance is initialized, so we don't have to worry about the problem. At the same time, the method only uses the mutual exclusion mechanism at the first call, which overcomes the low performance problem. This gives us a temporary summary of a perfect singleton pattern:
public class Singleton {/* Private construction method to prevent instantiation of */private Singleton () {}/* Use an inner class here to maintain a singleton */private static class Singletonfactory {private static Singleton instance = new Singleton ();} /* Get instance */public static Singleton getinstance () {return singletonfactory.instance;} /* Assume that the object is used for serialization to ensure that the object remains consistent before and after serialization */public object Readresolve () {return getinstance ();}}
In fact, it's perfect, not necessarily, assuming an exception is thrown in the constructor, the instance will never be created and will be faulted. So, the perfect thing is not, we can only according to the actual situation, choose the most suitable for their own application of the implementation of the scenario. Others do this: since we only need to synchronize when creating the class, it is also possible to separate the creation and getinstance () separately from the creation of the Synchronizedkeyword:
public class Singletontest {private static Singletontest instance = Null;private singletontest () {}private static Synchron ized void Syncinit () {if (instance = = null) {instance = new Singletontest ();}} public static Singletontest getinstance () {if (instance = = null) {Syncinit ();} return instance;}}
With performance in mind, the entire program only needs to create one instance at a time, so performance has no effect.
Add: use shadow instance to synchronize updates for properties of Singleton objects
public class Singletontest {private static singletontest instance = Null;private Vector properties = null;public vector ge Tproperties () {return properties;} Private Singletontest () {}private static synchronized void Syncinit () {if (instance = = null) {instance = new Singletontest ();}} public static Singletontest getinstance () {if (instance = = null) {Syncinit ();} return instance;} public void Updateproperties () {singletontest shadow = new Singletontest ();p roperties = Shadow.getproperties ();}}
Learning through a singleton pattern tells us:
1, the single-case model is simple to understand, but the detailed implementation is still a certain degree of difficulty.
2, Synchronizedkeyword Lock is the object, in use, must be used in the appropriate place (note the need to use the lock object and process, may not be the whole object and the whole process need to lock).
Here, the singleton mode has been basically finished, at the end, I suddenly think there is a problem, is to adopt the static method of the class, to achieve the effect of the single-case model, is also feasible, here are the differences?
First, a static class cannot implement an interface. (It is possible from a class point of view, but that destroys the static.) Because the interface does not agree with the method of static modification, it is not static even if it is implemented.
Second, a singleton can be delayed initialized, and static classes are typically initialized in the first load. The delay in loading is due to the fact that some analogies are large, so delayed loading can help improve performance.
Again, a singleton class can be inherited, and his method can be overwritten. However, static class internal methods are static and cannot be overwritten.
The last point, the simple analogy is more flexible, after all, from the implementation is only a common Java class, just to meet the basic needs of a single case, you can in the arbitrary implementation of some other functions, but static class does not. From the above summary, the basic can see the difference between the two, but, from the other side, we finally realized that the singleton pattern, the interior is a static class to achieve, so, the two have a very large association, just we consider the level of the problem is different. The combination of two ideas, the ability to create a perfect solution, like HashMap with array + linked list to achieve the same, in fact, many things in life are so, alone with different methods to deal with problems, there are strengths and shortcomings, the most perfect way is to combine the strengths of each method, Talent best to solve this problem!
4. Builder mode
The factory class pattern provides the pattern of creating a single class, while the builder pattern is to centralize the various products to manage the creation of composite objects, so-called composite objects that refer to a class with different properties, in fact the builder pattern is the combination of the previous abstract factory pattern and the last Test. Let's look at the code:
As before, a sender interface, two implementation classes MailSender and Smssender. Finally, the builder class such as the following:
public class Builder {private list<sender> List = new arraylist<sender> ();p ublic void Producemailsender (int c Ount) {for (int i=0; i<count; i++) {list.add (New MailSender ());}} public void Producesmssender (int. count) {for (int i=0; i<count; i++) {list.add (New Smssender ());}}}
Test class:
public class Test {public static void main (string[] args) {Builder builder = new Builder (); Builder.producemailsender (10);} }
From this point of view, the builder pattern integrates very versatile into a class that can create something more complex. So the difference with Project mode is that the factory model is concerned with creating a single product, while the builder pattern is focused on creating a conforming object, multiple parts. Therefore, the choice of the factory model or the builder model depends on the actual situation.
5. Prototype mode (PROTOTYPE)
Although the prototype pattern is a pattern of creation, it has nothing to do with the project schema, as the name suggests, the idea is to copy and clone an object as a prototype, creating a new object similar to the original object. This summary will be explained by the copying of the objects. In Java, the Copy object is implemented through clone (), and a prototype class is created first:
public class Prototype implements cloneable {public Object clone () throws Clonenotsupportedexception {Prototype proto = (P Rototype) Super.clone (); return proto;}}
Very easy, a prototype class, just need to implement the Cloneable interface, overwrite the Clone method, where the Clone method can be changed to arbitrary name, because the Cloneable interface is an empty interface, you can arbitrarily define the method name of the implementation class, such as Clonea or CLONEB, Since the focus here is Super.clone (), Super.clone () invokes the Clone () method of object, whereas in the object class, Clone () is native, and in detail how it is implemented, I'll have an article The call to interpret native methods in Java is no longer a drill down here. Here, I will combine the shallow copy of object and deep copy to say, first need to understand object deep, shallow copy concept:
Shallow copy: After copying an object, a variable of the underlying data type is created again, and the reference type, pointing to the original object.
Deep copy: After copying an object, both the base data type and the reference type are created again. In short, deep replication is completely replicated, and shallow copying is not exhaustive.
Here, write a sample of a depth copy:
public class Prototype implements Cloneable, Serializable {private static final long Serialversionuid = 1l;private String String;private serializableobject obj;/* Shallow copy */public Object clone () throws Clonenotsupportedexception {Prototype proto = (Prototype) Super.clone (); return proto;} /* Deep Copy */public Object Deepclone () throws IOException, ClassNotFoundException {/* Write binary Stream */bytearrayoutputstream BOS for current object = new Bytearrayoutputstream (); ObjectOutputStream oos = new ObjectOutputStream (BOS); Oos.writeobject (this);/* read out the new object produced by the binary stream */bytearrayinputstream bis = new Bytearrayinputstream (Bos.tobytearray ()); ObjectInputStream Ois ObjectInputStream (bis); return Ois.readobject ();} public string getString () {return string;} public void SetString (string string) {this.string = string;} Public Serializableobject Getobj () {return obj;} public void Setobj (Serializableobject obj) {this.obj = obj;}} Class Serializableobject implements Serializable {private static final long serialversionuid = 1L;}
To achieve deep replication, you need to read the binary input of the current object in the form of a stream, and then write out the corresponding object of the binary data.
Because the article length is longer, in order to better facilitate the reader to read, I will take the other introduction in another article (perhaps will be divided into two), thank you for your valuable comments and suggestions!
If you have any questions, please contact: Egg
Email:[email protected] Weibo: HTTP://WEIBO.COM/XTFGGEF
The design pattern of the beauty of Java [from rookie to master evolution]