What is mode?
A good person in Go knows that good "shapes" are very important to go. Form is the abstraction of the geometric shape of a piece on the board. Pattern is also the key for the human brain to grasp and understand the outside world. The human brain is also very powerful in processing models. It is an example that people can recognize familiar faces in hundreds of faces.
Simply put, when we deal with a large number of problems, we repeat one of the many different problems, which allows us to use one method to describe the essence of the problem and use it in essence, but the details will never be solved through repeated methods. This nature is called pattern. The model process abstracts questions. After ignoring unimportant details, it discovers the general value of the problem and finds a commonly used method to solve the problem.
The discovery pattern occurs at the same time as the research pattern, and it is not easy to find a new pattern. A good mode must meet the following requirements:
1. It can solve the problem. Instead of reflecting the problem, you must propose a solution to the problem.
2. the proposed solution is correct and not obvious.
3. It must be something that involves the deep structure of the software system, not just a description of an existing module.
4. It must satisfy the human aesthetic and be concise and beautiful.
In other words, a wonderful thing is not necessarily a pattern, but a pattern must be a wonderful thing.
All aspects of software engineering, such as development organization, software processing, project configuration management, and so on, can see the shadows of the pattern. But so far, the best research is the design model and organization model. In software programming, the use of the Pattern Method is valued after the program object. The Research on pattern methods in software programming started in 1990s.
The groundbreaking work on the use of modeling methods in Object-Oriented Programming is
Design Patterns-elements of reusable object-oriented software, E. Gamma, R. Helm, R. Johnson, and J. vlissides, 1995, Addison-Wesley.
These four authors are generally referred to as Gang of Four or gof ). (After the word appeared, many Western commercial hypes used the word "Lu" to make money. In 1980s, a four-person U.S. band named the team. Several small gangs have been called the Gang of Four in British political circles. Here, we use this term to refer to the four famous authors, with an element of abuse .)
Due to the features of the Java language, the implementation of the model in the Java language has its own characteristics. Java is the most popular pure OOP programming language today. The average quality of programmers who use Java programming is also relatively high. These programmers often do not meet the requirements of implementing program functions. They often want to keep forging ahead and improving themselves in the code structure, programming style, and even the way they think about solving problems. Mode is to summarize and theoretically optimize the code structure, programming style, and the way of thinking to solve the problem in a large number of practices. Understanding and mastering models is a good way for Java programmers to improve their own quality.
In my study and work, I summarize my experience to improve communication with readers.
The author will use a simple UML (Unified Modeling Language, Unified Modelling wide Ge) later ). Since there are many books on UML on the market, and the UML used by the author in the future is extremely simple, it is only a very simple introduction here, the purpose is to enable readers who have never been familiar with UML to understand what is described later.
Figure 1. UML class diagram example |
As shown in the class diagram in figure 1, the class box is divided into four layers: Class Name, variable list, function list, and attribute list. If the variable name is a positive character, it indicates that the class is real (concrete, that is, the class that can be instantiated). If the variable name is italic, it indicates that the class is abstract. Obviously, we provide a real class in the figure.
In class classuml in Figure 1, if a variable or function (method) has a plus (+) on the left, it indicates that it is public. If there is a minus (-) on the left (-) it indicates that it is private, and if there is a well (#) on the left, it indicates it is protected.
An attribute is a structure composed of an internal variable, a mutator, and an accessor.
In the upper-right corner of the box of the class, the parent class and the implemented interface of the class are usually written in two rows. You will see examples later.
There is a line between classes to indicate the relationship between them. Promotion (opposite to inheritance), dependency, accumulation, and association can occur between classes. The author will explain the examples later.
Package com. javapatterns. Singleton. Demos; Public class classuml { Public classuml (){}Private void aprivatefunction (){} Public void apublicmethod (){} Public int getaproperty () {return aprivatevar ;} Public void setaproperty (INT aprivatevar) {This. aprivatevar = aprivatevar ;} Static public void astaticmethod (){} Protected void aprotectedmethod (){} Private int aprivatevar; Public int apublicvar; Protected int aprotectedvar; } |
Code List 1. source code of the classuml class.
What is the founding model?
Creational patterns is the pattern used by classes during instantiation. When some systems create objects, they need to dynamically decide how to create objects and what objects to create. The founding model tells us how to construct and package these dynamic decisions. The generative model usually includes the following models:
1. factory function mode
2. Abstract Factory Model
3. Builder Mode
4. Original Model Mode
5. Single-state mode
Single Mode
A single-State class can have only one instance. Such classes are often used for resource management.
Resources to be managed include external software resources. For example, each computer may have several printers, but only one print processor software can be available. Each computer may have several fax cards, but only one fax software should manage the fax. Each computer can have several communication ports. Your software should manage these communication ports in a centralized manner to prevent a communication port from being called by both requests at the same time.
Resources to be managed include internal software resources. For example, most software has one or more properties files to store system configurations. Such a system should have an object to manage an attribute file. Many software have databases. Generally, the whole software should use a connection channel, instead of opening a new connection channel as needed.
Internal software resources to be managed include components that are responsible for recording the number of visitors to the website, components that record internal events of the software system, components with error information, or components for monitoring system performance. These components must be centrally managed and cannot be managed by multiple leaders.
Features of single-State classes
In general,
1. A single-State class can have only one instance.
2. It must create its own unique instance.
3. It must provide its own instance to all other classes.
Finally, in theory and practice, single-State classes are not limited to "one" instance, but can be easily promoted to any limited instance.
Several implementations of single-State Mode
Thanks to the features of the Java language, the implementation of the single-State mode in the Java language has its own characteristics. These features are mainly manifested in how to instantiate them.
Hungry Chinese single-State type
The hungry Chinese single-State class is the simplest single-State class in Java.
Figure 2. UML class diagram of the hungry Chinese single-State Class |
The link line in the figure indicates that this class will be self-instantiated.
Package com. javapatterns. Singleton. Demos; Public class eagersingleton { Private eagersingleton (){}Public static eagersingleton getinstance (){ Return m_instance; } Private Static final eagersingleton m_instance = new eagersingleton (); } |
Code List 2. Hungry Chinese single-State class.
It is worth noting that this class cannot be inherited because the constructor is private.
Lazy single-State Class
The lazy single-State class instantiates itself when it is referenced for the first time. If the loader is static, it will not instantiate itself when the lazy single-State class is loaded.
Package com. javapatterns. Singleton. Demos; Public class lazysingleton { Private lazysingleton (){}Public static lazysingleton getinstance () { If (m_instance = NULL) { File: // more than one threads might be here !!! Synchronized (lazysingleton. Class) { If (m_instance = NULL) { M_instance = new lazysingleton (); } } } Return m_instance; } Private Static lazysingleton m_instance = NULL; } |
Code List 3. Lazy single-State classes.
Figure 3. Lazy single-State classes |
The link line in the figure indicates that this class will be self-instantiated.
Readers may notice that the lazy single-State class implementation above uses the well-known dual-check principle that is often used in multi-threaded programming. Readers who are not familiar with the dual-check principle and multi-thread programming points can take a look at the questions and answers provided later.
Similarly, this class cannot be inherited because the constructor is private.
The hungry Chinese single-State class instantiates itself when it is loaded. The loader is static and will still instantiate itself when the hungry Chinese single-State class is loaded. From the perspective of resource utilization efficiency, this is slightly worse than the lazy single-State class. In terms of speed and response time, it is better than the lazy single-State class. However, the lazy single-State class must handle the access restrictions of key segments in the instantiated function when multiple threads reference this class for the first time. In particular, when a single-State class acts as a resource controller, Resource Initialization is required during instantiation, and Resource Initialization is likely to take time. This means that the probability of using this class for the first time when multithreading occurs increases.
The hungry Chinese single-State class can be implemented in Java, but it is not easy to implement in C ++, because static initialization does not have a fixed sequence in C ++, therefore, initialization of static m_instance variables and loading sequence of classes are not guaranteed, and problems may occur. This is why the example is lazy when gof introduces the concept of single-State classes. Their books have a huge impact, so that the single-State examples in Java are mostly lazy. In fact, the authors believe that the hungry Chinese single-State class is more in line with the characteristics of the Java language itself.
Registered single-State type
The registered single-State class is designed by gof to overcome the disadvantages that cannot be inherited by both the hungry and Chinese single-State classes. The author translated their example into the Java language and turned the self-instantiated method from lazy to hungry. However, its subclass instantiation method can only be lazy, which cannot be changed.
Figure 4. An example of a registered single-State Class |
The link line in the figure indicates that this class will be self-instantiated.
Package com. javapatterns. Singleton. Demos; Import java. util. hashmap;Public class regsingleton { Protected regsingleton (){} Static public regsingleton getinstance (string name) { If (name = NULL) { Name = "com. javapatterns. Singleton. demos. regsingleton "; } If (m_registry.get (name) = NULL) { Try { M_registry.put (name, class. forname (name). newinstance ()); } Catch (exception E) { System. Out. println ("error happened ."); } } Return (regsingleton) (m_registry.get (name )); } Static private hashmap m_registry = new hashmap (); Static { Regsingleton x = new regsingleton (); M_registry.put (X. getclass (). getname (), X ); } Public String about () { Return "Hello, I am regsingleton ."; } } |
Code list 4. register a single-State class. (For simplicity, the multi-thread access restriction is not considered here. You can add a dual-check access restriction on your own)
Its subclass
Figure 5. An example of the Register Type Single-state subclass. The link line in the figure shows that the class is self-instantiated by the parent class. |
Package com. javapatterns. Singleton. Demos; Import java. util. hashmap; Public class regsingletonchild extends regsingleton { Public regsingletonchild (){}Static public regsingletonchild getinstance () { Return (regsingletonchild) regsingleton. getinstance ( "Com. javapatterns. Singleton. demos. regsingletonchild "); } Public String about () { Return "Hello, I am regsingletonchild ."; } } |
Code List 5. subclasses of the registered single-State class.
In the original gof example, there is no getinstance () method. In this case, the subclass must call the getinstance (string name) method of the text class and input the subclass name, which is inconvenient. The author adds the getinstance () method to the example of the Register Type Single-state class subclass. The advantage of this is that regsingletonchild can return its own instance through this method, however, due to different data types, regsingleton cannot provide such a method.
The subclass must have a parent class to construct a sub-call to generate an instance. Therefore, the sub-class must be public. In this way, the instance is allowed to be generated in this way, rather than the registration of the parent class. This is a disadvantage of the registration Type Single-state class.
Gof once pointed out that the parent class instance must exist to have a subclass instance, which is a waste in some cases. This is another disadvantage of the registration Type Single-state class.
Garbage collection in Java language
Java garbage collection makes the use of single-State classes a little complicated. The reason is that the classes added to jdk1.1 are automatically cleared. This type of garbage collection will clear the class itself, not just the object! In fact, jdk1.1 can even clear some system classes!
In jdk1.0.x, the automatic clearing of classes has not been added.
In jdk1.2 and later versions, shengyang has tightened its class garbage collection rules, which stipulates that all classes loaded through local and system class loaders will never be recycled. In addition, classes loaded by other class loaders can be recycled only after the loaders are recycled by themselves.
Readers who use single-State classes in JDK 1.1 may encounter a strange problem of class disappearance if they do not understand the features of the Java language. To enable your single-State class to be used in all versions of the Java environment, the author provides a "guard" class program, which can ensure that your single-State class, or even any other object, once handed over to the "guard" object, it will not be inexplicably recycled by the garbage collector until you release it from "guard.
Figure 6. An example of the "guard" Class |
Package com. javapatterns. Singleton. Demos; Import java. util. vector;/** * This class keeps your objects from garbage collected */ Public class objectkeeper extends thread { Private objectkeeper () { New thread (this). Start (); } Public void run () { Try {join ();} Catch (interruptedexception e ){} } /** * Any object passed here will be kept until you call discardobject () */ Public static void keepobject (Object myobject) { System. Out. println ("Total number of kept objects:" + M_keptobjects.size ()); M_keptobjects.add (myobject ); System. Out. println ("Total number of kept objects:" + M_keptobjects.size ()); } /** * This method will remove the protect of the object you pass in and make it * Available for Garbage Collector to collect. */ Public static void discardobject (Object myobject) { System. Out. println ("Total number of kept objects:" + M_keptobjects.size ()); M_keptobjects.remove (myobject ); System. Out. println ("Total number of kept objects:" + M_keptobjects.size ()); } Private Static objectkeeper m_keeper = new objectkeeper (); Private Static vector m_keptobjects = new vector (); } |
Code List 6. An implementation of the guard class.
The guard class should be self-instantiated, and only one instance is needed in each system. This means that the guard class should be a single-State class. Of course, the disappearance of a class cannot happen to itself. The examples provided by the author meet all requirements.
A practical example
Here, the author provides a single-State class for reading properties files, as a practical example of a single-State class. Attribute files are similar to the. ini files used in Windows programming. They belong to the system's "resource". Reading attribute files is resource management, and it is clear that a single-State class is responsible.
Figure 7. UML in this example |
Obviously, attribute File Reading is involved in most systems, so this example is very useful. In this example, the author assumes that the attribute file to be read is in the current directory and is named Singleton. properties. This file contains the following attributes:
Node1.item1 = How Node1.item2 = are Node2.item1 = You Node2.item2 = doing Node3.item1 =? |
Code List 7. Attribute File Content
The source code of this example is as follows:
Package com. javapatterns. Singleton. Demos; Import java. util. properties; Import java. Io. fileinputstream; Import java. Io. file;Public class configmanager { /** * A private constructor is used to ensure the uniqueness of Instantiation. */ Private configmanager () { M_file = new file (pfile ); M_lastmodifiedtime = m_file.lastmodified (); If (m_lastmodifiedtime = 0) { System. Err. println (pfile + "file does not exist! "); } M_props = new properties (); Try { M_props.load (New fileinputstream (pfile )); } Catch (exception E) { E. printstacktrace (); } } /** * * @ Return returns a single instance of the configmanager class. */ Synchronized public static configmanager getinstance () { Return m_instance; } /** * Read a specific attribute item * * @ Param name: name of the attribute item * @ Param defaultval: Default Value of the attribute item * @ Return refers to the value of the property item (such an item exists) and the default value (such an item does not exist) */ Final public object getconfigitem (string name, object defaultval) { Long newtime = m_file.lastmodified (); // Check whether the property file has been modified by other programs (in most cases, the programmer manually. // If yes, read the file again. If (newtime = 0) { // The property file does not exist. If (m_lastmodifiedtime = 0) { System. Err. println (pfile + "file does not exist! "); } Else { System. Err. println (pfile + "file was deleted !! "); } Return defaultval; } Else if (newtime> m_lastmodifiedtime) { M_props.clear (); // get rid of the old properties Try { M_props.load (New fileinputstream (pfile )); } Catch (exception E) { E. printstacktrace (); } } M_lastmodifiedtime = newtime; Object val = m_props.getproperty (name ); If (val = NULL) { Return defaultval; } Else { Return val; } } /** * Full name of the property File */ Private Static final string pfile = system. getproperty ("user. dir ") + "/Singleton. properties "; /** * File object variables corresponding to attribute files */ Private file m_file = NULL; /** * Last modification date of the property File */ Private long m_lastmodifiedtime = 0; /** * Attribute object variables corresponding to the property File */ Private Properties m_props = NULL; /** * This class may have a unique instance. */ Private Static configmanager m_instance = new configmanager (); } |
Code List 8. Source Code of configman.
Obviously, the author uses the hungry Chinese implementation method to avoid the trouble of processing multi-threaded access. In the source code below, the author demonstrates how to use the guard class to "guard" and "release" configman classes, and how to call configman to read attribute files.
Objectkeeper. keepobject (configmanager. getinstance ()); Bufferedreader reader = new bufferedreader (New inputstreamreader (system. In )); System. Out. println ("type quit to quit "); Do { System. Out. Print ("property item to read :"); String line = reader. Readline (); If (line. Equals ("quit ")) { Break; } System. Out. println (configmanager. getinstance (). getconfigitem (line, "Not found .")); } While (true );Objectkeeper. discardobject (configmanager. getinstance ()); |
Code List 8. Source Code of configman.
Obviously, the author uses the hungry Chinese implementation method to avoid the trouble of processing multi-threaded access. In the source code below, the author demonstrates how to use the guard class to "guard" and "release" configman classes, and how to call configman to read attribute files.
Objectkeeper. keepobject (configmanager. getinstance ()); Bufferedreader reader = new bufferedreader (New inputstreamreader (system. In )); System. Out. println ("type quit to quit "); Do { System. Out. Print ("property item to read :"); String line = reader. Readline (); If (line. Equals ("quit ")) { Break; } System. Out. println (configmanager. getinstance (). getconfigitem (Line, "Not found .")); } While (true ); Objectkeeper. discardobject (configmanager. getinstance ()); |
Code List 9. How to call the configman class to read attribute files and call the guard class to guard and release the configman class
The following figure shows the running status of the above Code.
Figure 8. Code running status |
Q &
1. Why should I create a class instead of using a static "full course" variable? Of course, a static variable can only have one value, so naturally it is not "single state?
2. In the lazysingleton example, what if I move the restricted access keyword from lazysingleton. class to the declaration statement of the getinstance () method?
Package com. javapatterns. Singleton. Demos; Public class lazysingleton { Private lazysingleton (){}Synchronized static public lazysingleton getinstance () { If (m_instance = NULL) { M_instance = new lazysingleton (); } Return m_instance; } Private Static lazysingleton m_instance = NULL; } |
Code List 10. Lazy single-State class variants.
3. In the lazysingleton example, two-layer check if (m_instance = NULL) appears ). Is this necessary? If you remove the inner layer check, will the problem occur?
4. As above, if the outer check is removed, will there be problems?
5. The example shows how to call the eagersingleton class.
6. The example shows how to call the regsingleton class and regsingletonchild class.
7. What data types can be selected for the m_keptobjects variable in the guard class to make the program occupy less space?
8. Try to use the instance generation time, the instance identityhashcode, the class loader, the total number of instances, And the instances' ordinal number to determine whether a single State is indeed a single state.
Q & A answers
1. A variable cannot be initialized by itself and cannot have an inherited relationship. There is no real "full" variable in the Java language. A variable must belong to a class. In a complex program, it is often difficult to determine where a static variable initialization occurs. Of course, there are no errors when using variables. It is better to use Fortran rather than Java programming.
2. There will be no errors, but the efficiency is not good. In the original source code, the synchronized action takes effect only when this method is called for the first time and will not be encountered in future calls. In this case, the restriction of synchronized will be applied to any progress. This is tantamount to creating an unnecessary growth bridge, which is very stupid.
3. This will definitely cause problems. When getinstance () is called for the first time, multiple threads may almost arrive at the same time. Only one thread can arrive within the inner layer check. Other threads will wait at the synchronized () statement. In this way, after the first thread completes instantiation, other threads waiting for the synchronized () statement will be authorized to enter the synchronized () statement one by one. If there is no second check, they will try to instantiate one by one, and this is wrong.
4. There will be no problems in this way, but the efficiency is not good and it is very stupid. The principle is similar to the first question.
5,
Package com. javapatterns. Singleton. Demos; Public class regsingletontest { Public static void main (string [] ARGs) { File: // (1) test eager System. Out. println (eagersingleton. getinstance ());File: // (2) test Reg System. Out. println ( Regsingleton. getinstance ( "Com. javapatterns. Singleton. demos. regsingleton"). About ()); System. Out. println (regsingleton. getinstance (null). About ()); System. Out. println ( Regsingleton. getinstance ( "Com. javapatterns. Singleton. demos. regsingletonchild"). About ()); System. Out. println (regsingletonchild. getinstance (). About ()); } } |