DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
Building Scalable Real-Time Apps with AstraDB and Vaadin
Register Now

Trending

  • Why You Should Consider Using React Router V6: An Overview of Changes
  • What Is React? A Complete Guide
  • Writing a Vector Database in a Week in Rust
  • Authorization: Get It Done Right, Get It Done Early

Trending

  • Why You Should Consider Using React Router V6: An Overview of Changes
  • What Is React? A Complete Guide
  • Writing a Vector Database in a Week in Rust
  • Authorization: Get It Done Right, Get It Done Early
  1. DZone
  2. Coding
  3. Java
  4. Constructor Value Vs. Observer in Java

Constructor Value Vs. Observer in Java

Learn more about the constructor value and Observer in Java.

Sven Ruppert user avatar by
Sven Ruppert
CORE ·
Oct. 21, 19 · Presentation
Like (2)
Save
Tweet
Share
24.56K Views

Join the DZone community and get the full member experience.

Join For Free

Observer Vs. Constructor Value

Who wins in the battle between Constructor Values and the Observer? Well, it depends.

Who Belongs to Whom?

It is common to connect two components using constructor parameters. This procedure can be seen very clearly, for example, in the construction of graphic surfaces. Take the following source code:

public class SubView {
  private MainView mainView;
  public SubView(MainView mainView) {
    this.mainView = mainView;
  }
  public void buttonClicked(String input) {
    mainView.setInputValue(input);
  }
}


This subcomponent gets the surrounding main component via the constructor. The instance of this given component is just needed for a value transit. The value is the result of a user interaction inside the subcomponent and consumed inside the surrounding element.

This procedure leads to results in various challenges that I find avoidable. First, this sub-component is hard-bound to the main element. This hard-coupled type makes no sense since this bond is based purely on the use of the generated values.

Furthermore, the testing of the sub-component is more difficult because an instance of the main component or a corresponding MOCK must be used. Again, this is an additional requirement that merely adds complexity and at the same time, reduces the abstraction of the individual components. So what can you do here and add that to the project without another framework as a dependency?

Observer: The Classic

There is a straightforward design pattern that can help here. We're talking about the Observer pattern.

public class Observable<KEY, VALUE> {
  private final Map<KEY, Consumer<VALUE>> listeners = new ConcurrentHashMap<>();
  public void register(KEY key, Consumer<VALUE> listener) {
    listeners.put(key, listener);
  }
  public void unregister(KEY key) {
    listeners.remove(key);
  }
  public void sentEvent(VALUE event) {
    listeners.values()
             .forEach(listener -> listener.accept(event));
  }
}


The basic principle consists of three interactions. In a map, a consumer is stored for a given key. The consumer is the utilization unit for an event or better an input type that is defined by the second type definition of the map. You can register with a key and later the associated consumer can also be removed again from this map if needed.

If a data package is now ready for processing, it will be sent to all consumers who have already registered at this time using the method for posting events. In practice, all existing consumers in the map are processing the value once in an undefined order. The sent event must be immutable itself.

Registry

To simplify the process of registering and unsubscribing, you can modify the Observer design pattern a bit.

public interface Registration {
  void remove();
}
public class Registry<KEY, VALUE> {
  private final Map<KEY, Consumer<VALUE>> listeners = new ConcurrentHashMap<>();
  public static <K, V> Registry<K, V> instance() {
    return new Registry<>();
  }
  public Registration register(KEY key, Consumer<VALUE> listener) {
    listeners.put(key, listener);
    return () -> listeners.remove(key);
  }
  public void sentEvent(VALUE event) {
    listeners.values()
             .forEach(listener -> listener.accept(event));
  }
}


For this, a functional interface with the name Registration is defined, which only provides the method to remove itself from the registry. This method implements the respective logout process. The instance of a Registration is the return value of a registration process itself.

The processing of the event data is made in the same way we have done it at the implementation of the Observable. The practical use is shown below in a JUnit5 test:

final Registry<String, Event> eventBus = Registry.instance();

final String expected = "message 001";

final AtomicInteger counter = new AtomicInteger(0);
final String        key01   = "Consumer-01";
final String        key02   = "Consumer-02";

final Registration register01 = eventBus.register(key01, (event) -> {
  assertEquals(expected, event.getMessage());
  counter.incrementAndGet();
});
final Registration register02 = eventBus.register(key02, (event) -> {
  assertEquals(expected, event.getMessage());
  counter.incrementAndGet();
});

eventBus.sentEvent(new Event(expected, ""));
Assertions.assertEquals(2, counter.get());

register01.remove();
eventBus.sentEvent(new Event(expected, ""));
Assertions.assertEquals(3, counter.get());


Coupling of Components

Let's start with the sub-component. This element is no longer linked to the main component. In this example, I am using a general static event bus of type Registry. Of course, you can also use your event bus instance per component, which further decouples the component.

public class SubView {
  public void buttonClicked(String input) {
    EVENT_BUS.sentEvent(new Event(input));
  }
  public Registration register(String key, Consumer<Event> listener) {
    return EVENT_BUS.register(key, listener);
  }
}


If another component wants to use the values of the fictitious user interaction, it can register with the instance of the sub-component.

public static class MainView {
  //for demo public
  public  SubView      subView      = new SubView();
  private Registration registration = subView.register("keyXYZ", 
                                                       e -> inputValue = e.getValue());
  private String       inputValue;

  public String getInputValue() {
    return inputValue;
  }

  public void release() {
    registration.remove();
  }
}


The corresponding JUnit5 test looks like this.

final MainView mainView = new MainView();
final String inputValue = "inputValue";
//subview is public for demo
mainView.subView.buttonClicked(inputValue);

Assertions.assertEquals(inputValue, mainView.getInputValue());


Conclusion

With a few lines of source code, we have not only decoupled the components much better from each other but also simplified the testing of the individual elements. There are no mocks needed anymore. The increased abstraction also allows more than one component to register on the sub-component shown here. Of course, one should not forget at this point that the log-off from a registry should not be forgotten to allow the garbage collector to function correctly.

The source code for these source code examples can be found here.

Happy coding!

Further Reading

The Observer Pattern Using Java 8

Observer Design Pattern in a Nutshell

Java's Observer and Observable Are Depreciated in JDK 9

Java (programming language)

Opinions expressed by DZone contributors are their own.

Trending

  • Why You Should Consider Using React Router V6: An Overview of Changes
  • What Is React? A Complete Guide
  • Writing a Vector Database in a Week in Rust
  • Authorization: Get It Done Right, Get It Done Early

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com

Let's be friends: