Factories: Parameterized Object Initialization

Creating objects is something we do all the time. When we Set foo = New Something, we create a new instance of the Something class and assign that object reference to the foo variable, which would have been declared locally with Dim foo As Something.

With New

Often, you wish to instantiate Something with initial values for its properties – might look like this:

Dim foo As Something
Set foo = New Something
With foo
    .Bar = 42
    .Ducky = "Quack"
    '...
End With

Or, you could be fancy and make Something have a Self property that returns, well, the instance itself, like this:

Public Property Get Self() As Something
    Set Self = Me
End Property

But why would we do that? Because then we can leverage the rather elegant With New syntax:

Dim foo As Something
With New Something
    .Bar = 42
    .Ducky = "Quack"
    '...
    Set foo = .Self
End With

The benefits are perhaps more apparent with a factory method:

Public Function NewSomething(ByVal initialBar As Long, ByVal initialDucky As String) As Something
    With New Something
        .Bar = initialBar
        .Ducky = initialDucky
        Set NewSomething = .Self
    End With
End Function

See, no local variable is needed here, the With block holds the object reference. If we weren’t passing that reference down the call stack by returning it to the caller, the End With would have terminated that object. Not everybody knows that a With block can own an object reference like this, using With New. Without the Self property, a local variable would be needed in order to be able to assign the return value, because a With block doesn’t provide a handle to the object reference it’s holding.

Now the calling code can do this:

Dim foo As Something
Set foo = Factories.NewSomething(42, "Quack")

Here the NewSomething function is located in a standard module (.bas) named Factories. The code would have also been legal without qualifying NewSomething with the module name, but if someone is maintaining that code without Rubberduck to tell them by merely clicking on the identifier, meh, too bad for them they’ll have to Shift+F2 (go to definition) on NewSomething and waste time and break their momentum navigating to the Factories module it’s defined in – or worse, looking it up in the Object Browser (F2).

Where to put it?

In other languages, objects can be created with a constructor. In VBA you can’t have that, so you use a factory method instead. Factories manufacture objects, they create things.

In my opinion, the single best place to put a factory method isn’t in a standard/procedural module though – it’s on the class itself. I want my calling code to look something like this:

Dim foo As Something
Set foo = Something.Create(42, "Quack")

Last thing I want is some “factory module” that exposes a method for creating instances of every class in my project. But how can we do this? The Create method can’t be invoked without an instance of the Something class, right? But what’s happening here, is that the instance is being automatically created by VBA; that instance is named after the class itself, and there’s a VB_Attribute in the class header that you need to tweak to activate it:

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "Something"      '#FunFact controlled by the "Name" property of the class module
Attribute VB_GlobalNameSpace = False '#FunFact VBA ignores this attribute
Attribute VB_Creatable = False       '#FunFact VBA ignores this attribute
Attribute VB_PredeclaredId = True    '<~ HERE!
Attribute VB_Exposed = False         '#FunFact controlled by the "Instancing" property of the class module

The attribute is VB_PredeclaredId, which is False by default. At a low level, each object instance has an ID; by toggling this attribute value, you tell VBA to pre-declare that ID… and that’s how you get what’s essentially a global-scope free-for-all instance of your object.

That can be a good thing… but as is often the case with forms (which also have a predeclared ID), storing state in that instance leads to needless bugs and complications.

Interfaces

The real problem is that we really have two interfaces here, and one of them (the factory) shouldn’t be able to access instance data… but it needs to be able to access the properties of the object it’s creating!

If only there was a way for a VBA class to present one interface to the outside world, and another to the Create factory method!

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "ISomething"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

Public Property Get Bar() As Long
End Property

Public Property Get Ducky() As String
End Property

This would be some ISomething class: an interface that the Something class will implement.

The Something class would look like this- Notice that it only exposes Property Get accessors, and that the Create method returns the object through the ISomething interface:

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "Something"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
Private Type TSomething
    Bar As Long
    Ducky As String
End Type

Private this As TSomething
Implements ISomething

Public Function Create(ByVal initialBar As Long, ByVal initialDucky As String) As ISomething
    With New Something
        .Bar = initialBar
        .Ducky = initialDucky
        Set Create = .Self
    End With
End Function

Public Property Get Self() As ISomething
    Set Self = Me
End Property

Public Property Get Bar() As Long
    Bar = this.Bar
End Property

Friend Property Let Bar(ByVal value As Long)
    this.Bar = value
End Property

Public Property Get Ducky() As String
    Ducky = this.Ducky
End Property

Friend Property Let Ducky(ByVal value As String)
    this.Ducky = value
End Property

Private Property Get ISomething_Bar() As Long
    ISomething_Bar = Bar
End Property

Private Property Get ISomething_Ducky() As String
    ISomething_Ducky = Ducky
End Property

The Friend properties would only be accessible within that project; if that’s not a concern then they could also be Public, doesn’t really matter – the calling code only really cares about the ISomething interface:

With Something.Create(42, "Quack")
    Debug.Print .Bar 'prints 42
    .Bar = 42 'illegal, member not on interface
End With

Here the calling scope is still tightly coupled with the Something class though. But if we had a factory interface…

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "ISomethingFactory"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

Public Function Create(ByVal initialBar As Long, ByVal initialDuck As String) As ISomething
End Function

…and made Something implement that interface…

Implements ISomething
Implements ISomethingFactory

Public Function Create(ByVal initialBar As Long, ByVal initialDucky As String) As ISomething
    With New Something
        .Bar = initialBar
        .Ducky = initialDucky
        Set Create = .Self
    End With
End Function

Private Function ISomethingFactory_Create(ByVal initialBar As Long, ByVal initialDucky As String) As ISomething
    Set ISomethingFactory_Create = Create(initialBar, initialDucky)
End Function

…now we basically have an abstract factory that we can pass around to everything that needs to create an instance of Something or, even cooler, of anything that implements the ISomething interface:

Option Explicit

Public Sub Main()
    Dim factory As ISomethingFactory
    Set factory = Something.Self
    With MyMacro.Create(factory)
        .Run
    End With
End Sub

Of course this is a contrived example. Imagine Something is rather some SqlDataService encapsulating some ADODB data access, and suddenly it’s possible to execute MyMacro.Run without hitting a database at all, by implementing the ISomething and ISomethingFactory interfaces in some FakeDataService class that unit tests can use to test-drive the logic without ever needing to hit a database.

A factory is a creational pattern that allows us to parameterize the creation of an object, and even abstract away the very concept of creating an instance of an object, so much that the concrete implementation we’re actually coding against, has no importance anymore – all that matters is the interface we’re using.

Using interfaces, we can segregate parts of our API into different “views” of the same object and, benefiting from coding conventions, achieve get-only properties that can only be assigned when the object is initialized by a factory method.

If you really want to work with a specific implementation, you can always couple your code with a specific Something – but if you stick to coding against interfaces, you’ll find that writing unit tests to validate your logic without testing your database connections, the SQL queries, the presence of the data in the database, the network connectivity, and all the other things that can go wrong, that you have no control over, and that you don’t need to cover in a unit test, …will be much easier.

The whole setup likely isn’t a necessity everywhere, but abstract factories, factory methods, and interfaces, remain useful tools that are good to have in one’s arsenal… and Rubberduck will eventually provide tooling to generate all that boilerplate code.

Sounds like fun? Help us do it!

31 thoughts on “Factories: Parameterized Object Initialization”

  1. By using the factory method, does this now allow VBA to create an arbitrary number of an object and have each of those objects set to a different instance? In so much as a parallel to a `List` instead of needing to create a sized array of the object defined at runtime?

    Like

    1. Not sure I understand what you’re saying, but a factory is merely an object whose job is to create objects. I think what you have in mind is more like a *builder* pattern, which is another creational design pattern for creating complex objects, when the ‘how’ is on the calling code; with a factory, the ‘how’ is on the factory itself: it can take parameters, but given these parameters it knows how to initialize the object. Whereas a builder could also take parameters, but the builder doesn’t know what the final object will look like… Not sure how clear that is… I’m planning to write about the builder pattern as well.
      You could see the factory as a method that builds pizza boxes; you give it a size, and it gives you a pizza box of that size. The builder would be responsible for making the pizza; it knows you could want pepperoni, cheese (mozzarella? cheddar? goat?), bacon (bits? slices?), peppers (red? green? hot?), mushrooms, and whatnot – but it doesn’t know whether the pizza being built is going to have all that.
      Hope it helps!

      Like

  2. Hi Mathieu, I’ve tried to utilise the above and I’ve ytiple checked the code. But I don’t seem to get it to work. If I specify a variable to be ISomethingFactory the yes it instanstiates a ISomething object but for some reason as it’s contained within the ISomethingFactory the none of the methods are visible. Is that expected?

    Like

    1. Not sure I understand what you mean by “container within the ISomethingFactory”, does the ISomething interface not have any public members for the concrete type to implement?

      Like

  3. Sorry should have proof read my entry.
    Contained rather than container or more accurately constrained by the interface specification of the factory class even if it returns a ISomething object. So when I try to do like:

    Dim Anything as ISomethingFactory
    Set Anything = Something.Create(1, “test”)
    Debug.Print Anything.Bar

    then I get “complies error: Method or data member not found”.

    Hope that makes more sense.

    Like

    1. Yeah sorry for that typo! I did mean to write “contained” =) ok I see now. ‘Anything’ should be ‘As ISomething`, and in this case ‘Something’ *is* the factory. If ISomething has a Bar member then it should compile.

      Like

  4. Yes, that works and I understand that this technique provides a method to create different views of the same concrete class. But are we saying that the factory class would have to contain any and all members needed from the concrete class? If so the I suppose I had mis-understood and thought that you could create using the factory but pass around by the interface. So continuing the example above:

    Function PassObject(ByRef IAnything as ISomething) As ISomething
    Set PassObject = Something.Create( 2. iAnything.Ducky)
    End Fuction

    To call the above as:

    Dim Anybody As ISomethingFactory
    Set Anybody = Something.Create( 1, “Test”)

    Dim Another as ISomethingFactory
    Set Another = PassObject( Anybody)

    I know it’s contrived but is that the right concept? But equally anything that implements ISomethingFactory interface could be passed to PassObject. As then you could create another class Widget and IWidget but the interface ISomethingFactory. Or have I completely lost the plot.

    Like

    1. Sorry to say, I think you’re confused: the result of the Create factory method being assigned to some ISomethingFactory doesn’t make sense; the factory interface only has a Create method, i.e. Public Function Create(…) As ISomething – not As ISomethingFactory… Unless you’re having a SomethingFactoryFactory… but then you’d be writing Java 😉

      Like

  5. Hi Matthieu,

    I was understanding this, until the final bit of code (below), and thereafter I got lost.
    I think it was the post’s big reveal too 🙂

    I was wondering what is MyMacro, and what’s the method .Run?

    I interpreted it as MyMacro was perhaps a variable of ISomething.
    But there’s no Create or Run methods?

    So maybe its another Class?
    But I was unable to successfuly get it working.

    Public Sub Main()
    Dim factory As ISomethingFactory
    Set factory = Something.Self
    With MyMacro.Create(factory)

    Thanks in advance for any clarifying comments,
    Chris

    Like

    1. MyMacro is a class with the VB_PredeclaredId attribute set to True. That makes its methods and members accessible without creating an instance, using the *default instance*. That’s how you can do UserForm1.Show and display a form (although, now you’re storing state in the default instance, and that’s a very bad idea).

      Like

  6. It took a little to fully comprehend the usefulness of the Factory Pattern apart from unit testing. So it’s basically creating a rule for the creation of the Something class using an interface?
    So if had multiple constructors with varying parameters instead of having functions CreateA and CreateB or Load etc you could split each up into Factory Interfaces. eg ISomethingFactory1 Create(a,b,c) as ISomething; ISomethingFactory2 Create(ParmArray args()) as ISomething .

    Liked by 1 person

  7. I found this article to be very helpful. In playing around with the idea it seems it is perfectly possible to pass the parameters sent to Create to the Self Function. Here’s a small example.

    Class NumberReporter (with predeclared ID)
    Option Explicit
    Private Type Properties
    dummy As Long
    End Type

    Private Type State
    first As Long
    second As Long
    third As Long
    assigned As Boolean
    End Type

    Private p As Properties
    Private s As State

    Public Function Create(ByVal this_first As Long, ByVal this_second As Long, ByVal this_third As Long) As NumberReporter

    With New NumberReporter

    Set Create = .Self(this_first, this_second, this_third)

    End With

    End Function

    Public Function Self(ByVal this_first As Long, ByVal this_second As Long, ByVal this_third As Long) As NumberReporter

    With s

    .first = this_first
    .second = this_second
    .third = this_third

    End With

    Set Self = Me

    End Function

    Public Sub report()

    With s

    Debug.Print .first, .second, .third

    End With

    End Sub

    Tested with

    Public reporter_1 As NumberReporter
    Public reporter_2 As NumberReporter
    Public reporter_3 As NumberReporter

    Sub test()

    Set reporter_1 = NumberReporter.Create(1, 2, 3)
    Set reporter_2 = NumberReporter.Create(4, 5, 6)
    Set reporter_3 = NumberReporter.Create(7, 8, 9)

    reporter_1.report
    reporter_2.report
    reporter_3.report

    End Sub

    Which gives the expected output of

    1, 2, 3
    4, 5, 6
    7, 8, 9

    The only downside I found so far is that you need to be careful putting code in the Initialize or Terminate methods as the first ever call to NumberReporter will cause this code to run twice.
    If the above is common knowledge I’ll go stand in the corner with a paper bag over my head.

    PS The p and s variables are a derivation of the TSomething idea described in another RubberDuck blog. properties is reserved for private members which have public getters and setters.

    Like

  8. Is the Self method actually necessary? Why couldn’t the Create method do it this way:
    With New Something
    .Bar = initialBar
    .Ducky = initialDucky
    Set Create = Me
    End With

    Like

    1. That would always return the default instance though, not the New object that was created (and then discarded!).

      The Self property isn’t needed, but then you can’t have a With block:

      Dim result As Something
      Set result = New Something
      result.Bar = initialBar
      result.Ducky = initialDucky
      Set Create = result

      Liked by 1 person

      1. Got it! A great mentor of mine once told me, “one test is always worth a thousand good opinions.” I set up two Create methods one using Self and the other going to Me in the With New block. Stepping through the two different approaches shows the subtlety in the different “Me” instances. Thank you!

        Like

  9. Also trying to wrap my head around an encapsulated factory. Using the example started in https://rubberduckvba.wordpress.com/2020/02/27/vba-classes-gateway-to-solid/, I created an IPersonFactory class as an interface, similar to the Something example above. Using this code in my main module:
    Public Sub Test()
    Dim factory As IPersonFactory
    Set factory = Person.Self
    Dim p As IPerson
    Set p = factory.Create(“Joe”, “Smith”, #1/1/2000#)
    End Sub
    Compiles fine but throws a run time 91, Object variable or with block variable not set. Verified that my person class has VB_PredeclareId = True…. any help would be appreciated!

    Like

    1. Hm if you have an IPersonFactory interface, you’re looking at an abstract factory ..which is probably overkill here, since the dedault instance of Person already exposes a perfectly fine Create factory method. Does Set p = Person.Create(…) work? I suspect a missing Set keyword inside the Create method…

      Like

      1. Hi, thanks Mathieu for sparing a few minutes, much appreciate. You were right, I missed a Set keyword, not in the Create() method but in implementing IPersonFactory_Create() in the Person class. Sometimes this language makes me feel like an idiot!

        Liked by 1 person

    2. Note, there’s quite a leap between using a factory method on a default instance, vs leveraging abstract factories – the latter is extremely useful & I use all the time for literally everything, the former is useful in more advanced dependency injection scenarios where you need to keep coupling low to retain testability, but still need to provide a dependency at the entry point – e.g. you’d want to inject an abstract factory to spawn some IDbConnection object but you don’t know at startup if that’s going to be a SqlServerConnection or a MySqlConnection because the user might pick either from some dialog. …I hope I’m not making things more confusing!

      Like

  10. Hi Mathieu, thanks for taking the time to explain the factory concept as it applies to VBA. One challenge I’m experiencing is in implementing standard code for the Class_Initialize and Class_Terminate procedures for objects created by the factory instance. VBA expects these events to be private. Subsequently they aren’t captured as part of the interface definition and therefore it seems I have no way of prepopulating code for these events.

    Any suggestions?

    Like

    1. There should be very little (if any) code in these handlers, you want the factory method to initialize things.
      What is this “standard code” doing?

      Everything that the factory receives as parameters, needs to be exposed somehow, so that the factory is able to create objects and initialize them. So if your factory method receives a `Something` argument, you’ll want a `Something` property to set.

      Like

      1. The add-in I’m developing supports various levels of internal instrumentation (e.g. logging Info, Warning, Error, Trace, Debug events), and I’m creating an AppLog service class to manage this functionality. Any standard module or class needing instrumentation calls AppLogFactory.Create to instantiate the object using the interface IAppLog. The factory pattern is useful here as it allows me to confine any setup complexities to the factory object and allows the object instance to have a number of read-only properties

        Ignoring Class_Initialize (which the factory create method can replace), I want the Class_Terminate event to write to the trace log, and optionally record any unhandled error.

        As an example, an entry point procedure in a standard module calls the factory to create an instance of IAppLog as part of of it’s own initiation routine. During execution, various procedures are instrumented to call the IAppLog object’s methods (e.g. myApplog.Trace “something interesing happened”). If an unhandled error in the parent object or calling procedure causes the causes the IAppLog instance to terminate, the Class_Terminate event of IAppLog should record the unhandled error condition to the add-in’s event log.

        The issue I’m facing is that there doesn’t appear to be a way to ‘template’ the VB/VBA built-in Class_Terminate event in the Interface definition as it’s a private procedure, and therefore the factory can’t populate the code for this event. I could avoid using the factory method altogether and just instantiate the object directly (e.g. Set MyApplog = New Applog), but the object becomes overly complex with read/write properties and initialization code that would likely need to break encapsulation.

        Like

      2. Your client code simply cannot New up an AppLog instance, unless it’s newed up in the same project.
        Events are always private implementation details in VBA, Initialize and Terminate are no exception. I’m not sure what you mean by “populating code”, factories don’t populate any code anywhere…
        Also you should probably be using a factory method rather than a factory class, since the client code will not be able to create the factory instance anyway (or does the factory have its own factory method?)…
        Following the pattern in this article, your AppLog class should say “Implements IAppLog” and have a Create factory method that works off the class’ default instance and returns an IAppLog object. IAppLog interface/class defines the public API your add-in needs to expose for its clients; the AppLog class itself needs to expose Public/Friend Property Set/Let mutators for every single one of its dependencies (filewriter, config, etc.), so that the Create method is able to assign these properties on the object being created (it only breaks encapsulation if you are not coding against the interface, plus Friend members are only visible within the same project), and then its Class_Initialize handler can do whatever you need it to be doing – a factory method does not change that.

        Like

  11. I am confused why you still have the regular Get and Let properties, for example, what is wrong with doing:

    Private Property Get ISomething_Bar() As Long
    ISomething_Bar = this.Bar
    End Property

    instead of calling the public property Get Bar within the private property Get ISomething_Bar like this:

    Private Property Get ISomething_Bar() As Long
    ISomething_Bar = Bar
    End Property

    I had read that it is not good to expose other public properties to the interface, but isn’t that what is happening in the code above because for every implemented method you are still exposing the public non implemented properties?

    Like

    1. The properties need to be writable for the factory method to be able to assign them. Remove the properties and the code stops compiling!
      Only the factory method needs to access the class’ default interface exposing the setters: the rest of the code uses the ISomething interface (which only exposes the getters) to work with the same object, leaving the mutable default interface only ever accessed by the factory method. Eventually an `@Inject` Rubberduck annotation could decorate write-only properties to let Rubberduck know that these members are being property-injected by a factory method; the default interface only needs the Let/Set members, Get members are the unused, questionable ones here.

      Like

Leave a comment