10 Replies Latest reply: Mar 25, 2010 8:20 PM by 843853 RSS

    New(?) pattern looking for a good home

    843853
      Hi everyone, this is my second post to sun forums about this, I initially asked people for help with the decorator and strategy pattern on the general Java Programming forum not being aware that there was a specific section for design pattern related questions. Since then I refined my solution somewhat and was wondering if anyone here would take a look. Sorry about the length of my post, I know it's best to keep it brief but in this case it just seemed that a fully functional example was more important than keeping it short.

      So what I'd like to ask is whether any of you have seen this pattern before and if so, then what is it called. I'm also looking for some fresh eyes on this, this example I wrote seems to work but there are a lot of subtleties to the problem so any help figuring out if I went wrong anywhere is greatly appreciated. Please do tell me if you think this is an insane approach to the problem -- in short, might this pattern have a chance at finding a good home or should it be put down?

      The intent of the pattern I am giving below is to modify behavior of an object at runtime through composition. In effect, it is like strategy pattern, except that the effect is achieved by wrapping, and wrapping can be done multiple times so the effect is cumulative. Wrapper class is a subclass of the class whose instance is being wrapped, and the change of behavior is accomplished by overriding methods in the wrapper class. After wrapping, the object "mutates" and starts to behave as if it was an instance of the wrapper class.

      Here's the example:
      public class Test {
      
           public static void main(String[] args) {
                double[] data = { 1, 1, 1, 1 };
      
                ModifiableChannel ch1 = new ModifiableChannel();
                ch1.fill(data);
      
                // ch2 shifts ch1 down by 1
                ModifiableChannel ch2 = new DownShiftedChannel(ch1, 1);
                // ch3A shifts ch2 down by 1
                ModifiableChannel ch3A = new DownShiftedChannel(ch2, 1);
                // ch3B shifts ch2 up by 1, tests independence from ch3A
                ModifiableChannel ch3B = new UpShiftedChannel(ch2, 1);
                // ch4 shifts ch3A up by 1, data now looks same as ch2
                ModifiableChannel ch4 = new UpShiftedChannel(ch3A, 1);
      
      
                // print channels:
                System.out.println("ch1:");
                printChannel(ch1);
                System.out.println("ch2:");
                printChannel(ch2);
                System.out.println("ch3A:");
                printChannel(ch3A);
                System.out.println("ch3B:");
                printChannel(ch3B);
                System.out.println("ch4:");
                printChannel(ch4);
           }
      
           public static void printChannel(Channel channel) {
                for(int i = 0; i < channel.size(); i++) {
                     System.out.println(channel.get(i) + "");
                }
                // Note how channel's getAverage() method "sees"
                // the changes that each wrapper imposes on top
                // of the original object.
                System.out.println("avg=" + channel.getAverage());
           }
           
      }
      /**
       * A Channel is a simple container for data that can
       * find its average. Think audio channel or any other
       * kind of sampled data.
       */
      public interface Channel {
      
           public void fill(double[] data);
      
           public double get(int i);
      
           public double getAverage();
      
           public int size();
      
      }
      public class DefaultChannel implements Channel {
           private double[] data;
      
           public void fill(double[] data) {
                this.data = new double[data.length];
                for(int i = 0; i < data.length; i++)
                     this.data[i] = data;
           }

           public double get(int i) {
                if(i < 0 || i >= data.length)
                     throw new IndexOutOfBoundsException("Incorrect index.");
                return data[i];
           }

           public double getAverage() {
                if(data.length == 0) return 0;
                double average = this.get(0);
                for(int i = 1; i < data.length; i++) {
                     average = average * i / (i + 1) + this.get(i) / (i + 1);
                }
                return average;
           }

           public int size() {
                return data.length;
           }
      }
      public class ModifiableChannel extends DefaultChannel {
           protected ChannelModifier modifier;

           public void fill(double[] data) {
                if (modifier != null) {
                     modifier.fill(data);
                } else {
                     super.fill(data);
                }
           }
           public void _fill(double[] data) {
                super.fill(data);
           }

           public double get(int i) {
                if(modifier != null)
                     return modifier.get(i);
                else
                     return super.get(i);
           }
           public double _get(int i) {
                return super.get(i);
           }

           public double getAverage() {
                if (modifier != null) {
                     return modifier.getAverage();
                } else {
                     return super.getAverage();
                }
           }
           public double _getAverage() {
                return super.getAverage();
           }

      }
      public class ChannelModifier extends ModifiableChannel {
           protected ModifiableChannel delegate;
           protected ModifiableChannel root;

           protected ChannelModifier tmpModifier;
           protected boolean doSwap = true;
           private void pre() {
                if(doSwap) { // we only want to swap out modifiers once when the
                     // top call in the chain is made, after that we want to
                     // proceed without it and finally restore doSwap to original
                     // state once ChannelModifier is reached.
                     tmpModifier = root.modifier;
                     root.modifier = this;
                     if(delegate instanceof ChannelModifier)
                          ((ChannelModifier)delegate).doSwap = false;
                }
           }
           private void post() {
                if (doSwap) {
                     root.modifier = tmpModifier;
                } else {
                     if(delegate instanceof ChannelModifier)
                               ((ChannelModifier)delegate).doSwap = true;
                }
           }

           public ChannelModifier(ModifiableChannel delegate) {
                if(delegate instanceof ChannelModifier)
                     this.root = ((ChannelModifier)delegate).root;
                else
                     this.root = delegate;

                this.delegate = delegate;
           }

           public void fill(double[] data) {
                pre();
                if(delegate instanceof ChannelModifier)
                     delegate.fill(data);
                else
                     delegate._fill(data);
                post();
           }

           public double get(int i) {
                pre();
                double result;
                if(delegate instanceof ChannelModifier)
                     result = delegate.get(i);
                else
                     result = delegate._get(i);
                post();
                return result;
           }
           
           public double getAverage() {
                pre();
                double result;
                if(delegate instanceof ChannelModifier)
                     result = delegate.getAverage();
                else
                     result = delegate._getAverage();
                post();
                return result;
           }

           public int size() {
                //for simplicity no support for modifying size()
                return delegate.size();
           }
      }
      public class DownShiftedChannel extends ChannelModifier {
           private double shift;

           public DownShiftedChannel(ModifiableChannel channel, final double shift) {
                super(channel);
                this.shift = shift;
           }

           @Override
           public double get(int i) {
                return super.get(i) - shift;
           }
      }
      public class UpShiftedChannel extends ChannelModifier {
           private double shift;

           public UpShiftedChannel(ModifiableChannel channel, final double shift) {
                super(channel);
                this.shift = shift;
           }

           @Override
           public double get(int i) {
                return super.get(i) + shift;
           }
      }
      Output:
      ch1:
      1.0
      1.0
      1.0
      1.0
      avg=1.0
      ch2:
      0.0
      0.0
      0.0
      0.0
      avg=0.0
      ch3A:
      -1.0
      -1.0
      -1.0
      -1.0
      avg=-1.0
      ch3B:
      1.0
      1.0
      1.0
      1.0
      avg=1.0
      ch4:
      0.0
      0.0
      0.0
      0.0
      avg=0.0
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
        • 1. Re: New(?) pattern looking for a good home
          jduprez
          Hello,

          unless you sell your design better, I deem it is an inferior derivation of the Adapter pattern.

          In the Adapter pattern, the adaptee doesn't have to be designed to support adaptation, and the instance doesn't even know at runtime whether it is adapted.
          Your design makes the "modifiable" class aware of the modification, and it needs to be explicitly designed to be modifiable (in particular this constrains the implementation hierarchy). Overall DesignPattern are meant to provide flexibility, your version offers less flexibility than Adapter, as it poses more constraint on the modifiable class.
          Another sign of this inflexibility is your instanceof checks.

          On an unrelated note, I intensely dislike your naming choice of fill() vs _fill()+, I prefer more explicit names (I cannot provide you one as I didn't understand the purpose of this dual method, which a good name would have avoided, by the way).

          That being said, I haven't followed your original problem, so I am not aware of the constraints that led you to this design.

          Best regards,

          J.

          Edited by: jduprez on Mar 22, 2010 10:56 PM
          • 2. Re: New(?) pattern looking for a good home
            843853
            jduprez wrote:
            Hello,

            unless you sell your design better, I deem it is an inferior derivation of the Adapter pattern.

            In the Adapter pattern, the adaptee doesn't have to be designed to support adaptation, and the instance doesn't even know at runtime whether it is adapted.
            Your design makes the "modifiable" class aware of the modification, and it needs to be explicitly designed to be modifiable (in particular this constrains the implementation hierarchy). Overall DesignPattern are meant to provide flexibility, your version offers less flexibility than Adapter, as it poses more constraint on the modifiable class.
            Another sign of this inflexibility is your instanceof checks.

            On an unrelated note, I intensely dislike your naming choice of fill() vs _fill()+, I prefer more explicit names (I cannot provide you one as I didn't understand the purpose of this dual method, which a good name would have avoided, by the way).

            That being said, I haven't followed your original problem, so I am not aware of the constraints that led you to this design.

            Best regards,

            J.

            Edited by: jduprez on Mar 22, 2010 10:56 PM
            Thank you for your input, I will try to explain my design better. First of all, as I understand it the Adapter pattern is meant to translate one interface into another. This is not at all what I am trying to do here, I am trying to keep the same interface but modify behavior of objects through composition. I started thinking about how to do this when I was trying to apply the Decorator pattern to filter some data. The way I would do that in my example here is to write an AbstractChannelDecorator that delegates all methods to the Channel it wraps:
            public abstract class AbstractChannelDecorator implements Channel {
                    protected Channel delegate;
            ...// code ommitted
                 public double getAverage() {
                      return delegate.getAverage();
                 }
            ...// code ommitted
            }
            and then to filter the data I would extend it with concrete classes and override the appropriate methods like so:
            public class DownShiftedChannel extends AbstractChannelDecorator {
                 ...// code ommitted
                 public double get(int i) {
                      return super.get(i) - shift;
                 }
                   ...// code ommitted
            }
            (I am just shifting the data here to simplify the examples but a more realistic example would be something like a moving average filter to smooth the data).

            Unfortunately this doesn't get me what I want, because getAverage() method doesn't use the filtered data unless I override it in the concrete decorator, but that means I will have to re-implement the whole algorithm. So that's pretty much my motivation for this, how do I use what on the surface looks like a Decorator pattern, but in reality works more like inheritance?

            Now as to the other points of critique you mentioned:

            I understand your dislike for such method names, I'm sorry about that, I had to come up with some way for the ChannelModifier to call ModifiableChannel's super's method equivalents. I needed some way to have the innermost wrapped object to initiate a call to the topmost ChannelModifier, but only do it once -- that was one way to do it. I suppose I could have done it with a flag and another if/else statement in each of the methods, or if you prefer, the naming convention could have been fill() and super_fill(), get() and super_get(), I didn't really think that it was that important. Anyway, those methods are not meant to be used by any other class except ChannelModifier so I probably should have made them protected.

            The instanceof checks are necessary because at some point ChannelModifier instance runs into a delegate that isn't a ChannelModifier and I have to somehow detect that, because otherwise instead of calling get() I'd call get() which in ModifiableChannel would take me back up to the topmost wrapper and start the whole call chain again, so we'd be in infinite recursion. But calling get() allows me to prevent that and go straight to the original method of the innermost wrapped object.

            I completely agree with you that the example I presented has limited flexibility in supporting multiple implementations. If I had two different Channel implementations I would need two ModifiableChannel classes, two ChannelModifiers, and two sets of concrete implementations -- obviously that's not good. Not to worry though, I found a way around that. Here's what I came up with, it's a modification of my original example with DefaultChannel replaced by ChannelImplementation1,2:
            public class ChannelImplementation1 implements Channel { ... }
            
            public class ChannelImplementation2 implements Channel { ... }
            
            // this interface allows implementations to be interchangeable in ChannelModifier
            public interface ModifiableChannel {
                 public double super_get(int i);
                 public double super_getAverage();
                 public void setModifier(ChannelModifier modifier);
                 public ChannelModifier getModifier();
            }
            
            public class ModifiableChannelImplementation1
                      extends ChannelImplementation1
                      implements ModifiableChannel {
                 ... // see DefaultChannel in my original example
            }
            
            public class ModifiableChannelImplementation2
                      extends ChannelImplementation1
                      implements ModifiableChannel { ...}
            
            // ChannelModifier is a Channel, but more importantly, it takes a Channel,
            // not any specific implementation of it, so in effect the user has complete 
            // flexibility as to what implementation to use.
            public class ChannelModifier implements Channel {
                 protected Channel delegate;
                 protected Channel root;
            
                 protected ChannelModifier tmpModifier;
                 protected boolean doSwap = true;
            
                 public ChannelModifier(Channel delegate) {
                      if(delegate instanceof ChannelModifier)
                           this.root = ((ChannelModifier)delegate).root;
                      else
                           this.root = delegate;
            
                      this.delegate = delegate;
                 }
            
            
                 private void pre() {
                      if(doSwap) {
                           if(root instanceof ModifiableChannel) {
                                ModifiableChannel root = (ModifiableChannel)this.root;
                                tmpModifier = root.getModifier();
                                root.setModifier(this);
                           }
                           if(delegate instanceof ChannelModifier)
                                ((ChannelModifier)delegate).doSwap = false;
                      }
                 }
                 private void post() {
                      if (doSwap) {
                           if(root instanceof ModifiableChannel) {
                                ModifiableChannel root = (ModifiableChannel)this.root;
                                root.setModifier(tmpModifier);
                           }
                      } else {
                           if(delegate instanceof ChannelModifier)
                                     ((ChannelModifier)delegate).doSwap = true;
                      }
                 }
            
                 public void fill(double[] data) {
                      delegate.fill(data);
                 }
            
                 public double get(int i) {
                      pre();
                      double result;
                      if(delegate instanceof ModifiableChannel)
            // I've changed the naming convention from _get() to super_get(), I think that may help show the intent of the call
                           result = ((ModifiableChannel)delegate).super_get(i);
                      else
                           result = delegate.get(i);               
                      post();
                      return result;
                 }
            
                 public double getAverage() {
                      pre();
                      double result;
                      if(delegate instanceof ModifiableChannel)
                           result = ((ModifiableChannel)delegate).super_getAverage();
                      else
                           result = delegate.getAverage();
                      post();
                      return result;
                 }
            
                 public int size() {
                      return delegate.size();
                 }
            }
            
            public class UpShiftedChannel extends ChannelModifier { ...}
            
            public class DownShiftedChannel extends ChannelModifier { ... }
            • 3. Re: New(?) pattern looking for a good home
              jduprez
              unless you sell your design better, I deem it is an inferior derivation of the Adapter pattern.
              First of all, as I understand it the Adapter pattern is meant to translate one interface into another. This is not at all what I am trying to do here, I am trying to keep the same interface but modify behavior of objects through composition.
              Damn, stupid me!
              Yes I had Decorator in mind (honest) all along, Adapter has no role here whatsoever!
              Sorry for this miswording. Please re-read my earlier post with Decorator instead of Adapter:
              In the Decorator pattern, the decorated class doesn't have to be designed to support decoration, and the instance doesn't even know at runtime whether it is decorated.
              Your design makes the "modifiable" class aware of the modification, and it needs to be explicitly designed to be modifiable (in particular this constrains the implementation hierarchy).
              Overall DesignPattern are meant to provide flexibility, your version offers less flexibility than Decorator, as it poses more constraint on the modifiable class.
              Another sign of this inflexibility is your instanceof checks.
              As for the rest of your justifications, I'll need to re-read that more thoroughly, I promise to comment later.
              Best regards,
              • 4. Re: New(?) pattern looking for a good home
                843853
                >
                Damn, stupid me!
                Yes I had Decorator in mind (honest) all along, Adapter has no role here whatsoever!
                Sorry for this miswording. Please re-read my earlier post with Decorator instead of Adapter:
                No problem, I totally sympathize -- design patterns have been, at times, giving me headaches ever since my last software engineering course. I always think they'll make my programs better, but sometimes it just seems they are there to have me loose sleep and make me loopy. I can only keep track of the few I'm intimately familiar with (my favorite, and the one I almost always try to stay away from, is Strategy).

                In light of what you said your previous post makes a lot more sense to me now, and I completely agree with you -- without the changes I made in my last post (to make ChannelModifier implementation-independent) the pattern I presented is completely inferior to the Decorator pattern. Also the code for ChannelModifier and Modifiable implementations is probably more convoluted than necessary, I just hope it doesn't completely obscure the substance of what I was trying to accomplish. Perhaps with your help we may be able to clean it up a bit if we deem it worthy.

                >
                As for the rest of your justifications, I'll need to re-read that more thoroughly, I promise to comment later.
                Best regards,
                I look forward to your comments, thank you very much for taking the time to delve into this issue. Let me know if there is any specific part of code that I should explain more -- there was little room in my posts for comments and I had to ommit some code due to length constraints.

                -Andrey
                • 5. Re: New(?) pattern looking for a good home
                  jduprez
                  OK I understand better now (also with the help of your [previous topic|http://forums.sun.com/thread.jspa?threadID=5431689&start=0&tstart=0] ).

                  I agree that if you keep this Channel interface as it is, some base class needs knowledge that it will be decorated/modified/processed. Specifically, it must be designed to allow some object to change the behavior of some methods.
                  But I think the issue is merely that you coalesce several not-so-related methods into this single interface.

                  There may be a simpler solution, if you accept to split the Channel interface; method getAverage() in particular looks like it could be moved to another abstraction, this way:
                  /**
                   * A simple container for data
                   * Think audio channel or any other
                   * kind of sampled data.
                   */
                  public interface Channel { 
                       public void fill(double[] data);
                       public double get(int i); 
                       public int size(); 
                  }
                  
                  /**
                   * A computing abstraction that extracts stats out of a channel
                   */
                  public interface ChannelProcessor {
                      public void setChannel(Channel channel);
                      public double getAverage(); 
                      public double getVariance();
                      public double getDynamics();
                  }
                  
                  public class ChannelProcessorImpl {
                      private Channel theChannel;
                      // Obvious implementation omitted
                  }
                  
                  // See notes in post text
                  public class AbstractChannelDecorator {
                      protected Channel theDelegate;
                      public AbstractChannelDecorator(Channel delegate) { ...}
                  
                      // no method is implemented,
                      // that will be the job of the decorator implementation
                  }
                  Notes:
                  There are only 3 methods to implement in each decorator implementation, maybe two:
                  - getSize(), I presume, may be affected by a decorator that removes values (sub-sampling or audio compression for example).
                  - get(i), obviously, has to be overridden (note that a default implementation of get() that simply forwards to call to the delegate is probably not very useful: a proper subsampling decorator implementation, for example, would probably have to override get(), to provide an interpolated values).
                  - most likely, fill(...) could be implemented in terms of get(.) in the abstract decorator superclass; it's only a guess based on the method names and signatures, and maybe for efficiency fill() would need to be specifically overridden anyway (e.g. in a subsampling decorator).

                  So the abtract decorator superclass only serves the purpose to hold the delegate reference (and possibly the fill(...) default implementation). Not a big added value, so you might as well do away without this superclass.

                  Eventually, the processor implementation can be written once, using Channel's methods, so it can be used with whatever chain of ChannelDecorators.
                  You don't need to have different processor implementations, unless you need different ways to compute the average, or whatever other derived data (in this case you would have to devies another design, orthogonal to the Channel decoration design, for your processor classes.

                  Edited by: jduprez on Mar 24, 2010 9:50 PM
                  • 6. Re: New(?) pattern looking for a good home
                    843853
                    I haven't thought of that... hmm... splitting the interface into data-related and processing-related components certainly simplifies the whole thing. I've been thinking about this and whichever way I turn it I think your solution is better.

                    There's one thing that's been bothering me but the more I think about it the more I realize that it's probably a minor issue. It's the case where original implementations (e.g. ChannelImplementation1 and ChannelImplementation2) for some reason can't or shouldn't be modified (perhaps because we don't want to break them or we don't have the source code). In that case we can't really solve the problem by splitting the interface.

                    Of course I now realize that the way I'm doing it is not of much help either. In my example, I know that getAverage() method uses get() method rather than accessing the data array directly when computing the result, but that ties my whole solution to knowing the implementation details which is bad design. Yours is much better since getAverage() method must access data through the get() method. I guess if there was a way, this thread should be renamed ("modified") ;) to read "An example of how not to use inheritance". Ah well... It's still nice to know there's a way to override methods by wrapping objects, even if there's no point to actually doing it. Thanks for clarifying the issue for me.
                    • 7. Re: New(?) pattern looking for a good home
                      843853
                      fromrussiawithjava wrote:
                      Damn, stupid me!
                      Yes I had Decorator in mind (honest) all along, Adapter has no role here whatsoever!
                      Sorry for this miswording. Please re-read my earlier post with Decorator instead of Adapter:
                      No problem, I totally sympathize -- design patterns have been, at times, giving me headaches ever since my last software engineering course.
                      You're probably doing them wrong, then.They're supposed to make things easier. If you're having any trouble with them, it's probably because you're applying them where they don't necessarily fit.

                      As soon as I read the sentence "In effect, it is like strategy pattern, except that the effect is achieved by wrapping, and wrapping can be done multiple times so the effect is cumulative" in your initial post, I knew you were describing the Decorator pattern. Later, when you said "but in reality works more like inheritance" you probably didn't realise that the Decorator pattern is used as an alternative to class inheritance, for extending functionality.

                      All in all, I wouldn't get too hung up on using patterns. If one or more fits your problem, by all means use it. If not, you can't really call whatever you do solve the problem with "a pattern" until you use it somewhere else, if you see what I mean.
                      • 8. Re: New(?) pattern looking for a good home
                        jduprez
                        splitting the interface into data-related and processing-related components certainly simplifies the whole thing
                        OK. From this point on...
                        [if the] original implementations (e.g. ChannelImplementation1 and ChannelImplementation2) for some reason can't or shouldn't be modified (perhaps because we don't want to break them or we don't have the source code). In that case we can't really solve the problem by splitting the interface.
                        Unless you accept to give up reusing the original Channel.getAverage() implementations. In that case you can still devise your own interfaces (if you really need it, see below discussion):
                        If the Channel interface is a given, you can still devise your own CustomChannel interface that includes only the read methods. Then you devise an Adapter (this time, I do mean it!) to adapt the 3rd-party Channel interface to your own more flexible CustomChannel (which of course is an awful name, just for the sake of illustrating).
                        If you need to add custom channel implementations, they will implement CustomChannel, not Channel. The adapter will be just one, blind adapter, implementation.
                        Oh and if you want to reuse one of the getAverage() implementations, I think there is a way (by adapting the Channel interface to the custom ChannelProcessor one).

                        All those "if" are underlined, as since you explained your problem in a mix of general and specific terms, it's not clear whether you really need custom implementations in addition to the existing ones. And in support of Geeorge's comment above, you should know patterns for what they are: a tuple of (problem, constraints, solution). You should apply the solution of a known patterns only if your context +(problem, forces)+ matches the known pattern's tuple.
                        • 9. Re: New(?) pattern looking for a good home
                          782681
                          jduprez wrote:
                          ...And in support of Geeorge's comment above, you should know patterns for what they are: [a tuple of (problem, constraints, solution)|http://en.wikipedia.org/wiki/Design_pattern_(computer_science)#Documentation|reference]. You should apply the solution of a known patterns only if your context (problem, forces) matches the known pattern's tuple...
                          FTFY (made a clickable reference link for your "tuple" statement)

                          // Usable documentation is what I really like in professional patterns descriptions like those in GoF, Wikipedia, POSA series. No matter how complicated is the implementation code, documentation allows me to follow the designer's reasoning: find out whether pattern is of interest to me, whether it can be useful in solving particular problems, whether it makes sense to study it thoroughly etc

                          Edited by: gnat on Mar 25, 2010 10:11 AM
                          • 10. Re: New(?) pattern looking for a good home
                            843853
                            Unless you accept to give up reusing the original Channel.getAverage() implementations. In that case you can still devise your own interfaces (if you really need it, see below discussion):
                            If the Channel interface is a given, you can still devise your own CustomChannel interface that includes only the read methods. Then you devise an Adapter (this time, I do mean it!) to adapt the 3rd-party Channel interface to your own more flexible CustomChannel (which of course is an awful name, just for the sake of illustrating).
                            If you need to add custom channel implementations, they will implement CustomChannel, not Channel. The adapter will be just one, blind adapter, implementation.
                            I agree, putting read methods and processing methods into separate interfaces makes any kind of delegation pattern much easier and it can be done just as you say, using the Adapter pattern on existing classes.
                            Oh and if you want to reuse one of the getAverage() implementations, I think there is a way (by adapting the Channel interface to the custom ChannelProcessor one).
                            Now this I am not so sure of. Without changing the implementation somehow, there is no way to have getAverage() operate on anything except the data inside the original object. Of course that's a weakness with my solution too -- overriding get() method will have the desired effect only if getAverage() relies on the get() method and doesn't just take data[i] elements directly from the internal array, so I am relying on the implementation in this particular case.

                            After some searching I am inclined to believe that your approach is an application of Composite Reuse Principle, whereas my solution is an attempt at implementing Dynamic Inheritance. There's an old thread here from someone who also sought, however unsuccessfully, to use dynamic inheritance in their programming. Here someone was trying to find a way to do the same thing in C#, at first I mistook it for Java because the syntax is so alike and was excited since they talk about a possibility of changing the class of an object using Reflection Emit. Then I realized it's C# reflection api... boogers. Incidentally, anyone here know of a Java way to do what C#'s Emit does? I'm not familiar with C# but someone at that forum seemed to think it might work (no one who answered my [previous post|http://forums.sun.com/thread.jspa?threadID=5431689] on this matter seemed to think it was possible but C#'s Emit and whether Java has an equivalent were never mentioned).

                            I will try to come up with a (problem, constraints, solution) description of the pattern when I have more time. Meanwhile regarding the combination of general and specific terms -- ultimately I am looking for a general solution, or perhaps I found one and just don't know what to use it for. I tried to put it in terms of my specific example since otherwise the abstract discussion could get totally incomprehensible. I know other people at least had some sort of instinctive urge to use dynamic inheritance, me included, so there's my solution and the question now I guess is whether the desire to use it is justified or not.