0

I have an event that is raised when my job objects change state (e.g. initializing, running, complete, etc). The event is to be invoked from my set method in the State property, but before I make it that far, I'm trying to determine what makes more sense from a consumer standpoint. The event declaration is:

public event EventHandler<JobState> StateChanged;

This means the subscriber signature is:

void Job_StateChanged(object sender, JobState e) { ... }

As a result, I can't help but wonder if e should be the current state, or, since State holds the current state, if it should be the previous state instead.

Alternative Solution

If I create a StateChanging event instead of StateChanged, the state of the job in e seems less ambiguous, at least to me. The naming of StateChanging implies that the State property's value has not yet changed, but is about to change to the value specified in e.

However, my concern with this approach is that the naming also implies that the consumer can prevent the state change from completing, which I don't want to permit.


Should I send the current or previous state with my state changed event; or, should I go with a StateChanging event instead?

2
  • 3
    What useful information would a subscriber for that event get from receiving the previous state of the sender? What would happen if the notification is asynchronous and between emitting and receiving the first event, another event-emitting job change happens? Feb 16, 2022 at 18:01
  • 2
    Would it be possible to make the events themselves more explicit? Instead of a generic JobState event that contains the state, try to name events to describe what actually happened. JobStarted, JobCancelled, JobFinished, etc.
    – Rik D
    Feb 16, 2022 at 18:59

3 Answers 3

8

It won't hurt to include both OldState and NewState as properties of e. It could be convenient to the subscriber, who would not need to keep track of the current state and could perform updates only if NewState differs from OldState. This is a common pattern.

StateChanging is indeed pointless if the subscriber cannot cancel the change anyway.

[Edit]

Then again... when implemented properly, StateChanged would never be raised if the new state were not different from the old state so my first point does not really make sense (in a perfect world). The check for the change should really be at the other end, before the event is raised (or not after all).

Christophe has some other good points I missed.

1
  • Exactly. Send both. This is how I have implemented this in the past and it's very useful. Feb 16, 2022 at 22:18
4

In typical implementations of the Observer pattern, the subject being observed notifies all subscribed observers that its state changed by sending an event that also includes the new state. Exceptions to this rule are cases where it is expensive, not feasible, or desired, to send the new state included with the event.

In your case I would say that if the event is named StateChanged, and the notify observer handler includes JobState e, then that JobState should be the new state. That's the information the observers don't have yet, and might need to act upon.

The other scenario, as you mention, might require the ability to cancel the original process that triggered the event, I would avoid this as it increases coupling and voids the benefit of using the observer pattern.

Additional note on adding things just in case because it won't hurt: Yes, it does hurt. That extra code, coupling, dependencies and optimizations, will later translate into more code to manage, test, a higher likelihood of defects and reduced flexibility when you want to evolve your code base. Keep your code clean and your options open for when you actually have requirements.

3

In short

To encourage a sound separation of concerns, prefer StateChanged with only the new state. Consider StateChanging with care, as it risks to create hidden coupling (e.g. the receiver may be expected to know the possible transitions from old to new).

Some more explanations

A StateChanged event allows in principle other objects to take necessary actions to adjust to a new state, according to a sound separation of concerns:

  • If StateChanged to Complete the scheduler can remove the job from the list. Or if it changed to running, it should’t matter if before it was Initializing or OnHold.
  • If countdown state changed to 0, the rocket should initiate ignition, whatever the previous state was.

The old state shouldn’t matter. If you need to know both, the old state and the new state, there might be a tricky hidden coupling somewhere: an object would need to care for the internals of another object. That’s not really in the spirit of the principle of least knowledge.

A StateChanging event is different : Its purpose could either be to intervene (e.g. cancel a state transition), or to synchronize coupled objects (e.g. in an aggregate; or to feed a related state machine elsewhere). As the event would correspond to the action of changing rater than the completed action, old and new states would make sense.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.