1. Observer Pattern
Observer Pattern is a commonly used design pattern that defines a one-to-many dependency relationship that allows multiple observer objects to monitor a certain subject object at the same time. When a theme object changes, all its dependencies are notified and automatically updated.
In the observer pattern, there are two important roles, namely subject and observer. The topic maintains a list of observers and provides an interface for registering and deleting observers, as well as an interface for notifying observers. Observers define methods for receiving topic notifications so that the topic can notify observers when the state changes.
The advantage of using the observer pattern is that it can reduce the coupling between objects and make the interaction between objects more flexible. If we need to add a new observer, we only need to make it implement the Observer interface and register it in the topic. There is no need to modify the original code.
The observer pattern is widely used in actual development, such as the event listening mechanism in Java, the broadcast mechanism in Android, and so on.
Example
Below is an example of the Observer pattern with a news theme. |
Create the observer interface NewsPublisher and define the register, unregister and notifyObservers methods.
public interface NewsPublisher {
void register(NewsSubscriber subscriber);
void unregister(NewsSubscriber subscriber);
void notifyObservers(String news);
}
Create the observer interface NewsSubscriber and define the update method.
public interface NewsSubscriber {
void update(String news);
}
Create a news source class Newspaper that implements the NewsPublisher interface for publishing news.
import java.util.ArrayList;
import java.util.List;
public class Newspaper implements NewsPublisher {
private List<NewsSubscriber> subscribers = new ArrayList<>();
public void addNews(String news) {
System.out.println("Newspaper: " + news);
notifyObservers(news);
}
@Override
public void register(NewsSubscriber subscriber) {
subscribers.add(subscriber);
}
@Override
public void unregister(NewsSubscriber subscriber) {
subscribers.remove(subscriber);
}
@Override
public void notifyObservers(String news) {
for (NewsSubscriber subscriber : subscribers) {
subscriber.update(news);
}
}
}
Create multiple observer classes TVNewsSubscriber and WebNewsSubscriber to implement the NewsSubscriber interface for receiving news.
public class TVNewsSubscriber implements NewsSubscriber {
private String name;
public TVNewsSubscriber(String name) {
this.name = name;
}
@Override
public void update(String news) {
System.out.println("TV News Subscriber " + name + ": " + news);
}
}
public class WebNewsSubscriber implements NewsSubscriber {
private String name;
public WebNewsSubscriber(String name) {
this.name = name;
}
@Override
public void update(String news) {
System.out.println("Web News Subscriber " + name + ": " + news);
}
}
In the client program, instantiate Newspaper and add subscribers to it, and then call the addNews method to publish news.
package org.example.observer;
public class Test {
public static void main(String[] args) {
Newspaper newspaper = new Newspaper();
newspaper.register( new WebNewsSubscriber("张三"));
newspaper.register(new WebNewsSubscriber("李四"));
newspaper.register(new WebNewsSubscriber("王五"));
newspaper.addNews("1111");
}
}
Application scenarios
Java's observer pattern is mainly used in the following scenarios: |
life examples
The Observer pattern is a design pattern whose purpose is to establish a one-to-many dependency relationship between objects so that when the state of an object changes, all objects that depend on it can be notified and automatically updated. Here are some examples of the Observer pattern in life: |
2. Singleton mode
The singleton pattern is a common creational design pattern that restricts a class to create only one object instance. In the singleton pattern, only one object instance of a class exists, and this object can be accessed by all other objects in the system.
The singleton pattern is typically used when you need to control a resource, such as when creating a database connection, thread pool, or cache object. In these scenarios, the singleton pattern can ensure that only one object instance exists in the system, thus avoiding resource waste and conflicts.
The singleton pattern can be implemented in many different ways, including lazy style, hungry style, double-checked locking, static inner classes, enumerations, etc. Each implementation method has its advantages and disadvantages, and the appropriate implementation method needs to be selected based on specific demand scenarios.
1. Hungry-style singleton mode
The Hungry-style singleton mode is the simplest singleton mode, simple to implement and thread-safe. Instantiation is completed when the class is loaded, so it is very fast when called, but lazy loading cannot be achieved.
A private, immutable Singleton instance is created when the class is loaded. Only one instance will exist at any time, and there will be no concurrency, so there will be no thread safety issues. At the same time, since the instance is declared final , there will be no reordering problem when obtaining the instance , ensuring thread safety.
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
2. Lazy singleton mode (thread unsafe)
The lazy singleton mode is instantiated only when the getInstance() method is called, achieving lazy loading, but it is thread unsafe in a multi-threaded environment.
This singleton mode is unsafe in a multi-threaded environment because it does not consider thread safety. When multiple threads call the getInstance() method at the same time, multiple instances may be created.
For example, thread A and thread B call the getInstance() method at the same time. At this time, instance is null . When A executes instance = new Singleton();, since the initialization has not been completed, thread B will also enter the judgment of instance == null. , and a new instance will also be created. This will result in the creation of multiple instances.
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
In order to solve the thread insecurity problem of lazy singleton mode, thread safety can be achieved by adding the synchronized keyword to the getInstance() method . But this approach is inefficient because you need to acquire the lock every time you get an instance.
This code is thread-safe because the synchronized keyword is used on the getInstance() method, which ensures that only one thread can access this method at the same time. If multiple threads call the getInstance() method at the same time in a multi-threaded environment, only one thread can execute the line of code instance = new Singleton() due to the synchronized keyword, ensuring that only one instance is created.
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3. Double check lock singleton mode
The double-checked locking singleton mode implements lazy loading and thread safety through locking and double judgment.
This code uses the "double-check lock" mechanism, that is, the first time it checks whether the object instance exists, it will only obtain the lock and create the instance if it does not exist. Because the synchronized block uses the class object Singleton.class as the lock object, it is guaranteed that only one thread can enter and create an instance in the synchronized block. At the same time, the volatile keyword is used to ensure visibility in a multi-threaded environment. That is, if one thread modifies the value of instance, other threads can immediately see the change in this value, thereby avoiding dirty reads. Therefore, this code is thread-safe.
|
4. Static inner class singleton pattern
This method uses the static inner class of the class to implement the singleton mode, which has the advantage of thread safety. The static inner class of the class will only be loaded once when the class is loaded. At the same time, the static inner class will not be initialized when the singleton class is loaded. It will only be initialized when the method in the static inner class is called, and static initialization will only Executed once, thus ensuring thread safety. In addition, since SingletonHolder is a private static inner class, it cannot be accessed from the outside, which also ensures the uniqueness of the singleton.
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
5. Enumeration singleton mode
The enumeration singleton mode is a more concise implementation method. The enumeration constant will only be initialized once, so the singleton mode can be implemented.
这个实现方式是使用了 Java 的枚举类型来实现单例模式,而 Java 的枚举类型保证了在任何情况下都只会有一个实例。因此这种实现方式是线程安全的。枚举类型的实现方式是由 JVM 内部保证线程安全的。在 Java 中,枚举类型是天然的单例模式,且枚举类型在多线程环境下也是线程安全的。
public enum Singleton {
INSTANCE;
public Singleton2 getInstance() {
return Singleton2.getInstance();
}
}
说明
1. 私有构造函数`Singleton()`:防止外部直接实例化该类,仅允许通过`getInstance()`方法获取该类的唯一实例。 |
应用场景
Java单例模式通常在以下场景中使用: |
生活实例
Plaintext |