Implementation Classes Again
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)
Outputting:
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
Maybe?

Loading…
Add a comment
You are not allowed to comment on this entry as it has restricted commenting permissions.