As I can't use true multiple inheritance in C#, how would I design
this to be able to share the methods and properties?
This is the right way to share the methods and properties. But:
The trouble now is that in my extendedTextBox class I can't cast my
extendedTextBox as a 'BaseExtendedControl' to use the extension
methods, as the extendedTextBox inherits from the standard TextBox
class like this:
public class ExtendedTextBox : TextBox, IExtendedControl
so there's no common base to cast ExtendedTextBox as
'BaseExtendedControl' as it doesn't inherit from it.
Yes, you're right there is no common class, but you can solve the problem by casting to the IExtentedControl
interface which is common to your classes and contains the methods that you need. So what about:
public static class ExtendedControlHelper
{
public static void AddRequiredFieldValidator(this IExtendedControl control)
{
control.rfv = new RequiredFieldValidator();
control.rfv.ErrorMessage = control.RequiredMessage;
ConfigureAndAddValidator(control, control.rfv);
}
public static void ConfigureAndAddValidator(this IExtendedControl control, BaseValidator validator)
{
validator.ControlToValidate = control.ID;
validator.Display = ValidatorDisplay.Dynamic;
validator.CssClass = "validationMessage ";
validator.CssClass += control.ValidatorCssClass;
validator.EnableClientScript = control.ClientScript;
control.Controls.Add(validator);
}
}
I don't think you need to implement the IExtendedControl
interface in the ExtendedControlHelper
class. Moreover I corrected the grammar of the interface name from IExtendendControl
to IExtendedControl
.
EDIT :
Another solution could be to use the dynamic
keyword. The dynamic keyword is like object
but the type is checked at run time instead of compile time:
public static class ExtendedControlHelper
{
public static void AddRequiredFieldValidator(this dynamic control)
{
//...
}
public static void ConfigureAndAddValidator(this dynamic control, BaseValidator validator)
{
//...
}
}
The dynamic type doesn't exist at run time because it's solved by the compiler, so if you write this: myExtendedTextBox.AddRequiredFieldValidator();
the method is solved as
//...
public static void AddRequiredFieldValidator(this ExtendedTextBox control)
{
//...
}
and so on. So the type can change continuously at run time.
For more informations give a look here.
I think you are on the road to a very often seen form of wrong use of inheritance. If you want a class X
which uses an exchangeable type of logger, inject an object of type ILog
by passing it as a constructor parameter. If your logger needs specific initialization, create a specific logger, inherited from ILog
. Don't bury the logger initialization in some kind of artifical base class A
of X
, in attempt to make this reusable. The latter would actually work against reusability (and testability) of X
, and also against the reusability of your specific logger outside of the context of X
.
Best Answer
As usual, there are several different ways. Here are a few possible ideas for you to investigate:
Inheritance
I would not realize this via inheritance - especially not if you want multiple such roles for a member. Even with just one role, there's the composition over inheritance principle that tells you to avoid doing that if possible. It reduces your potential for reuse dramatically. Anyways, with your requirement that you want to be able to support multiple roles for a member, you would have to provide subclasses for each combination, which results in an exponential explosion of class combinations. Not very feasible.
Decorator
As you pointed out the decorator pattern would be one possible way, but would still require a lot of work in wrapping the original
Member
and providing all of its methods via the decorated object as well.Composition
Composition is a straightforward way that you could use to add a role to a
Member
, if you can define a common interface for the roles, i.e.,Supporter
,Volunteer
, etc. would implement the interfaceMemberRole
and theMember
class gets an attribute of typeMemberRole
(or a list thereof).If, however, the roles each have different methods, composition doesn't work so well anymore (f.ex. a
Supporter
hassupport()
, but aPlayer
hasplay()
), because you cannot find a common interface for the roles.Traits/Mixins
As you didn't tell us which programming language you want to use, let me add one more option that would be interesting, but harder to realize in some languages. As an example, consider Scala with its support for traits. They are a perfect match for your problem, as you could do the following:
Composing traits like this essentially saves you the exponential explosion mentioned above, as you do not need to provide a specific class for a MemberSupporterPlayer and such. An additional advantage is the type-safety shown above that ensures you cannot call methods from a role that your member does not possess.
For something similar in C# you can consider Mixins. See for example the remix project for this. They essentially allow the same kind of composition, but of course use a different syntax.