re-motion mixins basics -- overriding mixin methods
Having a target class overriding a mixin's method is easy to implement – just use the OverrideMixin
attribute. In the following code sample, the programmer is impatient with the OnThePhoneMixin
's Wait
method and writes his own for the Parrot
class. This overriding Wait
slashes the milliseconds in half:
[Uses (typeof(OnThePhoneMixin))] public class Parrot { public virtual void Whistle () { Console.WriteLine ("'River Kwai march' here..."); } public virtual void Say (string s) { Console.WriteLine ("\"{0}\"", s); } // The 'Wait' method is part of the 'IOnThePhone' interface, // implemented by the 'OnThePhone' class, which brings its own // 'Wait' implementation. We override that method here. The // 'OnThePhone's 'Wait' method must be virtual for this to // work [OverrideMixin] public virtual void Wait (int milliseconds) { Console.WriteLine ("[The parrot's (overridden) method]"); Thread.Sleep (milliseconds / 2); } }
OverrideMixin
only works if the mixin class is derived from the generic type Mixin<TThis>
or Mixin<TThis, TBase>
, as discussed in re-motion mixins basics -- overriding target methods#Target overrides Mixin?. This is the reason why the dessert wax sample listed in re-motion mixins basics -- overriding target methods#Target overrides Mixin? won't work (as hinted in the discussion following the listing).
The sophistication of the OverrideMixin
attribute lies not so much in the how, but in the when and why.
When to use the OverrideMixin
attribute
Note that in the example above we have attributed the target class with Uses
instead of the mixin class with Extends
. As pointed out on the wiki-page re-motion mixins basics -- the 'Uses' attribute and the 'ObjectFactory'#Using {{Uses}}, this is the rare case. In practice, the Extends
attribute for attributing the mixin class is far more popular.
This situation is reversed for target classes overriding mixin methods, because the presence of the OverrideMixin
attribute nullifies the assumption that target classes shall be independent of what is mixed to them. Think about it: If the mixin class already knows it is supposed to extend a particular target class (and therefore attributed with Extends
), then it would probably be cleaner to subclass the mixin class, override the method in that subclass and use the subclass for mixing. So when is it useful to use Uses
and OverrideMixin
?
This question is not easy to answer. The next section on this page explains how to use OverrideMixin
productively for the template method pattern.
The template method pattern
The template method pattern is useful for adding a "plan" or "procedural knowledge" to a target class. The wikipedia article on the subject gives the example of a simple engine for board games. The Chess
and Monopoly
specializations provide the methods for
- initializing the board (and the bank, in the case of monopoly)
- determining when a game has ended
- determining who has won
The superordinate "template", the abstraction of how to conduct such a board game, is encoded in the base class.
If you look at our Parrot
class (hierarchy) and the OnThePhoneMixin
you will see something very similar:
- the
Parrot
class knows how to say a given string - the
OnThePhoneMixin
class with itsPretendToTalkOnThePhone
method is the plan and the procedure of what to say when to give the audience the illusion that the speaker is having a phone conversation
The author believes that putting the "template", the "plan" or "procedure" into an extra mixin class is a much cleaner way of implementing the template method pattern than the route of implementation inheritance given in the wikipedia article, because it is more flexible.
For this scheme to work, the target class must be able to override methods from the mixin implementing the template. Specializations might require to modify some (or even all) methods, but other specializations might be served well by the defaults provided by the template. In the contrived parrot example from the beginning of this page, the specialization was that the template's Wait
method waited for too long, for example.