All entries for Wednesday 14 March 2007
March 14, 2007
On the way home I thought of a way to override implementation class members in the user class.
class User use SomeImpl for IFooBar [OverrideImpl(IFooBar)] private SomeMethod() : void // specialized code... class SomeImpl : IFooBar public virtual SomeMethod() : void // Normal code
The macro can translate this into “real” code by creating a new class inside User that inherits from the implementation class. We override the virtual method and make call to the User method.
The partial expansion then looks like this:
class User use SomeImpl_User_Overrides for IFooBar private SomeMethod() : void // Specialized code class SomeImpl_User_Overrides : SomeImpl private _user : User public this(user : User) _user = user public override SomeMethod() _user.SomeMethod()
So then we use the specialized class as the implementation.
This approach will also work for abstract methods defined in the implementation class that require implementation.
I’ve been thinking more about traits and implementation classes in OOP.
I put together a quick program in Nemerle to demonstrate the ideas I’m playing with.
I defined a class that implements System.ComponentModel.IEditableObject. It takes a “user” object as a parameter and implements a simple stack based state saver. BeginEdit copies all the fields of the user, pushing them onto the stack. CancelEdit can then restore state by popping the stack and setting the fields back to those saved values. EndEdit simply pops the stack leaving the current values in place.
class EditableObject[T] : IEditableObject private _states : Stack[Dictionary[FieldInfo, object]] = Stack() private _user : T // We only take the reflection performance hit once per type T. private static _fields : array[FieldInfo] static this() _fields = typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance) public this(user : T) _user = user public BeginEdit() : void _states.Push(CopyState()) public EndEdit() : void if (_states.Count > 0) _ = _states.Pop() // Discard top of stack else throw InvalidOperationException() public CancelEdit() : void if (_states.Count > 0) RestoreState(_states.Pop()) else throw InvalidOperationException() private CopyState() : Dictionary[FieldInfo, object] def dictionary = Dictionary() foreach (f in _fields) dictionary[f] = f.GetValue(_user) dictionary private RestoreState(previous : Dictionary[FieldInfo, object]) : void foreach (f in _fields) f.SetValue(_user, previous[f])
I now want to use this “implementation” class in many different classes. I do not want to inherit from this class directly. Trying to create an abstract base class purely to stuff lots of different implementation details together is just plain ugly too.
So I used the Nemerle ProxyPublicMembers macro.
class Person : IEditableObject [ProxyPublicMembers] _editable : EditableObject[Person] = EditableObject(this) [Accessor(flags=WantSetter)] mutable _firstName : string [Accessor(flags=WantSetter)] mutable _surname : string public this(firstName : string, surname : string) _firstName = firstName _surname = surname public override ToString() : string $"$FirstName $Surname"
With the single line:
[ProxyPublicMembers] _editable : EditableObject[Person] = EditableObject(this)
the three public methods BeginEdit, CancelEdit, EndEdit are inserted into Person and they simply call the respective methods on the _editable instance field.
A simple test of the code is:
static class Program static Main() : void def p1 = Person("Andrew", "Davey") Console.WriteLine(p1) p1.BeginEdit() p1.FirstName = "Foo" Console.WriteLine(p1) p1.CancelEdit() Console.WriteLine(p1) p1.BeginEdit() p1.Surname = "Smith" p1.EndEdit() Console.WriteLine(p1)
Andrew Davey Foo Davey Andrew Davey Andrew Smith
One of the advantages of this implementation class approach is the ability to store both instance and static state. The EditableObject only takes the reflection performance hit to get a type’s fields once. The user class never has to know about this state at all.
It would also be possible to enable a form of “overriding” by the user class. Perhaps it needs a special way to save and restore state. We just need a way to tell the implementation class to call the user to do something, rather than doing it itself. Delegates could be passed to the implementation class upon construction. Or perhaps a more declarative approach could be taken by marking “override” methods in a user class with an attribute.
[OverrideImplementation(EditableObject)] private CopyState() : Dictionary[FieldInfo, object] // do stuff here
We would just require some macro trickery to get this working I reckon.
As it stands the standard ProxyPublicMethods macro in Nemerle enables an excellent approach to modelling problems in clean OO style. I think an additional macro to tidy up the syntax would make things even nicer.
class Person uses EditableObject for IEditableObject