0

I have a service class that does some magic. I want to introduce a new type of functionality - raise an event. I am absolutely sure that decorator pattern is great for this scenario. The problem is that the variables I'd like to pass as an event arguments are created only in that service class function. These variables are not a return type. Is there a clever way of accessing them in the decorator class to raise an event in there?

class Service
{
function DoStuff()
{
variable1 = 1
variable2 = 2
variable3 = 3
}
}

class ServiceDecorator
{
function DoStuff()
{
parent DoStuff()
// Raise an event with variables listed in the service class function.
}
}
3
  • 1
    "I have a service class that does some magic." - Does that mean that it has a single void method that does something, and no external code (clients or derivatives) can tell what's going on, or have any kind of interaction other than a "fire and forget" call? If that's the case, then you should change that to something more structured. Jan 5 at 1:32
  • 1
    E.g., you could pass these vars to an appropriately placed virtual method that a derived class can override, but that does nothing by default. Jan 5 at 1:35
  • Great point! I have a function that is supposed to build an entity; it does, but it also saves it to the database in there too.
    – pro100tom
    Jan 5 at 10:36
5

First and foremost, the Decorator has nothing to do with inheritance. If you are extending a class, that is bog standard polymorphism. A Decorator has the same interface as the class you are trying to extend, and takes another class of the same type to wrap it.

Decorators are used often in middleware. I.e. wrapping a service to provide logic before and after the thing you are extending.

Take for example an interface for processing web requests:

public interface WebService {
    void handle(HttpRequest request, HttpResponse response)
}

You could have your fancy web service implementation that does things, but then you need to add in some telemetry. In that case you can use a Decorator to handle the telemetry part without changing anything in your working code. The decorator would look something like this:

public class TelemetryWebService {
    private WebService wrapped

    public TelemetryWebService(WebService handler) {
        wrapped = handler
    }

    public handle(HttpRequest request, HttpResponse response) {
        context = new TelemetryContext(request)
        context.sendBeginMessage(request)

        wrapped.handle(request, response)

        context.sendEndMessage(request)
    }
}

That's what a Decorator is.

Now, if you extend a class that's called polymorphism. Your object oriented language has keywords to manage the scope of variables. The common convention is as follows:

  • private attributes only belong to the class they are defined in
  • protected attributes can be accessed by the class they are defined in as well as any class that extends that
  • public attributes can be accessed by any code with a reference to your object

So if you want to expose the internal state of your base class, you have to make a conscious decision to make that state available. To protect yourself from things outside your class changing how it works, it's best to make that state available as read only. That's commonly done as a "getter" method. Some languages have some syntactic sugar called properties. Either way, be careful about what you allow to change the state in your class.

2
  • I see your point. But now I am confused. I saw examples of instantiation chaining like "new DecoratorA(new DecoratorB(new Class()))" With your example we won't be able to chain these. Am I missing something? Let's say we want to have one decorator for logging and another for raising events. With your solution this is impossible.
    – pro100tom
    Jan 5 at 10:42
  • How wouldn't you chain them in the way I set it up? The constructor for the decorator takes any object implementing the WebService interface. Even another decorator that implements the same interface. I don't see how my example precludes those examples. Jan 5 at 13:56
1

Filip has guided me in the right direction. My function that was doing magic, was doing more than it should. By abstracting out the responsibilities the code wrote itself in a way that has solved the problem.

To be concrete, my function was suppose to build entities. But it also was saving them. The event I wanted to fire was suppose to trigger after the successful save. So it turns out it doesn't even belong to this build class at all!

So yeah, if anyone faces similar issue, check first that your function doesn't do more than it should!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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