开发者

How to handle 'this' reference in decorator pattern

I have a problem in a class of mine that uses the decorator pattern.

The pr开发者_如何转开发oblem arises when the inner object uses the "this" reference in calls to other objects. This causes all calls from the object that received the "this" reference to be made directly to the inner object, without going through the outer first.

What is the usual way to solve this problem?

Thanks.


Objects have an implicit value: their identity (can be tested by applying ==). When you wrap them, you effectively hide that identity (worse, you also expose a potentially misleading identity, the identity of the wrapper itself). So an obvious approach is compensating for this by exposing the identity of the object via another way - explicitly. E.g. you might introduce a method Object getIdentity(), that returns an object really representing the intended identity, and allowing applying == to it.

The huge downside though is that you still allow == on the decorator itself, e.g. a hazard that:

  • is natural enough to be tricked into it (identity == decorator instead of identity == decorator.getIdentity())
  • silently does the wrong thing (compare with a runtime exception - good luck debugging that)

The problem would be non-existent if, for example, objects had a method like:

protected Object getIdentity() {
    return this;
}

On which == operator would be defined, so a wrapper could also wrap the identity of the wrapped object, instead of replacing it with its own.


In general, you can't. Unless you subclass your decorated class, your inner class will be free to call any method using itself as parameter, and there is no way to change it.

When using the decorator pattern, the decorator class is in charge of passing the this reference (referencing the decorator itself) to other methods. From my point of view, the decorator is the most similar thing to a proxy: the inner decorated object is completely wrapped into its decorator(s) and is not directly accessible. So, you must find by yourself a way to disallow your decorated object to directly access other objects and being able to pass this reference


What you're describing is a blend of the decorator and template patterns. The decorator pattern permits you to add behavior dynamically to an object (through the use of a proxy-like mechanism). The template pattern breaks an algorithm into several methods so you can vary an object's behavior by substituting methods via subclassing, or in your case, the decorator.

Because decorators are a type of proxy, they hold a reference to the target object (or another decorator wrapped n-layers deep around the target object). But the target usually doesn't track its decorators or make any assumption regarding decorators.

So every time the target object's behavior is altered by adding or removing the outermost decorator your design will either need to update the target object with a reference to its outermost decorator or the target object will need to query another object for the outermost decorator's reference.

If the target object can query the reference to the decorated object stack from whichever object is responsible for holding it (something must), then you're probably ok. Otherwise the target object might need to resort to a delegate or mediator.


In decorator pattern, you should define an interface for accessing the object. It's a good idea that all the public members are exposed through that interface so the decorator class will know all the public members and have to consider providing them in a proper way.

for example consider we have this class:

class FileCreator {
   public void SafeCreateFile(string name, string content) {
      MakeSureOfSufficientFreeSpace(content);
      CreateFile(name, content);
   }

   protected MakeSureOfSufficientFreeSpace() { ... }
   public CreateFile() { ... }
}

now if we want to be able to add log capability to FileCreator whenever we want by using decorator, we will extract IFileCreator interface from FileCreator and write WithLogFileCreatorDecorator. but to prevent calling FileCreator methods instead of decorated ones we should define another decorator and move SafeCreateFile logic into it.

interface IFileCreator {
    void SafeCreateFile();
    void MakeSureOfSufficientFreeSpace();
    void CreateFile();
}

class FileCreator : IFileCreator  {
   public void SafeCreateFile(string name, string content) {
      throw new NotImplementedEXception("requires WithLogFileCreatorDecorator to run this method")
   }

   protected MakeSureOfSufficientFreeSpace() { ... }
   public CreateFile() { ... }
}

//nothing special here
abstract class FileCreatorDecorator : IFileCreator {
     private IFileCreator _decorationTarget;
     public FileCreatorDecorator (IFileCreator decorationTarget) { _decorationTarget= decorationTarget; }
    
     public virtual void SafeCreateFile() { _decorationTarget.SafeCreateFile(); }
     protected virtual void MakeSureOfSufficientFreeSpace() { _decorationTarget.MakeSureOfSufficientFreeSpace(); }
     public virtual void CreateFile() { _decorationTarget.CreateFile(); }
}

class WithLogFileCreatorDecorator() : FileCreatorDecorator {
   protected override void MakeSureOfSufficientFreeSpace() { ... }
   public override void CreateFile() { ... }
}

// here we separate SafeCreateFile logic from base class to a decorator 
// so it will call decorated methods
class SafeFileCreatorDecorator() : FileCreatorDecorator {
   public override  void SafeCreateFile(string name, string content) {
      MakeSureOfSufficientFreeSpace(content);
      CreateFile(name, content);
   }
}

We can say SafeFileCreatorDecorator is a facade for easy accessing the FileCreator class that is attached to main object via decorator.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜