CDI (Part 3): Events and Observers for Low Coupling and High Cohesion

综合编程 DZone (源链)

Hello!

This is the Part 3
of the CDI Series in Java
that contains:

  • Part 1: Factory in CDI with @Produces annotation
  • Part 2: CDI Qualifiers in Java: Polymorphism in DI

  • Part 3
    : CDI Events and Observers for Low Coupling and High Cohesion
  • Part 4
    : CDI and Lazy Initialization
  • Part 5
    : Interceptors in CDI
  • Part 6
    : CDI Dependency Injection and Alternatives
  • Part 7
    : Working with CDI Decorators

In this part of the CDI Series
, we’ll explore Events and Observers
used in CDI to have code with Low Coupling
and High Cohesion
.

Let’s contextualize our application problem.

  • We must have a Checkout class
  • We must send an email when a checkout is finished
  • We must send a metric when a checkout is finished

Let’s code!

Step 1: Creating the Entire Scenario

This first class will use CDI 2.0, which allows us to use CDI in a standalone mode:

public class MainApplication {

    public static void main(String[] args) {
        try (CDI container = new Weld().initialize()) {
            Checkout checkout = container.select(Checkout.class).get();

            Buyer buyer = new Buyer("welcome@hackingcode.com");
            Order order = new Order(buyer, 10L, 80.0);

            checkout.finishCheckout(order);
        }
    }

}

This code won’t compile, since we don’t have the Order
class yet.

Next, we have this Order
class with a few fields:

public class Order {

    private double price;
    private Long id;
    private Buyer buyer;

    public Order(Buyer buyer, Long id, double price) {
        this.buyer = buyer;
        this.id = id;
        this.price = price;
    }

    public double getPrice() {
        return price;
    }

    public Long getId() {
        return id;
    }

    public Buyer getBuyer() {
        return buyer;
    }

}

As you can see, an Order
has a Buyer
. Let’s create this class:

public class Buyer {

    private String email;

    public Buyer(String email) {
        this.email = email;
    }

    public String getEmail() {
        return email;
    }

}

Great!

It’s time to create the class that sends metrics
. This is good cohesion, since this class is responsible only for sending metrics:

public class MetricCreator {

    public void createMetricFor(Order order) {
        System.out.println("Creating new Metric for OrderId: " + order.getId());
    }

}

Following the same thought, we’ll create the EmailSender
class:

public class EmailSender {

    public void sendEmailFor(Buyer buyer) {
        System.out.println("Sending email to: " + buyer.getEmail());
    }

}

Finally, it’s time to create the Checkout
class. In finishCheckout()
, we’re receiving an Order as a parameter and, with this object, we send an email
and metrics
:

public class Checkout {

    @Inject
    private EmailSender emailSender;

    @Inject
    private MetricCreator metrics;

    public void finishCheckout(Order order) {
        System.out.println("Finishing Checkout with Id: " + order.getId());

        emailSender.sendEmailFor(order.getBuyer());

        metrics.createMetricFor(order);
    }

}

Nice!

So far, we have good cohesion and low coupling because the Checkout
class just knows the methods to send email
and metrics
but it doesn’t know how
.

But what if we need to do more? For example:

  • Send a push notification
  • Send Kafka messages for other systems
  • Send events to Amazon SQS for third-party integrations

Can we follow the same pattern as above? Can we just inject the responsible class in the Checkout
class?

public class Checkout {

    @Inject
    private EmailSender emailSender;

    @Inject
    private MetricCreator metrics;

    @Inject
    private PushNotification pushNotification;    

    @Inject
    private KafkaMessager kafka;    

    @Inject
    private AmazonSqs amazonSqs;    

    public void finishCheckout(Order order) {
        System.out.println("Finishing Checkout with Id: " + order.getId());

        emailSender.sendEmailFor(order.getBuyer());

        metrics.createMetricFor(order);

        pushNotification.send(order);

        kafka.send(order.getId(), order.getBuyer());

        amazonSqs.send(order);
    }

}

As you can see, we can quickly fall into a bunch of injections that don’t matter to the Checkout itself.

Every timewe need to use the Order in Checkout, we should open
the Checkout class to add an @Inject annotation with the new object.

To follow good cohesion, in this case, I’d like to just see code related to a checkout being finished.

Let’s refactor this code with more CDI!

Step 2: Refactoring to Use CDI Events and Observers

Do you remember the Observer Pattern
? So, we will use that to notify that one action has happened
.

We’ll create a class called CheckoutEvent
that will indicate the checkout event.

public class CheckoutEvent {

    private Order order;

    public CheckoutEvent(Order order) {
        this.order = order;
    }

    public Order getOrder() {
        return order;
    }

}

Now it’s time to get help from CDI. We must use the Event Interface
to notify all subscribers
interested in the checkout event:

public class Checkout {

    @Inject
    private Event event;

    public void finishCheckout(Order order) {
        System.out.println("Finishing Checkout with Id: " + order.getId());

        event.fire(new CheckoutEvent(order));
    }

}

As you can see, we’ve used the method fire()
to trigger
the action to notify
all subscribers in the event

Step 3: Observing the CDI Event

When a Checkout action is taken, we would like to have two actions executed:

  • Send an email
  • Create a metric

We should indicate that the EmailSender
class is interested in the checkout event. We can do that by using the @Observes
annotation from CDI, as you can see below:

public class EmailSender {

    public void sendEmailFor(@Observes CheckoutEvent event) {
        Buyer buyer = event.getOrder().getBuyer();

        System.out.println("Sending email to: " + buyer.getEmail());
    }

}

Of course, we can subscribe
more interested classes, as we will see with the MetricCreator
class below:

public class EmailSender {

    public void sendEmailFor(@Observes CheckoutEvent event) {
        Buyer buyer = event.getOrder().getBuyer();

        System.out.println("Sending email to: " + buyer.getEmail());
    }

}

Excellent! Notice that your Checkout
class doesn’t
know about the EmailSender
and MetricCreator
classes. The finishCheckout()
method is just worried about its logic and will notify everyone who is interested in this event, but without knowing them
. Pretty cool!

That’s it! I hope that this part of the CDI series
is useful to you!

In the next post we’re going to explore Lazy Initialization in CDI: Part 4: CDI and Lazy Initialization

Thanks!

您可能感兴趣的

Using QuantLib from Clojure via the SWIG/Java bind... As I mentioned in a previous post we have been doing some work in QuantLib/Swig/Java in part using the Clojure language which is build on top o...
G1 垃圾收集器介绍 之前根据 Sun 的内存管理白皮书介绍了在 HotSpot JVM 分代算法中的几个垃圾收集器,本文将介绍 G1 垃圾收集器。 G1 的主要关注点在于达到 可控的停顿时间 ,在这个基础上尽可能提高吞吐量,这一点非常重要。 G1 被设计用来长期取代 CMS 收集器,和 CMS 相同的...
献给迷惘的Java架构工程师 1. 工程化专题 (团队大于3个人之后,你需要去考虑团队合作,科学管理) 2. 源码分析专题 (好的程序员,一行代码一个设计就能看出来,源码分析带你品味代码,感受架构) 3.高性能及分布式专题 (跟上技术节奏,扩宽技术视...
4张图看懂Java,分四个阶段自学Java入门... Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互联网,同时拥有全球最大的开发者专业社群。 给你学习路线:html-css-js-jq-javase-数据库-...
Android 中的 Effective Java(速查表) Effective Java 是一本被广泛认可的著作,它指明了在写 Java 代码时兼顾 可维护性 与 效率 的方式。Android 也是使用 Java 来开发的,这意味着前书中的 所有 建议仍旧可用,真的是这样吗?并不尽然。 某些 同学 认为书中的“大部分...
DZone责编内容来自:DZone (源链) | 更多关于

阅读提示:酷辣虫无法对本内容的真实性提供任何保证,请自行验证并承担相关的风险与后果!
本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » CDI (Part 3): Events and Observers for Low Coupling and High Cohesion



专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录