Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Ishan Soni
Ishan Soni

Posted on

The Observer design pattern

The Observer design pattern is also called Publish-Subscribe pattern and is the core principle of the Reactive paradigm (PUSH in favor of PULL).

Design Considerations

There are manyobservers (subscribers that are interested in some data) that are observing apublisher (which maintains that data). Observers register themselves to the publisher. The publisher can then push data to these observers, since it maintains a list of subscribed observers (PUSH!), whenever there is a change in the publisher’s state.

This pattern defines a one-to-many dependency between objects such that when the state of one object changes, all its dependents are notified and updated automatically.

Example: You are working on the user microservice. When a user is created, the following activities have to be performed:

  1. Send out a welcome email to the user.
  2. Generate and send a verification token to this user using which they can verify their account.

One way of structuring our code would be:

public void createUser(CreateUserCommand command) {    //omitted - code to create and save a user    sendWelcomeEmail();    generateAndSendVerificationToken();}
Enter fullscreen modeExit fullscreen mode

This violates both theS andO of theSOLID principles. Your UserService is doing too much work and is not extensible. Let's assume, as part of the user creation process, we now need to create an Avatar for the user as well. Your code would then become:

public void createUser(CreateUserCommand command) {    //omitted - code to create and save a user    sendWelcomeEmail();    generateAndSendVerificationToken();    generateAvatar();}
Enter fullscreen modeExit fullscreen mode

Instead, let's try to use the Observer design pattern

The data

public record UserCreated(String userId, String email, String firstName, String lastName) {}
Enter fullscreen modeExit fullscreen mode

The Publisher interface

public interface Publisher<T> {    void subscribe(Subscriber<T> subscriber);    void unsubscribe(Subscriber<T> subscriber);    void publish(T data);}
Enter fullscreen modeExit fullscreen mode

The Subscriber interface

public interface Subscriber<T> {    void next(T data);}
Enter fullscreen modeExit fullscreen mode

Let's create the concrete Publisher and Subscribers

public class UserPublisher implements Publisher<UserCreated> {    private final List<Subscriber<UserCreated>> subscribers = new ArrayList<>();    @Override    public void subscribe(Subscriber<UserCreated> subscriber) {        subscribers.add(subscriber);    }    @Override    public void unsubscribe(Subscriber<UserCreated> subscriber) {        subscribers.remove(subscriber);    }    @Override    public void publish(UserCreated data) {        subscribers.forEach(s -> s.next(data));    }}
Enter fullscreen modeExit fullscreen mode

public class UserCreatedNotifier implements Subscriber<UserCreated>{    @Override    public void next(UserCreated data) {        System.out.println("Sending email to " + data.email());    }}
Enter fullscreen modeExit fullscreen mode

public class VerificationTokenGenerator implements Subscriber<UserCreated> {    @Override    public void next(UserCreated data) {        System.out.println("Generating verification token and sending to " + data.email());    }}
Enter fullscreen modeExit fullscreen mode

Let's test it out

public void createUser(CreateUserCommand command) {    //... User creation code    UserPublisher userPublisher = new UserPublisher();    UserCreatedNotifier userCreatedNotifier = new UserCreatedNotifier();    VerificationTokenGenerator verificationTokenGenerator = new VerificationTokenGenerator();    userPublisher.subscribe(userCreatedNotifier);    userPublisher.subscribe(verificationTokenGenerator);    userPublisher.publish(new UserCreated("user-1", "user1@gmail.com", "User", "1"));}
Enter fullscreen modeExit fullscreen mode

Output

Now, let's work on the Avatar generation process and see how extensible this design pattern is:

public class AvatarGenerator implements Subscriber<UserCreated> {    @Override    public void next(UserCreated data) {        System.out.println("Generating avatar for user " + data.email());    }}
Enter fullscreen modeExit fullscreen mode

//same code as before    AvatarGenerator avatarGenerator = new AvatarGenerator();userPublisher.subscribe(avatarGenerator);
Enter fullscreen modeExit fullscreen mode

Output

Advantages

Loose Coupling: When two objects are loosely coupled, they can interact but have a very little knowledge of each other. The observer design pattern provides an object design where the publisher and subscribers are loosely coupled:
The only thing a publisher knows about a subscriber is that it implements the subscriber interface. We can add new/remove subscribers at any time since the only thing the publisher depends on is a list of objects that implement the subscriber interface.

Changes to either publisher or subscribers will not affect each other.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

  • Joined

More fromIshan Soni

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp