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 IContainer
s), 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 IControlContainer
s in it's table structure. And you can compose new IControlContainer
s 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.
3 Answers 3
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– Dave Cousineau2018年09月10日 08:02:31 +00:00Commented Sep 10, 2018 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– Dave Cousineau2018年09月10日 08:04:39 +00:00Commented Sep 10, 2018 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 systemPrintElement
) is a rough implementation of the composite pattern. (Controls have Controls have Controls.) Maybe that is the more important part.Dave Cousineau– Dave Cousineau2018年09月10日 08:07:05 +00:00Commented Sep 10, 2018 at 8:07 -
oh well, I guess this is as close as I'll get. thanksDave Cousineau– Dave Cousineau2018年09月11日 18:27:15 +00:00Commented Sep 11, 2018 at 18:27
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.
-
1I 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– Dave Cousineau2018年09月09日 23:24:39 +00:00Commented Sep 9, 2018 at 23:24
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– Dave Cousineau2018年09月10日 00:01:16 +00:00Commented Sep 10, 2018 at 0:01
-
I'm assuming his ControlContainerForm runs differently depending on what control container it contains.Daniel T.– Daniel T.2018年09月10日 00:02:37 +00:00Commented Sep 10, 2018 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 havingIContainer
s, 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– Dave Cousineau2018年09月10日 00:05:53 +00:00Commented Sep 10, 2018 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.– Daniel T.2018年09月10日 00:17:52 +00:00Commented Sep 10, 2018 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– Dave Cousineau2018年09月10日 00:34:10 +00:00Commented Sep 10, 2018 at 0:34
Explore related questions
See similar questions with these tags.
BuildAnotherContainer()
?Control.Title = "Fancy " + Control.Title;
, then we might be looking at a Decorator pattern.BuildAnotherContainer()
is just any code that gets another one from somewhere else.