Subscribe to The Madman Scribblings        RSS Feed
-----

Damn you Inheritance. Damn you to Hell!

Icon 3 Comments
I'm throwing this problem out the community.

I would really like is a way of having methods that only exist on the base-class.

Token (The Base-Class)


<ComponentModel.ImmutableObject(True)>
Public MustInherit Class Token
  Private _Index As Integer
  Private _Count As Integer
  ' Constructor is only visible from inside Token and it Descendants '
  Protected Friend Sub New(ByVal Index As Integer, Count As Integer)
    _Index = Index : _Count = Count
  End Sub
  Public ReadOnly Property Index As Integer
    Get
      Return _Index
    End Get
  End Property
  Public ReadOnly Property Count As Integer
    Get
      Return _Count
    End Get
  End Property
  ' This parser is only visible from inside Token '
  ' Essentially a Factory Method, produce an Instance of a Digit (Token)
  Protected Shared Function Parser(ByRef ParseState As ParseState) As ParseState
    Return ParseState
  End Function

End Class



A Descendant: Digit

Public Class Digit
  Inherits Token
  Private Sub New(ByVal Index As Integer, Count As Integer)
    MyBase.New(Index, Count)
  End Sub
  Protected Overloads Shared Function Parser(ByRef cr As CharReader) As Digit
    Dim crc = cr.Current
    If crc.HasValue = False Then Return Nothing
    Select Case crc.Value
      Case "0"c To "9"c : Return New Digit(cr.Index, 1)
      Case Else : Return Nothing
    End Select
  End Function
End Class



OK. So on to the issue of the .Parse

Requirements
  • The only way to create a Token is via this a shared function on Token
    So I can control how they are constructed.
    Example
    Dim dt = Token.Parse(Of Digit)(Reader)
    so the following would an error.
    Dim dt = Digit.Parse would be an error, since Parse doesn't exist at all on the class Digit. Note: Not just hidden from the the IDE and Intellisense, I really meaning that any Derived Class of Token doesn't that is non-existent.



Since the way of creating a instance of any Derived Token is going be the same.
Strict Requirement:



Issues with what I've tried.
  • Use an Interface
    Can't specify Shared Methods.
  • Composition
    The Composed Class wouldn't be a Token, which it need to be it this design.
  • Inheritance
    Sounds reasonable.

    Public MustInherit Class Token
      ...
    
      Public Function Parse(Of T As Token)(ByRef cr As CharReader) As T
        Return Parser(cr)
      End Function
        ...
    
    

    no the Issue is the Parse method appear on only descendants.
    Sort of solve by doing.
      Protected Shadows Function Parse(ByRef cr As CharReader) As Digit
        Return Digit.Parse(cr)
      End Function
    
    

    now the Issue is how to access the shared method in the Derived Class?

    Since the Parse are essentially the same
    {ClassName}.Parse(cr)
    Why not find a way directly invoke (call) that hidden parser method from the base?

    Resorting to using Generics and Reflection
       Public Shared Function Parse(Of T As Token)(ByRef cr As CharReader) As ParseState
        Dim start = cr.Index
        '
        ' Here Be Nasty Reflection Based Bodge Job 
        '
        ' Why? What I want to write. ( Call a static method on the generic (constrianed) type specifier.)
        '
        ' Return T.Parser(cr)
        ' 
        ' Start Bodgery {
        Dim args As Object = {cr}
        Dim tt As T = GetType(T).InvokeMember("Parser",
                                        Reflection.BindingFlags.InvokeMethod +
                                        Reflection.BindingFlags.NonPublic +
                                        Reflection.BindingFlags.Static, Nothing, tt, args)
        cr = args(0) ' The ByRef Value get returned back into array.
        If tt Is Nothing Then cr = cr << start
        Return tt
        ' } End Bodgery
      End Function
    
    


    This leaves the issue of remove the .Parse from descendants.
    The only way I could find is extracting the parse code and placing it in Module.
    Public Module TokenLIB
      Public Function Parse(Of T As Token)(ByRef cr As CharReader) As T
        Dim start = cr.Index
        '
        ' Here Be Nasty Reflection Based Bodge Job 
        '
        ' Why? What I want to write. ( Call a static method on the generic (constrianed) type specifier.)
        '
        ' Return T.Parser(cr)
        ' 
        ' Start Bodgery {
        Dim args As Object = {cr}
        Dim tt As T = GetType(T).InvokeMember("Parser",
                                        Reflection.BindingFlags.InvokeMethod +
                                        Reflection.BindingFlags.NonPublic +
                                        Reflection.BindingFlags.Static, Nothing, tt, args)
        cr = args(0) ' The ByRef Value get returned back into array.
        If tt Is Nothing Then cr = cr << start
        Return tt
        ' } End Bodgery
      End Function
    
    


Which to me just feels so wrong, to me it should be in Token class.


So is there a way of define Base-Class only methods?




An Idea: Partial Inheritance.
A class can specify what aspect of it can be inherited.

Partial Inheritance
Only some of the traits of the parents are passed on the their children.

Public MustInherit Class Token
  MustOverride Protected Shared Function Parser(cr As CharReader ) As Token 
  NonInheritible Public Function Parse(Of T As Token)(cr As CharReader) As T
    Return (Of T).Parser(cr)
    ' Invoke a shared method on generic parameter type. '
  End Function
End Class


Deerived Class

Public Class Digit
  Inherit Token
  Overrides Protected Shared Function Parser(cr As CharReader ) As Token 
  End Function

  /* No Parse method exist for the child class */
End Class

3 Comments On This Entry

Page 1 of 1

cfoley Icon

20 February 2012 - 05:56 AM
My inclination is that if you don't want something to be inherited, it shouldn't be in the base class. Since your method is shared, you could easily put it in a class or module of its own.

In Java, static (shared) methods aren't inherited. I'm not sure how this applies to VB but static methods belongs to their class, not the subclasses. In older versions of Java, calling Digit.Parser() would have given you an error, although I think later versions allow it as syntactic sugar.

I don't like the idea of NonInheritable. It has the huge drawback of encouraging poor design, and I can't see any positives.
0

AdamSpeight2008 Icon

20 February 2012 - 08:41 AM
cfoley

Sadly there are inherited, if weren't I wouldn't have this issue.

derived <: base

Any method of base is inherited by the derived, including static/shared methods.
0

cfoley Icon

20 February 2012 - 08:59 AM
Well, that's OK too. Make it non-overrideable (if it isn't by default) and then you know any invocation ends up at the right piece of code.

Why don't you make a separate factory class or module?
0
Page 1 of 1