Skip to content

Commit 5f3b836

Browse files
authored
Merge pull request #2 from ahucoder/dev
The Observer Pattern
2 parents 0c47239 + c5932fe commit 5f3b836

File tree

11 files changed

+243
-7
lines changed

11 files changed

+243
-7
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
<artifactId>spring-boot-starter-web</artifactId>
2727
<version>3.4.5</version>
2828
</dependency>
29+
<dependency>
30+
<groupId>com.google.guava</groupId>
31+
<artifactId>guava</artifactId>
32+
<version>33.4.0-jre</version>
33+
</dependency>
2934
</dependencies>
3035

3136
</project>

src/main/java/observer/App.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package observer;
2+
3+
import observer.event.WeatherUpdateEvent;
4+
import observer.eventbus.EventBus;
5+
import observer.listener.User;
6+
import observer.publisher.WeatherStationPublisher;
7+
8+
public class App {
9+
10+
public static void main(String[] args) throws InterruptedException {
11+
EventBus eventBus = new EventBus();
12+
WeatherStationPublisher weatherStation = new WeatherStationPublisher(eventBus);
13+
14+
// create users
15+
User user1 = new User("Tom", System.out::println);
16+
User user2 = new User("Jerry", System.out::println);
17+
18+
// subscribe to weather update events
19+
eventBus.subscribe(WeatherUpdateEvent.class, user1);
20+
eventBus.subscribe(WeatherUpdateEvent.class, user2);
21+
22+
// activate the weather station
23+
weatherStation.start();
24+
System.out.println("The weather broadcast system has been activated");
25+
26+
// stop after running for a period of time
27+
Thread.sleep(6100);
28+
eventBus.unsubscribe(WeatherUpdateEvent.class, user1);
29+
System.out.println("user1 has been unsubscribed");
30+
Thread.sleep(6100);
31+
weatherStation.stop();
32+
33+
System.out.println("The weather broadcast system has been turned off");
34+
}
35+
36+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package observer.event;
2+
3+
import java.text.SimpleDateFormat;
4+
import java.util.Date;
5+
import java.util.Objects;
6+
7+
public abstract class BaseEvent<T> implements Event<T> {
8+
private final long timestamp;
9+
private final T source;
10+
private static final SimpleDateFormat DATE_FORMAT =
11+
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
12+
13+
protected BaseEvent(T source) {
14+
this.timestamp = System.currentTimeMillis();
15+
this.source = Objects.requireNonNull(source);
16+
}
17+
18+
@Override
19+
public long timestamp() {
20+
return timestamp;
21+
}
22+
23+
@Override
24+
public T source() {
25+
return source;
26+
}
27+
28+
public String formattedTimestamp() {
29+
return DATE_FORMAT.format(new Date(timestamp));
30+
}
31+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package observer.event;
2+
3+
public interface Event<T> {
4+
long timestamp();
5+
6+
T source();
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package observer.event;
2+
3+
public class WeatherUpdateEvent extends BaseEvent<String> {
4+
public WeatherUpdateEvent(String source) {
5+
super(source);
6+
}
7+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package observer.eventbus;
2+
3+
import com.google.common.collect.Lists;
4+
import com.google.common.collect.Maps;
5+
import observer.event.Event;
6+
import observer.listener.EventListener;
7+
8+
import java.util.List;
9+
import java.util.Map;
10+
11+
/**
12+
* Event bus, responsible for event publishing and subscription
13+
*/
14+
public class EventBus {
15+
16+
private final Map<Class<?>, List<EventListener<?>>> listenerMap = Maps.newConcurrentMap();
17+
18+
/**
19+
* subscribe event
20+
*
21+
* @param eventClass event type
22+
* @param eventListener event listener
23+
* @param <E> event generics
24+
*/
25+
public <E extends Event<?>> void subscribe(Class<E> eventClass, EventListener<E> eventListener) {
26+
listenerMap.computeIfAbsent(eventClass, v -> Lists.newCopyOnWriteArrayList()).add(eventListener);
27+
}
28+
29+
/**
30+
* cancel
31+
*
32+
* @param eventClass event type
33+
* @param eventListener event listener
34+
* @param <E> event generics
35+
*/
36+
public <E extends Event<?>> void unsubscribe(Class<E> eventClass, EventListener<E> eventListener) {
37+
List<EventListener<?>> eventListeners = listenerMap.get(eventClass);
38+
if (eventListeners != null) {
39+
eventListeners.remove(eventListener);
40+
}
41+
}
42+
43+
/**
44+
* publish event
45+
*
46+
* @param event event
47+
* @param <E> event generics
48+
*/
49+
@SuppressWarnings("unchecked")
50+
public <E extends Event<?>> void publish(E event) {
51+
List<EventListener<?>> eventListeners = listenerMap.get(event.getClass());
52+
if (eventListeners != null) {
53+
eventListeners.forEach(eventListener -> ((EventListener<E>) eventListener).onEvent(event));
54+
}
55+
}
56+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package observer.listener;
2+
3+
import observer.event.Event;
4+
5+
@FunctionalInterface
6+
public interface EventListener<T extends Event<?>> {
7+
void onEvent(T event);
8+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package observer.listener;
2+
3+
import observer.event.WeatherUpdateEvent;
4+
5+
import java.util.function.Consumer;
6+
7+
public class User implements EventListener<WeatherUpdateEvent> {
8+
private final String name;
9+
private final Consumer<String> consumer;
10+
11+
public User(String name, Consumer<String> consumer) {
12+
this.name = name;
13+
this.consumer = consumer;
14+
}
15+
16+
@Override
17+
public void onEvent(WeatherUpdateEvent event) {
18+
String message = String.format("[%s] received weather update event: %s, current time is %s", name, event.source(), event.formattedTimestamp());
19+
consumer.accept(message);
20+
}
21+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package observer.publisher;
2+
3+
import observer.event.Event;
4+
import observer.eventbus.EventBus;
5+
6+
import java.util.Objects;
7+
8+
public abstract class Publisher<E extends Event<?>> {
9+
private final EventBus eventBus;
10+
private final int intervalMillis;
11+
private volatile boolean running;
12+
private Thread workerThread;
13+
14+
public Publisher(EventBus eventBus, int intervalMillis) {
15+
this.eventBus = Objects.requireNonNull(eventBus);
16+
this.intervalMillis = intervalMillis;
17+
}
18+
19+
protected abstract E getEventInfo();
20+
21+
public final void start() {
22+
if (running) {
23+
return;
24+
}
25+
running = true;
26+
workerThread = new Thread(() -> {
27+
try {
28+
while (running) {
29+
E event = getEventInfo();
30+
if (event != null) {
31+
eventBus.publish(event);
32+
Thread.sleep(intervalMillis);
33+
}
34+
}
35+
} catch (InterruptedException e) {
36+
Thread.currentThread().interrupt();
37+
}
38+
});
39+
workerThread.start();
40+
}
41+
42+
public final void stop() {
43+
running = false;
44+
if (workerThread != null) {
45+
workerThread.interrupt();
46+
}
47+
}
48+
49+
public final boolean isRunning() {
50+
return running;
51+
}
52+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package observer.publisher;
2+
3+
import observer.event.WeatherUpdateEvent;
4+
import observer.eventbus.EventBus;
5+
6+
import java.util.Random;
7+
8+
public class WeatherStationPublisher extends Publisher<WeatherUpdateEvent> {
9+
private final Random random = new Random();
10+
11+
public WeatherStationPublisher(EventBus eventBus) {
12+
super(eventBus, 3000);
13+
}
14+
15+
@Override
16+
protected WeatherUpdateEvent getEventInfo() {
17+
String info = random.nextBoolean() ? "Sunny Day" : "Rain Day";
18+
return new WeatherUpdateEvent(info);
19+
}
20+
}

0 commit comments

Comments
 (0)