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!

Advertisements

11 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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s