Github source address
An overview of 23 design patterns
- Java Language Design – An overview of 23 design patterns
Creation pattern
- Factory Method Pattern
- Abstract Factory Pattern
- Builder Mode
- Prototype mode
- Singleton
Structural mode
- Facade Pattern
- Adapter mode (Adapter)
- Proxy mode
- Composite mode
- Flyweight Mode
- Decorator pattern
- Bridge mode (Bridge)
Behavioral pattern
- Mediator Mode
- Observer Model
- Command mode
- Iterator pattern (Iterator)
- Template Method
- Strategy Pattern
- State mode
- Memento Mode
- Interpreter mode
- Chain of Responsibility model
- Visitor Pattern
define
Support a large number of fine-grained objects efficiently using sharing techniques
Usage scenarios
- A large number of similar objects exist in the system
- Fine-grained objects have close external state, and internal state is context-independent, that is, the object has no specific identity
- Scenarios where buffer pools are required
When multiple places in the system need the same set of information, you can encapsulate the information into an object and cache the object. In this way, one object can be provided to multiple places, avoiding multiple creation of the same object and consuming a large amount of memory space.
In fact, the share mode is an improved mechanism of the factory mode. The share mode also requires the creation of an object or a group of objects, and it is through the factory method to generate objects, but the share mode for the factory method to increase the cache function.
Mainly summarized as the following application scenarios:
- It is often applied to the development of the bottom layer of the system to solve the performance problems of the system.
- Scenarios where the system has a large number of similar objects and requires a buffer pool.
In daily life, yuan sharing mode is also common, such as the housing resource sharing of various intermediary agencies, and the national social security network.
The advantages and disadvantages
- Advantages: Greatly reduces the number of objects in memory, improves performance and reduces memory
- Disadvantages: To make objects shareable, some state needs to be externalized, complicating the program logic, and reading external state makes the run time slightly longer
Roles involved
- Abstract Flyweight: A Flyweight object abstracts the base class or interface, defining both the external state of the object and the interface or implementation of its internal state.
- ConcreteFlyweight: A business that implements abstract role definitions. The role’s internal state handling should be context-specific, and there should not be an operation that changes the internal state and modifies the external state.
- FlyweightFactory: Manages the pool of weightobjects and creates weightobjects.
The specific implementation
Now it is required to simulate a pseudo-code query train ticket, which can check the relevant ticket information through the departure station and destination station. Train information includes: departure station, destination station, price, seat type.
public interface ITicket {
void showInfo(String bunk);
}
Copy the code
public class TrainTicket implements ITicket {
private String from;
private String to;
private int price;
public TrainTicket(String from, String to) {
this.from = from;
this.to = to;
this.price = new Random().nextInt(600);
}
@Override
public void showInfo(String bunk) {
System.out.println(String.format("% S ->%s: %s Price: %s $.this.from, this.to, bunk, this.price)); }}Copy the code
Finally create the TicketFactory class:
public class TicketFactory {
public static ITicket queryTicket(String from, String to) {
return newTrainTicket(from, to); }}Copy the code
Write client code:
public class Test {
public static void main(String[] args) {
ITicket ticket = TicketFactory.queryTicket("Hangzhou"."Beijing");
ticket.showInfo("Train"); }}Copy the code
By analyzing the above code, we find that when the client queries, the system directly creates a train ticket object through TicketFactory. However, when a large number of users request information about the same ticket at a certain moment, the system will create a large number of such train ticket objects, resulting in a sudden increase in the system memory pressure. In fact, it is better to cache this ticket object and reuse it for other query requests, so that an object can meet the requirements to support thousands of query requests, and there is no pressure on memory. Using the share mode can be a good solution to this problem. We continue to optimize the code by making changes in the TicketFactory class to add a caching mechanism:
public class TicketFactory {
private static Map<String, ITicket> ticketPool = new ConcurrentHashMap<>();
public static ITicket queryTicket(String from, String to) {
String key = from + "- >" + to;
if (ticketPool.containsKey(key)) {
System.out.println("Use cache:" + key);
return ticketPool.get(key);
}
System.out.println("First query, create object:" + key);
ITicket ticket = new TrainTicket(from, to);
ticketPool.put(key, ticket);
returnticket; }}Copy the code
Modify the client code:
public class Test {
public static void main(String[] args) {
ITicket ticket = TicketFactory.queryTicket("Hangzhou"."Beijing");
ticket.showInfo("Train");
ticket = TicketFactory.queryTicket("Hangzhou"."Beijing");
ticket.showInfo("Train");
ticket = TicketFactory.queryTicket("Hangzhou"."Beijing");
ticket.showInfo("Train"); }}Copy the code
The running results are as follows:
Hangzhou -> Beijing hangzhou -> Beijing: Bullet train price:581Yuan use cache: Hangzhou -> Beijing Hangzhou -> Beijing: Bullet train price:581Yuan use cache: Hangzhou -> Beijing Hangzhou -> Beijing: Bullet train price:581yuanCopy the code
As can be seen, except for the first query to create an object, the subsequent query of the same train ticket information is using the cache object, no need to create a new object.
For example, we often use the database Connection pool, because the main performance consumption when we use the Connection object is to establish and close the Connection. In order to improve the performance of the Connection in the call, we create the cache of the Connection object before the call, and use the value from the cache. Use it and then put it back to achieve the purpose of resource reuse.
The share mode in String
Java defines the String class as final (immutable). JVM strings are usually stored in the String constant pool. Java ensures that there is only one copy of a String in the constant pool, which was in the constant pool before JDK6.0. The JVM takes it out of the permanent generation and places it in the heap.
Let’s take a test:
public class StringTest {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
String s3 = "he" + "llo";
String s4 = "hel" + new String("lo");
String s5 = new String("hello");
String s6 = s5.intern();
String s7 = "h";
String s8 = "ello";
String s9 = s7 + s8;
System.out.println(s1 == s2);//true
System.out.println(s1 == s3);//true
System.out.println(s1 == s4);//false
System.out.println(s1 == s5);//false
System.out.println(s1 == s6);//true
System.out.println(s4 == s5);//false
System.out.println(s1 == s9);//false}}Copy the code
The String class is final, and when a String variable is created as a literal, the JVM puts that literal “hello” into the String constant pool at compile time, which is loaded into memory when the Java program is started. The character of this string constant pool is that there is one and only one copy of the same literal. If there are other identical literals, the JVM returns a reference to that literal. If there are no identical literals, the literal is created in the string constant pool and returns a reference to it.
Since the literal “hello” to which S2 refers already exists in the constant pool (s1 precedes S2), the JVM returns a reference to the literal binding, so s1==s2.
The concatenation of s3 literals is simply “hello”, and the JVM optimizes it at compile time, so s1 and S3 are equal.
New String(“lo”) in S4 generates two objects, lo, new String(“lo”), lo in the String constant pool, new String(“lo”) in the heap, String s4 = “hel” + new String(“lo”) is essentially the addition of two objects, the compiler does not optimize, the result of the addition is stored in the heap, while s1 is stored in the String constant pool, of course not equal. S1 is equal to s9, same thing.
S4 ==s5 the sum of the two is both in the heap and, needless to say, definitely not equal.
S1 = = s6, s5. Intern () method can be a heap in the string at run time dynamic join the string constant pool (string constant pool is the content of the program start was loaded ok), if the string constants in the pool of the object literal, it returns the literal string constant pool in the reference, otherwise, Creates a copy of the literal into the string constant pool and returns a reference to it. So s1==s6 prints true.