0

I understand that decorators solve the problem of needing to mix and match behaviors, but it was suggested that I could use the decorator pattern to solve issues of brittleness that might arise due to changes in the underlying base code.

Specifically, Microsoft provides an ActiveDirectoryMembershipProvider that has virtual methods for creating and updating users. The update method doesn't update all the information that we'd like to update so I felt we should create a new class that inherits from the ActiveDirectoryMembershipProvider so that we could call the base implementation and then write code to update the extended information that the base implementation doesn't cover.

But it was suggested that because the ActiveDirectoryMembershipProvider is 3rd party code, we might run into issues that could be solved by the decorator pattern. It doesn't seem to me that any issues that arise from changes in the underlying code could be mitigated by the decorator pattern. Am I missing anything here?

3
  • 1
    related: What is an Anti-Corruption layer, and how is it used?
    – gnat
    Oct 15, 2014 at 17:22
  • In this particular case, if underlying code changes could potentially break things, it would have broken both class derivation and decorator pattern equally bad. (This would be a good reason to have integration tests.) And to forecast whether breaking changes would happen, you would have to take Microsoft's advice on face value - it could potentially promise not to break things and then go ahead and break it a few months later. So, to offer a useful advice to your situation, it will require a fortune-teller who will know Microsoft's intention for the next .NET release.
    – rwong
    Oct 16, 2014 at 7:06
  • The only kind of breaking changes that would affect class derivation but not decorator is probably the addition of the sealed keyword to the class, or the private-ization of something that your additional behavior depends on.
    – rwong
    Oct 16, 2014 at 7:09

3 Answers 3

5

Repeat after me - Patterns are not always the answer

Phew. Now that we've gotten that out of the way, lets move on. First off, anytime you're extending 3rd party code that doesn't make any guarantees to lifespan or function declarations, you're asking for a nightmare when updating that 3rd party code. A method could change it's parameters, or the class may be removed all together, leaving your code now empty and lonely.

The decorator pattern is great at allowing you to granularly modify small pieces of the underlying method but isn't the end-all be-all solution you may think it is. If you need to add an entire pattern and all the ceremony that comes with it to modify one little variable, why not just make a method that does just that. You don't need it to be hyper extensible or worry about what qualities the pattern has. Just do what you need to do without all the extra overhead.

2
  • Your concern applies to ANY library. Extending any class of a library would incur in the risk you mention. Does it mean every piece of software whould be done from scratch to avoid depending on anything done by a third party ? Oct 16, 2014 at 19:37
  • Sure, if you assume that every library is going to do so, but many libraries provide a thing called an API to program against, and then they go on to make some guarantees about what will and won't change in that API, and how to interface with it so that your code doesn't break. What he's doing here is nothing like that at all. He's taking a random class, and programming against it like it was an API, when the provider hasn't made that guarantee. Sure an API isn't going to keep the world from crashing when you update, but its a far cry from what's going on here.
    – Ampt
    Oct 16, 2014 at 19:38
3

I agree with @Ampt's answer.

I would add another argument here.

Note that you can only ever inherit from one class - I mean, in most popular languages, which happens to include C#. (And even in those where it is possible, such as C++, it is adviced to use this possibility sparringly).

So by making your class extend ActiveDirectoryMembershipProvider, you are effectively blocking it from extending any other class, or incorporating it into any custom class hierarchy that you have in place.

Composition (based on delegation) beats inheritance in terms of flexibility, decorator or not.

Another factor is testability. If you create a decorator that enwraps ActiveDirectoryMembershipProvider, it may be easier to substitute it with a dummy object for testing purposes.

0

It help to understand exactly what Decorator Pattern allows you to do.


Let's just focus on a Decorator that wraps around a single function. (For all other method calls, the decorator would forward it right toward the underlying object.)

This is what the Decorator's method could possibly do:

  • Possibly do some of its own stuff before calling the underlying object
  • Possibly call the method of the same name on the underlying object
    • Or, possibly not. The call was eaten by my dog.
    • Or, still call it, but with a different set of parameters.
  • After returning from the method on the underlying object, possibly do some other stuff of its own.
    • Say, replace the returned value.
    • Or, throw a pizza.

By pizza I mean exception.


What Decorator could not possibly do, is:

A decorator method cannot dictate the internal control flow of the method in the underlying object, in ways that the underlying object doesn't expose.

What I mean by that is:

If the underlying object exposes toggles and switches which influence the internal control flow of its method, fine. Your decorator method can use these toggles and switches, and possibly restore them after returning from the underlying method.

If the underlying object doesn't allow you to influence some aspects of its internal control flow, you're stuck.

In this case, if you need to implement your own alternative control flow to completely replace the underlying method, there's a name for it. The Copy-and-paste Pattern.


However, you cannot do this:

Execute the next 253 bytes of machine code after entering the underlying method. Skip the next 47 bytes of machine code. Oh, by the way, inject some garbage into the underlying method's stack, and prepare to take over the machine's hypervisor with a zero-day exploit.

Actually you could possibly do that, but that's called a Trojan, not a Decorator.


So, going back to the main question. Should I use a Decorator, or a Derived Class?

I guess I'll leave that question to people who are infinitely more familiar with your code base.

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.