VBA: Circular reference in factory class

68 Views Asked by At

In a Excel VBA project, I have several classes and interfaces.

Each sheet in the excel workbook corresponds to an instance of a VBA class "clsPmModule".

The instances are created by individual factory methods in a class "clsFactory".

Now, regarding OOP principles, the classes built in the factory methods call other factory methods to create new instances of other classes and to provide them on the fly in the factory method.

Additionally, each instance of clsModule can have observers (OOP Observer Pattern). For this, clsModule has a public method "addObserver". clsModule implements IObservable as well as IObserver.

Now unfortunately, I have a circular reference of factory method calls that kills the application.

I thought of singleton or passing collections via the custom constructor instead of the calls in the factory methods, but that does not seem to be a solution.

Any ideas for quick or clean fixes?

clsFactory

Public Function buildSomeInstance as clsPmModule

   Set buildSomeInstance = new clsPmModule
   with buildSomeInstance
      .constructor(...) 'custom constructor
      .addObserver buildAnotherInstance
   end with

end function

Public Function buildAnotherInstance as clsPmModule

   Set buildAnotherInstance = new clsPmModule
   With buildAnotherInstance
      .constructor(...)   'custom constructor
      .bindValidationSource(buildSomeInstance) 'circular reference
   End With

End Function

1

There are 1 best solutions below

3
freeflow On

In class pmModule add the following methods.

'If using Rubberduck then in the PmClass Declarations section add
'@PredecaredId


'@DefaultMember
Public Deb(... your constructor params) as PmModule
    With New PmModule
        Set Deb = .ConstructInstance(your constructor params)
    End With
end function

Friend function ConstructInstance(... your constructor params) as PmModule
    <code dealing with constructor params>
    Set ConstructInstance = Me
end function

I use deb (short for Debutante) as my factory method. Its an in joke.

Then ensure that the Predeclared attribute for PmModule is set to true (you may have to export the class, set the attributes, then reimport). The free and fantastic Rubberduck addin makes this much easier with its '@PredeclaredId , '@Exposed and '@Defaultmember annotations

By making Deb the default member you can now use the class name in the manner of a constructor as in other languages

Dim myPmModule as PmModule
set myPmModule = PmModule(...Constructor params)

You should now ensure that the BindValidationSource method is a function that returns a PmModule, and in the method return the instance Me.

e.g.

Public Function BindValidationSOurce(byref ipPmModule as PmModule) as PmModule

 <your code>

Set BindValidationSource = Me

End Function

You should now be able to write

set myPmModule = PmModule(...Constructor params).BindValidationSource(PmModule(constructor parameters))

WHich hopefully achieves your original intent.