Changing design midstream

  • Thread starter Thread starter Helmut Giese
  • Start date Start date
H

Helmut Giese

Hello out there,
as part of a 'designer like' program I have a class
class SuperPanel : Panel {
}
with functions like addWidget, rmWidget, cloneWidget, serialize and
deserialize, support for drag and drop, etc.

All fine and well, but I missed out on one thing: I need the same for
a GroupBox - conceptually something like
class SuperGroupBox : GroupBox {
}
with exactly the same set of functions.

Now I certainly don't want to duplicate all the code, but I haven't
been able to find a satisfactory solution:
- I don't see a common base class
- I don't see a sensible way to build an inheritance chain
- I thought of creating a class which would not _be_ a Control but
rather _contain_ a Control like
class Container {
Panel panel;
// Create either a Panel or a GroupBox
Container(bool isPanel) {
if (isPanel)
panel = new Panel();
else
panel = new GroupBox();
}
}
but this would break lots of code where I am using an instance of my
class directly as a Control, e.g when doing something like
Control self;
...
(self as SuperPanel)...

So it looks like I painted myself into a corner - does anybody know a
way out?
Any idea will be massively appreciated.
Best regards
Helmut Giese
 
Hi Helmut,

If I got you right you put some code into your specific panal child
class.
Now you have indentified that code common for not only the panal sub
class but another familiar class.
As C# does not allow inheritance of multiple classes you need another
way of sharing the common methods.

I would suggest the following way:

Encapsulate the common functionality in a seperate class and aggregate
it from both of your child classes.
This would follow the OOP principles: "prefer composition over
inheritance" or "single responsibility pattern" or "seperation of
concerns principle"

http://en.wikipedia.org/wiki/Separation_of_concerns
http://blogs.msdn.com/steverowe/archive/2008/04/28/prefer-composition-over-inheritance.aspx

I cannot estimate the impact of code changes for that refactoring but
seperating concerns enables results in code that is better testable
and readable.


Cheers, Sebastian
 
Helmut said:
Hello out there,
as part of a 'designer like' program I have a class
class SuperPanel : Panel {
}
with functions like addWidget, rmWidget, cloneWidget, serialize and
deserialize, support for drag and drop, etc.

[...]
Now I certainly don't want to duplicate all the code, but I haven't
been able to find a satisfactory solution:
[...]

So it looks like I painted myself into a corner - does anybody know a
way out?

Caveat: without being completely immersed in your design process, I
could easily give inapplicable advice.

That said…

It seems to me that this may be a good case for composition rather than
inheritance, just as you seem to have considered already. I believe
that with some effort, you can successfully using composition without
sacrificing the use of your container class as a Control.

Unfortunately, the approach that I'd consider the simplest – to override
the Controls property and delegate management of the children to
whatever container class is composed into your control – doesn't really
seem supported very well by .NET, because the Controls property isn't
virtual. :(

But, you can flip the implementation around, and create a helper class
that can be composed into a Panel and a GroupBox sub-class, delegating
all the "widget" features to that helper class.

If you like, you can even make the features in your helper class part of
an interface. Then, make you new Panel and GroupBox sub-classes
implement the interface. With that, then Visual Studio will easily
create all of the various stubs in your classes for the interface, which
you can then just go through and "implement" simply by calling the
appropriate member in the helper class.

If I think of any other suggestions later, I'll post again. But for
now, that's what comes to mind.

Pete
 
Hi Sebastian,
Encapsulate the common functionality in a seperate class and aggregate
it from both of your child classes.
This would follow the OOP principles: "prefer composition over
inheritance" or "single responsibility pattern" or "seperation of
concerns principle"
'separation of concern' is always a Good Thing (TM).
Yes, composition seems the way to go (Peter's suggestion appears to go
into the same direction).
I cannot estimate the impact of code changes for that refactoring
Well, I will have to bite the bullet one way or the other.

Thanks for the links and the idea.
Best regards
Helmut Giese
 
Hi Peter,
Caveat: without being completely immersed in your design process, I
could easily give inapplicable advice.
don't worry ...
It seems to me that this may be a good case for composition rather than
inheritance, just as you seem to have considered already. I believe
that with some effort, you can successfully using composition without
sacrificing the use of your container class as a Control.
But, you can flip the implementation around, and create a helper class
that can be composed into a Panel and a GroupBox sub-class, delegating
all the "widget" features to that helper class.
Now that's an interesting idea: It would certainly make life easier if
my class would remain usable as a Control.
If you like, you can even make the features in your helper class part of
an interface. Then, make you new Panel and GroupBox sub-classes
implement the interface.
This I did not understand - but then I never so far implemented an
interface. What would be the syntax
a) to be 'part of an interface' (as opposed to _being_ an interface)?
b) to have MyPanel inherit from Panel _and_ implement the interface?
With that, then Visual Studio will easily
create all of the various stubs in your classes for the interface, which
you can then just go through and "implement" simply by calling the
appropriate member in the helper class.
Sounds cool (VS working for me) - but to tell the truth: you lost me
here.

In any case, your (and Sebastian's) suggestion to use compostion seems
to be the way out of my corner.
Thanks and best regards
Helmut Giese
 
Helmut said:
[...]
If you like, you can even make the features in your helper class part of
an interface. Then, make you new Panel and GroupBox sub-classes
implement the interface.

This I did not understand - but then I never so far implemented an
interface. What would be the syntax
a) to be 'part of an interface' (as opposed to _being_ an interface)?

I simply mean that those custom members you described earlier would
define the interface you declare.
b) to have MyPanel inherit from Panel _and_ implement the interface?

A class can declare any number of interfaces it implements, but can
inherit just one other class. So your panel would inherit Panel, but
implement the interface describing the additional functionality.

The interface approach is optional. You can accomplish this without the
interface at all. It's just a way to get Visual Studio to do some of
the typing for you (at the expense of a little more typing elsewhere).

Here's a basic example of what I mean (using the member names you stated
earlier…I don't actually like the naming convention much :p ):

interface IWidgetSupport
{
void addWidget(Widget widget);
void rmWidget(Widget widget);
Widget cloneWidget(Widget widget);
// etc.
}

(Note: it's not clear why the "cloneWidget()" method isn't just in
Widget as an implementation of ICloneable).

Anyway, then you'd have the implementation:

class WidgetSupportImpl : IWidgetSupport
{
public void addWidget(Widget widget)
{
// implementation goes here
}

public void rmWidget(Widget widget)
{
// implementation goes here
}

public Widget cloneWidget(Widget widget)
{
// implementation goes here
}
}

class MyPanel : Panel, IWidgetSupport
{
private IWidgetSupport _ws = new WidgetSupportImpl();

public void addWidget(Widget widget)
{
_ws.addWidget(widget);
}

public void rmWidget(Widget widget)
{
_ws.rmWidget(widget);
}

public Widget cloneWidget(Widget widget)
{
return _ws.addWidget(widget);
}
}

Note that when you add the ", IWidgetSupport" text to the class
declaration, changing this:

class MyPanel : Panel
{
}

to this:

class MyPanel : Panel, IWidgetSupport
{
}

…you can right-click on IWidgetSupport and one of the choices will be to
automatically generate the stub implementation in the class (or you can
just get the cursor there, push Ctrl-Period, and a little menu will show
up with the option…and this works in the WidgetSupportImpl class too).
Then, all you have to do is scroll down to the newly-added stubs and
insert your actual implementation.

Again, the interface is not really necessary. All of the above would
work just fine without the interface being part of the approach. It's
just that the interface makes it a little easier to organize the code
and to ensure you haven't left anything out. But, to me those things
are useful and suggest that using an interface, though not required, is
still desirable.

Pete
 
Thanks Peter,
your explanation and example were really helpful and much appreciated.
Best regards
Helmut Giese

Helmut said:
[...]
If you like, you can even make the features in your helper class part of
an interface. Then, make you new Panel and GroupBox sub-classes
implement the interface.

This I did not understand - but then I never so far implemented an
interface. What would be the syntax
a) to be 'part of an interface' (as opposed to _being_ an interface)?

I simply mean that those custom members you described earlier would
define the interface you declare.
b) to have MyPanel inherit from Panel _and_ implement the interface?

A class can declare any number of interfaces it implements, but can
inherit just one other class. So your panel would inherit Panel, but
implement the interface describing the additional functionality.

The interface approach is optional. You can accomplish this without the
interface at all. It's just a way to get Visual Studio to do some of
the typing for you (at the expense of a little more typing elsewhere).

Here's a basic example of what I mean (using the member names you stated
earlier…I don't actually like the naming convention much :p ):

interface IWidgetSupport
{
void addWidget(Widget widget);
void rmWidget(Widget widget);
Widget cloneWidget(Widget widget);
// etc.
}

(Note: it's not clear why the "cloneWidget()" method isn't just in
Widget as an implementation of ICloneable).

Anyway, then you'd have the implementation:

class WidgetSupportImpl : IWidgetSupport
{
public void addWidget(Widget widget)
{
// implementation goes here
}

public void rmWidget(Widget widget)
{
// implementation goes here
}

public Widget cloneWidget(Widget widget)
{
// implementation goes here
}
}

class MyPanel : Panel, IWidgetSupport
{
private IWidgetSupport _ws = new WidgetSupportImpl();

public void addWidget(Widget widget)
{
_ws.addWidget(widget);
}

public void rmWidget(Widget widget)
{
_ws.rmWidget(widget);
}

public Widget cloneWidget(Widget widget)
{
return _ws.addWidget(widget);
}
}

Note that when you add the ", IWidgetSupport" text to the class
declaration, changing this:

class MyPanel : Panel
{
}

to this:

class MyPanel : Panel, IWidgetSupport
{
}

…you can right-click on IWidgetSupport and one of the choices will be to
automatically generate the stub implementation in the class (or you can
just get the cursor there, push Ctrl-Period, and a little menu will show
up with the option…and this works in the WidgetSupportImpl class too).
Then, all you have to do is scroll down to the newly-added stubs and
insert your actual implementation.

Again, the interface is not really necessary. All of the above would
work just fine without the interface being part of the approach. It's
just that the interface makes it a little easier to organize the code
and to ensure you haven't left anything out. But, to me those things
are useful and suggest that using an interface, though not required, is
still desirable.

Pete
 
Back
Top