{"id":16,"date":"2019-05-28T01:19:44","date_gmt":"2019-05-28T01:19:44","guid":{"rendered":"https:\/\/www.great-white-software.com\/blog\/?p=16"},"modified":"2019-06-21T01:50:22","modified_gmt":"2019-06-21T01:50:22","slug":"an-extensible-class-factory","status":"publish","type":"post","link":"https:\/\/www.great-white-software.com\/blog\/2019\/05\/28\/an-extensible-class-factory\/","title":{"rendered":"An extensible class factory"},"content":{"rendered":"\n<p>Some times you want to make a nice generic class that a user can extend via subclassing.<\/p>\n\n\n\n<p>However, one issue arises with this.<\/p>\n\n\n\n<p>How can a factory method in the super class create instances of the subclasses ? This is especially troublesome if the classes you have are encrypted so you cant change the source or in a plugin.<\/p>\n\n\n\n<p>However, it IS possible to create an \u201cextensible factory method\u201d so the factory can create instances of the subclasses users provide.<\/p>\n\n\n\n<p>Lets look at how you can do this.<\/p>\n\n\n\n<p>Usually a class factory method looks something like<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Class MySuperClass\n  Private Sub Constructor()\n  End Sub\n\n  Shared Function CreateNew()\n    return new MySuperClass\n  End Function\nEnd Class\n<\/code><\/pre>\n\n\n\n<p>The private constructor makes it so no one can create an instance without using the factory.<\/p>\n\n\n\n<p>So how do you make this basic pattern extensible so the factory can create instances of the correct subclasses ?<\/p>\n\n\n\n<p>If you had the source code you could change the factory method to<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  Shared Function CreateNew(name as string)\n    select case name\n    case \u201cMyCustomSubclass\u201d\n       return new MyCustomSubclass\n    else\n      return new MySuperClass\n    end select\n  End Function\n<\/code><\/pre>\n\n\n\n<p>But there are some real problems with this<\/p>\n\n\n\n<p>the superclass has to be alterable &#8211; if you dont have source code you cant do this. So distributing code to other people to uses becomes a real problem<\/p>\n\n\n\n<p>every time you add a subclass you have to remember to alter the superclass<\/p>\n\n\n\n<p>What do you do if this is a plugin written in C++ ?<\/p>\n\n\n\n<p>As the author of a class thinking about how to solve these issues is really important. And this one is solvable. For pure Xojo code and for plugin authors.<\/p>\n\n\n\n<p>Recall that Introspection allows you to look at a class, and its constructors. As well you can invoke one and get back a new instance.<\/p>\n\n\n\n<p>So lets create a mechanism to let the superclass know about subclasses, and then use that knowledge to create the right instances when we need them.<\/p>\n\n\n\n<p>First lets add the \u201cway to know about subclasses\u201d<\/p>\n\n\n\n<p>At runtime the way to \u201cknow\u201d about other classes is .. introspection !<\/p>\n\n\n\n<p>And that knowledge is a typeinfo object.<\/p>\n\n\n\n<p>So our superclass needs a way to get the typeinfo\u2019s of subclasses and hang on to it for later use.<\/p>\n\n\n\n<p>Holding on to it for later is easy &#8211; an array of Introspection.TypeInfo objects will suffice.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  Dim mSubTypes() as Introspection.TypeInfo<\/code><\/pre>\n\n\n\n<p>Now how do we get all the info about subtypes ?&nbsp;<\/p>\n\n\n\n<p>We cant just go through all the objects that exist at runtime because if there are no instances of subtypes yet then we would not find any. So we need some way to \u201cregister\u201d the subtypes with the superclass so it can use this information later.<\/p>\n\n\n\n<p>So a method like<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  Shared Sub RegisterSubType( subType() as Introspection.TypeInfo)\n<\/code><\/pre>\n\n\n\n<p>that adds to the array we defined earlier should suffice<\/p>\n\n\n\n<p>Our class now looks like<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Class MySuperClass\n  Private Sub Constructor()\n  End Sub\n\n Shared Function CreateNew(name as string)\n    select case name\n    case \u201cMyCustomSubclass\u201d\n       return new MyCustomSubclass\n    else\n      return new MySuperClass\n    end select\n  End Function\n\n  Shared Sub RegisterSubType(\n         subType as Introspection.TypeInfo)\n    mSubtypes.Append subType\n  End Sub\n\n  Private Dim mSubTypes() as Introspection.TypeInfo\n\nEnd Class\n<\/code><\/pre>\n\n\n\n<p>And now all we really need to fix is the CreateNew method so it can return a subtype properly<\/p>\n\n\n\n<p>We\u2019ll leave the parameters as is for now &#8211; so we create \u201cby name\u201d<\/p>\n\n\n\n<p>This has some implications, like if you make a typo you wont get back the instance you asked for, but we can fix that later.<\/p>\n\n\n\n<p>The first thing I\u2019ll do is slightly alter the method signature so it has a default parameter.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> Shared Function CreateNew(name as string = \u201c\u201d)\n<\/code><\/pre>\n\n\n\n<p>And that\u2019s if for changing the method signature<\/p>\n\n\n\n<p>This allows us to do a tiny bit of optimization AND makes writing code a tiny bit easier<\/p>\n\n\n\n<p>So first things first &#8211; if NO name is supplied we just return a new instance of the superclass.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  if name = \u201c\u201d then\n    return new MySuperClass\n  end if<\/code><\/pre>\n\n\n\n<p>And after this we have to find the right \u201cTypeInfo\u201d and create an instance from that. And should we find no match we should return nil &#8211; which can be used as an error check to find any issues.<\/p>\n\n\n\n<p>Each TypeInfo object has a NAME property.<\/p>\n\n\n\n<p>So we an iterate through our list of registered TypeInfo objects, find the one with the right name, and invoke its Constructor.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  For i As Integer = 0 To mSubTypes.Ubound\n    \n    \/\/ find the type info with the right name\n    If mSubTypes(i).Name = subtype then\n    \n    \/\/ found it so get a list of its constructors\n    Dim cInfo() As Introspection.ConstructorInfo =\n                               mSubTypes(i).GetConstructors\n    \n    \/\/ and find the 0 param constructor\n    \/\/ since out factory does not pass along any params\n    For j As Integer = 0 To cInfo.Ubound\n      Dim pInfo() As Introspection.ParameterInfo = \n                                 cInfo(j).GetParameters\n      \n      \/\/ has no params ?\n      If pInfo.ubound &lt; 0 Then\n        \/\/ yes - invoke this one &amp; return the result\n        \/\/ which will be an instance of the right type\n        Return cInfo(j).Invoke\n      End If\n      \n    Next\n    \n  End If\n  \n  Next\n\n  \/\/ if we reach here we will be default return a nil \n  \/\/ or we could explicitly return nil<\/code><\/pre>\n\n\n\n<p>And there we go &#8211; a superclass with a factory method that can be extended by anyone &#8211; with or without source code !<\/p>\n\n\n\n<p>The end result is a class as follows<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Class MySuperClass\n  Private Sub Constructor()\n  End Sub\n\n  Shared Function CreateNew(name as string = \u201c\u201d)\n    if name = \u201c\u201d then\n      return new MySuperClass\n    end if\n\n   For i As Integer = 0 To mSubTypes.Ubound\n    \n      \/\/ find the type info with the right name\n      If mSubTypes(i).Name = subtype then\n    \n      \/\/ found it so get a list of its constructors\n      Dim cInfo() As Introspection.ConstructorInfo =\n                               mSubTypes(i).GetConstructors\n    \n      \/\/ and find the 0 param constructor\n      \/\/ since out factory does not pass along any params\n      For j As Integer = 0 To cInfo.Ubound\n        Dim pInfo() As Introspection.ParameterInfo = \n                                 cInfo(j).GetParameters\n      \n        \/\/ has no params ?\n        If pInfo.ubound &lt; 0 Then\n          \/\/ yes - invoke this one &amp; return the result\n          \/\/ which will be an instance of the right type\n          Return cInfo(j).Invoke\n        End If\n      \n      Next\n    \n    End If\n  \n    Next\n\n    \/\/ if we reach here we will be default return a nil \n    \/\/ or we could explicitly return nil\n  End Function\n\n  Shared Sub RegisterSubType(\n         subType as Introspection.TypeInfo)\n    mSubtypes.Append subType\n  End Sub\n\n  Private Dim mSubTypes() as Introspection.TypeInfo\n\nEnd Class<\/code><\/pre>\n\n\n\n<p>Something you might do is to make the factory NOT take a string but a typeinfo so that there\u2019s no \u201ctypo\u2019s\u201d to debug at runtime. Using a typeinfo would mean most typo issues would be caught at compile time.<\/p>\n\n\n\n<p>That\u2019s left as an exercise for the reader.<br><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Some times you want to make a nice generic class that a user can extend via subclassing. However, one issue arises with this. How can a factory method in the super class create instances of the subclasses ? This is especially troublesome if the classes you have are encrypted so you cant change the source &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.great-white-software.com\/blog\/2019\/05\/28\/an-extensible-class-factory\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;An extensible class factory&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[2],"class_list":["post-16","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-safe-code"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.great-white-software.com\/blog\/wp-json\/wp\/v2\/posts\/16","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.great-white-software.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.great-white-software.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.great-white-software.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.great-white-software.com\/blog\/wp-json\/wp\/v2\/comments?post=16"}],"version-history":[{"count":3,"href":"https:\/\/www.great-white-software.com\/blog\/wp-json\/wp\/v2\/posts\/16\/revisions"}],"predecessor-version":[{"id":156,"href":"https:\/\/www.great-white-software.com\/blog\/wp-json\/wp\/v2\/posts\/16\/revisions\/156"}],"wp:attachment":[{"href":"https:\/\/www.great-white-software.com\/blog\/wp-json\/wp\/v2\/media?parent=16"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.great-white-software.com\/blog\/wp-json\/wp\/v2\/categories?post=16"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.great-white-software.com\/blog\/wp-json\/wp\/v2\/tags?post=16"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}