views:

71

answers:

3

In C#, I have a class hierarchy with a couple of abstract base classes near the top and a fair number of derived classes. A few these concrete classes have some common properties and methods that are implemented identically. It strikes me as wasteful and so one solution might be to implement this common behaviour in another abstract base class.

abstract class Control;

abstract class SquareControl: Control
{
    public int SquarishProperty;
    public void SquarishMethod();
};

class Window: SquareControl;
class Button: SquareControl;

However, what if several other classes in the hierarchy shared some other behaviour but also share something in common with one of the controls from another base class? Perhaps there are lots of areas of commonality. It would become impractical to model this with abstract base class implementation wouldn't it?

abstract class FlashableControl: Control
{
    public int FlashyProperty;
    public void FlashMethod();
};

class StatusBar: FlashableControl;  // but it's also a bit square too, hmm...

So how do you go about sharing such implementations across classes without using base classes?

I imagine I want to delegate the implementaion of an interface to another class and have that class implement those properties and methods on behalf of the desired classes, so that to the user, the StatusBar and Window appear to support a standard interface, but under the covers it's something else that implements it.

I can visualise aggregating classes that implement this behaviour, but is this appropriate and are there any pitfalls? What are the alternatives?

Thanks

+3  A: 

You could use 'has-a' instead of 'is-a' and delegate to an internal square control

    class Window : Control, ISquareControl
    {
        private SquareControl square;

        public void SquareOperation()
        {
            square.SquareOperation();
        }
    }

    class SquareControl : Control, ISquareControl
    {
        public void SquareOperation()
        {
            // ...
        }
    }
BioBuckyBall
This is pretty much what I had in mind with the aggregation approach, and I guess I might want to keep the SquareControl class internal so that it is hidden from the users of Window.Are there any pitfalls with this approach, limitations or gotchas?
DaBozUK
A: 

One way is to use Interfaces and Base Classes.

Flashable would make a good Interface instead of a class.

Patrick Säuerl
The interface and base class is pretty much what I described above that I wanted to avoid. The problem being that the class hierarchy can get messy if several different sets of classes share some but not all behaviour.
DaBozUK
+2  A: 

You can use a pattern like this:

public interface ICommonServices
{
    string SomeProperty { get; set; }

    void SomeMethod(string param);
}

public static class CommonServiceMethods
{
    public static void DoSomething(this ICommonServices services, string param)
    {
        services.SomeMethod(services.SomeProperty + ": " + param + " something extra!");
    }
}

All classes that implement ICommonServices now also get some free behavior via the extension method, which depends solely on those features exposed by all ICommonServices implementers. If you need access to base class functionality, you can put that in its own interface and have ICommonServices implement that interface as well. Now you can create 'default' extension functionality for interfaces without having to use multiple base classes.


EDIT

If you want some of these methods to be internal, you can modify the pattern like this:

public class MyObject : IServices
{
    public string PublicProperty { get; private set; }

    string IServices.SomeProperty { get; set; }

    void IServices.SomeMethod(string param)
    {
        //Do something...
    }
}

public interface IPublicServices
{
    string PublicProperty { get; }
}

internal interface IServices : IPublicServices
{
    string SomeProperty { get; set; }

    void SomeMethod(string param);
}

internal static class ServiceMethods
{
    public static void DoSomething(this IServices services, string param)
    {
        services.SomeMethod(services.SomeProperty + ": " + param + " something extra!");
    }
}

Basically we're exposing both public and internal interfaces. Note that we implement the internal interface methods explicitly, so that the methods are not available for public consumption (since the public client can't get access to the interface type.) In this case, the helper extension methods are internal, relying on the internal interface, though you could also create public helper methods that rely on the public interface.

Dan Bryant
Microsoft Unity IoC containers also uses this trick.
Lex Li
This approach I really like and will have a go at implementing it. What's the best approach to concealing this class from client code in other assemblies?Boz
DaBozUK
@DaBozUK, I've amended the answer with an example for how to provide only internal access
Dan Bryant
On a side note, the explicit implementation means that the methods will not be available to your internal clients if you have an instance of the class itself (and not the interface). You can get around this by creating internal methods with the same name that the explicit interface overrides call.
Dan Bryant