2

There's a sort of pattern that I've sort of stumbled myself into "discovering" that seems extremely useful, but I've never seen it described before. It's sort of a way of achieving inheritance through an interface. It's really wierd where the class "becomes" the thing that it accepts. (Written here in C# but it doesn't have to be.)

interface IContainer {
   Thing Thing { get; }
}

class Concrete : IContainer {
   public Thing Thing { get; }

   public Concrete(IContainer container) {
      Thing = container.Thing;
   }

   // or...

   public Concrete() {
      Thing = BuildAnotherContainer().Thing;
   }
}

A more realistic example that is similar to what I really use:

interface IControlContainer {
   Control Control { get; }
}

public class FancyControl : IControlContainer {
   public Control Control { get; }

   public FancyControl() {
      Control = BuildControl().Control;
   }
}

BuildControl() is really code that builds an object from a script. The object is any one of many other IControlContainer objects that are designed to build controls using certain patterns (like table layout).

Notice how the outer object effectively "becomes" the inner object. Lately I've been thinking that it almost reminds me of prototypal inheritance in JavaScript, but I'm not sure. I would love to read more about this and find other ways of applying it, but I don't think I've ever seen it before and I can't find anything about it.

I know it isn't the Composite pattern, because that's about making a hierarchy/tree of objects.

I know it isn't just straight composition, because the point here is that to an outsider, there's no difference between the inner and outer objects (when viewed as IContainers), yet they do actually differ and have different implementations.

Here's an even more concrete example:

interface IControlContainer {
   Control UntypedControl { get; }
}

interface IControlContainer<TControl> : IControlContainer
where TControl : Control {
   TControl Control { get; }
}

class TableLayoutHelper : IControlContainer<TableLayoutPanel> {
   public Control UntypedControl => Control;
   public TableLayoutPanel Control { get; }

   public TableLayoutHelper() {
      Control = new TableLayoutPanel { Size = new Size(500, 500) };
   }

   // lots of code that makes building a UI with a table layout nice and easy
}

class EmployeeControl : IControlContainer {
   public Control UntypedControl { get; }

   public TextBox NameBox { get; }

   public EmployeeControl() {
      var tlh = new TableLayoutHelper();

      // use tlh to build a table layout        
      NameBox = tlh.AddTextBox("Name");

      UntypedControl = tlh.UntypedControl;
   }
}

class ControlContainerForm : Form {
   // a windows form that can host any IControlContainer
}

// then compose a ControlContainerForm with a new EmployeeControl

Notice how EmployeeControl "IS" a TableLayoutHelper, at least when looked at as an IControlContainer. The TLH itself can also place IControlContainers in it's table structure. And you can compose new IControlContainers out of existing ones, like I can place an EmployeeControl onto another IControlContainer somewhere else, and so on...

I've used an analogous system in a reporting framework as well and it works wonders.

  • What and where is BuildAnotherContainer()? – Euphoric Sep 9 '18 at 19:43
  • 1
    When you say they actually differ, could you provide an example of such a difference? If it is something like Control.Title = "Fancy " + Control.Title;, then we might be looking at a Decorator pattern. – Chris Wohlert Sep 9 '18 at 20:52
  • BuildAnotherContainer() is just any code that gets another one from somewhere else. – Dave Cousineau Sep 9 '18 at 23:03
  • @ChrisWohlert hmmm.... maybe it is Decorator... – Dave Cousineau Sep 9 '18 at 23:10
  • 1
    After your "even more concrete example" it looks like a doctored factory pattern where the produced object is not returned to the caller but rather stays wrapped in the factory that you create a new instance of in the process. Patterns are supposed to make life easier for people coming in after you. The confusion you are stirring and the trouble you are having explaining how this could be useful suggests this is not an existing pattern nor a new one. – Martin Maat Sep 10 '18 at 5:46
2

What you have shown in your examples is simply an Adapter a.k.a. Wrapper. You also extracted a get method for retrieving the wrapped object into an explicit interface, which is not necessarily part of that pattern, but probably useful for your case.

It is not a classic Decorator, since that would require the adapter to have a common interface with the wrapped object.

  • The intent of an adapter is to "change" interfaces. This is more about extending a class hierarchy without inheriting from it (I guess, I'm not really sure). I should have put in my question as well that maybe this is overcomplicated in some way and could be simplified. The way that assigning the container property to "myself" after the other container is finished with it effectively makes "me" the same thing even though I didn't do any of the work is the key part. And then another container can take my property after I'm done with it, etc. It's just so flexible and modular... – Dave Cousineau Sep 10 '18 at 8:02
  • There's no end to the ways that you can compose the objects this way. It just feels so fundamental, like this should be a known OOP building pattern or something. But maybe instead it is just equivalent to something else like regular composition...? I'm not sure. – Dave Cousineau Sep 10 '18 at 8:04
  • I guess an important part that I didn't think of is that in both the windows forms and reporting systems, the internal type (Control, and for my reporting system PrintElement) is a rough implementation of the composite pattern. (Controls have Controls have Controls.) Maybe that is the more important part. – Dave Cousineau Sep 10 '18 at 8:07
  • oh well, I guess this is as close as I'll get. thanks – Dave Cousineau Sep 11 '18 at 18:27
0

The pattern, if you insist on calling it that, is basically a copy constructor.

The first example being a shallow one and the second example being a deep one.

  • 1
    I added a more concrete example. I don't think it's a copy constructor, that's completely different. It sounds like it may be Decorator but I'm not sure. – Dave Cousineau Sep 9 '18 at 23:24
0

This looks like the Strategy pattern to me. Your ControlContainerForm is the Context, your IControlContainer is the Strategy that changes the way the form works

+--------------------+     +-----------------+
|ControlContainerForm+---->+IControlContainer|
+--------------------+     +-----+-----+-----+
                                 ^     ^
                                 |     |
                +----------------++  +-+-------------+
                |IControlContainer|  |EmployeeControl|
                +-----------------+  +---------------+
  • I don't think it's Strategy. The Strategy pattern is about varying behaviour by swapping implementations at runtime. Stick in a ThingA, and your system runs in Mode A. Swap it for a ThingB, and it runs in Mode B. Etc. – Dave Cousineau Sep 10 '18 at 0:01
  • I'm assuming his ControlContainerForm runs differently depending on what control container it contains. – Daniel T. Sep 10 '18 at 0:02
  • The ControlContainerForm is just the place the objects ultimately get used; it's only mentioned for demonstration. The pattern is the idea of having IContainers, where you have the inner type (Control) which is being modified by the outer types (Containers). The containers pass the controls around, modifying them in their own ways. Someone mentioned Decorator, but I don't know if it's quite the same; it could be a variant of Decorator. – Dave Cousineau Sep 10 '18 at 0:05
  • It's not Decorator because a decorator IContainer would be the sole owner of the IContainer that is passed into it. It looks like all your idea does is allow multiple different IContainer's to modify a Thing. Thus breaking encapsulation. Maybe the Object Orgy anti-pattern. – Daniel T. Sep 10 '18 at 0:17
  • It doesn't "break encapsulation" just because it passes one object along. Each class has its own internals respective to its own role in the modification process. – Dave Cousineau Sep 10 '18 at 0:34

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.