I would like some methodology or example to try to simulate the application of the Factory project pattern in VBA.
I would like some methodology or example to try to simulate the application of the Factory project pattern in VBA.
I'm using the example from the book "Use the Head - Design Patterns", that of a pizzeria.
I have used two approaches to having Fachory Method in VBA:
1) Using a collection to avoid using many nested IF's but for this all the authoring interfaces I use need to have the same signature.
2) I create an interface that selects my creative interfaces, so I can have the factory method whose objects can have creation methods with different signatures, and avoid using many nested IF's.
First approach:
HereIcreatetheIPizzainterface,whereallthepizzasshouldimplementit.
'todasaspizzasimplementamIPizzaPropertyGetgetNome()AsString:EndPropertyPropertyGetgetIngredientes()AsString:EndPropertyPropertyGetgetPreco()AsDouble:EndProperty
ThenIisolatethemethodtocreateapizzainanotherinterface.Inthisapproach,eachPizzaclassshouldimplementitsownpizza-makinginterface.IntheexamplewehaveclsPizzaCalabresaandclsPizzaQueijo,whichrespectivelyimplementICriarPizzaCalabresaandICriarPizzaQueijo.
class:ICriarPizzaCalabresa
'aclasseclsPizzaCalabresadeveimplementarICriarPizzaCalabresaFunctioncriar(ByValnomeDaPizzaAsString,ByValingredientesAsString,ByValprecoAsDouble)AsIPizza:EndFunction
class:ICriarPizzaQueijo
'aclasseclsPizzaQueijodeveimplementarICriarPizzaQueijoFunctioncriar(ByValnomeDaPizzaAsString,ByValingredientesAsString,ByValprecoAsDouble)AsIPizza:EndFunction
NotethattheyhavethesamesignatureandreturnthesameIPizzainterface.
Thedefaultmodule,typeEnum,wasmissing.
ThedefaultmoduleoftypeEnum,modEnumPizzalookslikethis:
PublicEnumenumTypePizzaCALABRESAQUEIJOEndEnum
TheclassclsPizzaCalabresalookslikethis:
ImplementsIPizzaImplementsICriarPizzaCalabresaPrivateTypeTTypenomeAsStringingredientesAsStringprecoAsDoubleEndTypePrivatethisAsTType'interfaceparaacriaçãodeumapizzadeCalabresaPrivateFunctionICriarPizzaCalabresa_criar(ByValnomeDaPizzaAsString,ByValingredientesAsString,ByValprecoAsDouble)AsIPizzaWiththis.nome=nomeDaPizza.ingredientes=ingredientes.preco=precoEndWith'retornoareferênciadainterfaceIPizzadeumainstanciaclsPizzaCalabresaSetICriarPizzaCalabresa_criar=MeEndFunctionPrivatePropertyGetIPizza_getNome()AsStringIPizza_getNome=this.nomeEndPropertyPrivatePropertyGetIPizza_getIngredientes()AsStringIPizza_getIngredientes=this.ingredientesEndPropertyPrivatePropertyGetIPizza_getPreco()AsDoubleIPizza_getPreco=this.precoEndProperty
Andfinally,theclassclsPizzaQueijo:
ImplementsIPizzaImplementsICriarPizzaQueijoPrivateTypeTTypenomeAsStringingredientesAsStringprecoAsDoubleEndTypePrivatethisAsTType'interfaceparaacriaçãodeumapizzadeQueijoPrivateFunctionICriarPizzaQueijo_criar(ByValnomeDaPizzaAsString,ByValingredientesAsString,ByValprecoAsDouble)AsIPizzaWiththis.nome=nomeDaPizza.ingredientes=ingredientes.preco=precoEndWith'retornoareferênciadainterfaceIPizzadeumainstanciaclsPizzaQueijoSetICriarPizzaQueijo_criar=MeEndFunctionPrivatePropertyGetIPizza_getNome()AsStringIPizza_getNome=this.nomeEndPropertyPrivatePropertyGetIPizza_getIngredientes()AsStringIPizza_getIngredientes=this.ingredientesEndPropertyPrivatePropertyGetIPizza_getPreco()AsDoubleIPizza_getPreco=this.precoEndProperty
NowwewillseehowtheFactoryclassofthisfirstapproachis:
ImplementsICriarPizzaCalabresaImplementsICriarPizzaQueijoPrivateTypeTType'Maspoderiaserumdicionário,Referencias=>MicrosoftScriptingRuntimeobjCollectionAsCollectionEndTypePrivatethisAsTTypePrivateSubClass_Initialize()DimobjInterfacePizzaQueijoAsICriarPizzaQueijoDimobjInterfacePizzaCalabresaAsICriarPizzaCalabresa'estouobtendoarefenciadainterfaceICriarPizzaQueijoeICriarPizzaQueijo'eguardandoemvariaveisSetobjInterfacePizzaQueijo=MeSetobjInterfacePizzaCalabresa=Me'paraevitarousodeIF'saninhados,usoumacolecao.'MaspoderiaserumdicionarioSetthis.objCollection=NewCollectionWiththis.objCollection'guardoasrefenciasnestacolecao,ondeachaveehdotipoEnum,'nestecasooenumQUEIJOeCALABRESA,domodulo'modEnumPizza'.AddobjInterfacePizzaQueijo,CStr(enumTypePizza.QUEIJO).AddobjInterfacePizzaCalabresa,CStr(enumTypePizza.CALABRESA)EndWith'asvariaveiscomasrefenciasasinterfacesestaoguardadasnacolecao'eporissonaoprecisamosmaisdelasSetobjInterfacePizzaQueijo=NothingSetobjInterfacePizzaCalabresa=NothingEndSubFunctionCriarPizza(ByValenumPizzaAsenumTypePizza,ByValnomeAsString,ByValingredientesAsString,ByValprecoAsDouble)AsIPizzaDimobjInterfaceAsObjectSetobjInterface=this.objCollection(CStr(enumPizza))'Estamoschamandoometodoporligacaotardia('latebinding')'masnotequeparaissotodososmetodosdasinterfacesimplementadas'precisamteramesmaassinaturaSetCriarPizza=objInterface.criar(nome,ingredientes,preco)EndFunctionPrivateFunctionICriarPizzaCalabresa_criar(ByValnomeDaPizzaAsString,ByValingredientesAsString,ByValprecoAsDouble)AsIPizzaDimobjPizzaAsclsPizzaCalabresaDimobjInterfacePizzaAsICriarPizzaCalabresaSetobjPizza=NewclsPizzaCalabresaSetobjInterfacePizza=objPizzaSetICriarPizzaCalabresa_criar=objInterfacePizza.criar(nomeDaPizza,ingredientes,preco)SetobjPizza=NothingSetobjInterfacePizza=NothingEndFunctionPrivateFunctionICriarPizzaQueijo_criar(ByValnomeDaPizzaAsString,ByValingredientesAsString,ByValprecoAsDouble)AsIPizzaDimobjPizzaAsclsPizzaQueijoDimobjInterfacePizzaAsICriarPizzaQueijoSetobjPizza=NewclsPizzaQueijoSetobjInterfacePizza=objPizzaSetICriarPizzaQueijo_criar=objInterfacePizza.criar(nomeDaPizza,ingredientes,preco)SetobjPizza=NothingSetobjInterfacePizza=NothingEndFunction
Andtoseeallthiswork,wecreatedthedefaultmodulecalledmodMain,whichlookslikethis:
ModMainmodule:
SubMain()DimobjPizzaAsIPizzaDimobjFactoryAsFactorySetobjFactory=NewFactorySetobjPizza=objFactory.CriarPizza(QUEIJO,"Pizza de Queijo", "mussarela, oregano, oleo de oliva, molho simples", 25.99)
Debug.Print "Nome: " & objPizza.getNome
Debug.Print "Ingredientes: " & objPizza.getIngredientes
Debug.Print "Preco: " & objPizza.getPreco
Debug.Print
Set objPizza = objFactory.CriarPizza(CALABRESA, "Pizza de Calabresa", "Calabresa, cebola, oleo de oliva, oregano", 30.99)
Debug.Print "Nome: " & objPizza.getNome
Debug.Print "Ingredientes: " & objPizza.getIngredientes
Debug.Print "Preco: " & objPizza.getPreco
End Sub
And the output in the immediate window is:
Nome: Pizza de Queijo
Ingredientes: mussarela, oregano, oleo de oliva, molho simples
Preco: 25,99
Nome: Pizza de Calabresa
Ingredientes: Calabresa, cebola, oleo de oliva, oregano
Preco: 30,99
Follow the example download link: Factory Method - Stackoverflow - EN-BR.xlsm
End of part one
2) I create an interface that selects my authoring interfaces, so I can have the factory method whose objects can have creation methods with different signatures, as well as avoiding the use of many nested IF's.
I have refuted the previous example adding little complexity. The novelty is that now we have to inform the ingredients in class Factory, besides the price to be due to the size of the pizza, which is Enum type.
Thedefaultmodulesfollow.
module:modEnumTamanho
PublicEnumenumTypeTamanhoPEQUENAMEDIAGRANDEFAMILIAEndEnum
module:modMain
SubMain()DimobjPizzaAsIPizzaDimobjFactoryAsFactorySetobjFactory=NewFactoryDebug.Print'Anteseraassim:SetobjPizza=objFactory.CriarPizza(CALABRESA,"Pizza de Calabresa", "Calabresa, cebola, oleo de oliva, oregano", 30.99)
Set objPizza = objFactory.Fabricar.Calabresa("Pizza de Calabresa")
Debug.Print "Nome: " & objPizza.getNome
Debug.Print "Ingredientes: " & objPizza.getDescricao
Debug.Print "Preco: " & objPizza.getPreco(GRANDE)
Debug.Print
'Antes era assim: Set objPizza = objFactory.CriarPizza(QUEIJO, "Pizza de Queijo", "mussarela, oregano, oleo de oliva, molho simples", 25.99)
Set objPizza = objFactory.Fabricar.Queijo("Pizza de Queijo")
Debug.Print "Nome: " & objPizza.getNome
Debug.Print "Ingredientes: " & objPizza.getDescricao
Debug.Print "Preco: " & objPizza.getPreco(PEQUENA)
End Sub
Now follow the class modules.
interface: IPizza
'todas as pizzas implementam IPizza
Property Get getNome() As String: End Property
Property Get getDescricao() As String: End Property
Property Get getPreco(ByVal tamanho As enumTypeTamanho) As Double: End Property
interface: ICriarPizzaQueijo
Function Criar(ByVal nomeDaPizza As String, dictIngredientes As Scripting.Dictionary, dictTamanhoPreco As Scripting.Dictionary) As IPizza: End Function
interface: ICriarPizzaCalabresa
Function Criar(ByVal nomeDaPizza As String, dictIngredientes As Scripting.Dictionary, dictTamanhoPreco As Scripting.Dictionary) As IPizza: End Function
interface: ISelectInterface
Property Get Calabresa(ByVal nome As String) As IPizza: End Property
Property Get Queijo(ByVal nome As String) As IPizza: End Property
Now follow the classes of the Pizzas
class: clsPizzaCalabresa
'
'Adicione a referencia 'Microsoft Scripting Runtime' em 'Ferramentas' => 'Referencias...'
'
Implements IPizza
Implements ICriarPizzaCalabresa
Private Type TType
nome As String
dictIngredientes As Scripting.Dictionary
dictTamanhoPreco As Scripting.Dictionary
End Type
Private this As TType
Private Function ICriarPizzaCalabresa_criar(ByVal nomeDaPizza As String, dictIngredientes As Scripting.Dictionary, _
dictTamanhoPreco As Scripting.Dictionary) As IPizza
With this
.nome = nomeDaPizza
Set .dictIngredientes = dictIngredientes
Set .dictTamanhoPreco = dictTamanhoPreco
End With
Set ICriarPizzaCalabresa_criar = Me
End Function
Private Property Get IPizza_getDescricao() As String
Dim i As Long
Dim descricao As String
descricao = ""
For i = 0 To this.dictIngredientes.Count - 1
If i <> this.dictIngredientes.Count - 1 Then
descricao = descricao & this.dictIngredientes(i) & ", "
Else
descricao = descricao & this.dictIngredientes(i)
End If
Next i
IPizza_getDescricao = descricao
End Property
Private Property Get IPizza_getNome() As String
IPizza_getNome = this.nome
End Property
Private Property Get IPizza_getPreco(ByVal tamanho As enumTypeTamanho) As Double
IPizza_getPreco = this.dictTamanhoPreco(tamanho)
End Property
class: clsPizzaQueijo
'
'Adicione a referencia 'Microsoft Scripting Runtime' em 'Ferramentas' => 'Referencias...'
'
Implements IPizza
Implements ICriarPizzaQueijo
Private Type TType
nome As String
dictIngredientes As Scripting.Dictionary
dictTamanhoPreco As Scripting.Dictionary
End Type
Private this As TType
Private Function ICriarPizzaQueijo_criar(ByVal nomeDaPizza As String, dictIngredientes As Scripting.Dictionary, _
dictTamanhoPreco As Scripting.Dictionary) As IPizza
With this
.nome = nomeDaPizza
Set .dictIngredientes = dictIngredientes
Set .dictTamanhoPreco = dictTamanhoPreco
End With
Set ICriarPizzaQueijo_criar = Me
End Function
Private Property Get IPizza_getDescricao() As String
Dim i As Long
Dim descricao As String
descricao = ""
For i = 0 To this.dictIngredientes.Count - 1
If i <> this.dictIngredientes.Count - 1 Then
descricao = descricao & this.dictIngredientes(i) & ", "
Else
descricao = descricao & this.dictIngredientes(i)
End If
Next i
IPizza_getDescricao = descricao
End Property
Private Property Get IPizza_getNome() As String
IPizza_getNome = this.nome
End Property
Private Property Get IPizza_getPreco(ByVal tamanho As enumTypeTamanho) As Double
IPizza_getPreco = this.dictTamanhoPreco(tamanho)
End Property
And why the class responsible for making the Pizzas
class: Factory
Implements ISelecioneInterface
Function Fabricar() As ISelecioneInterface
Set Fabricar = Me
End Function
Private Property Get ISelecioneInterface_Calabresa(ByVal nome As String) As IPizza
Dim objPizza As clsPizzaCalabresa
Dim objInterfacePizza As ICriarPizzaCalabresa
Dim dictTamanhoPreco As Scripting.Dictionary
Dim dictIngredientes As Scripting.Dictionary
Set dictTamanhoPreco = New Scripting.Dictionary
Set dictIngredientes = New Scripting.Dictionary
With dictTamanhoPreco
.Add enumTypeTamanho.PEQUENA, 20.99
.Add enumTypeTamanho.MEDIA, 25.99
.Add enumTypeTamanho.GRANDE, 35.99
.Add enumTypeTamanho.FAMILIA, 45.99
End With
With dictIngredientes
.Add .Count, "Calabresa"
.Add .Count, "cebola"
.Add .Count, "molho simples"
.Add .Count, "mussarela"
.Add .Count, "azeitona"
.Add .Count, "orégano"
.Add .Count, "pimenta calabresa"
End With
Set objPizza = New clsPizzaCalabresa
Set objInterfacePizza = objPizza
Set ISelecioneInterface_Calabresa = objInterfacePizza.Criar(nome, dictIngredientes, dictTamanhoPreco)
End Property
Private Property Get ISelecioneInterface_Queijo(ByVal nome As String) As IPizza
Dim objPizza As clsPizzaQueijo
Dim objInterfacePizza As ICriarPizzaQueijo
Dim dictTamanhoPreco As Scripting.Dictionary
Dim dictIngredientes As Scripting.Dictionary
Set dictTamanhoPreco = New Scripting.Dictionary
Set dictIngredientes = New Scripting.Dictionary
With dictTamanhoPreco
.Add enumTypeTamanho.PEQUENA, 20.99
.Add enumTypeTamanho.MEDIA, 25.99
.Add enumTypeTamanho.GRANDE, 35.99
.Add enumTypeTamanho.FAMILIA, 45.99
End With
With dictIngredientes
.Add .Count, "Mussarela"
.Add .Count, "parmesão"
.Add .Count, "gorgonzola"
.Add .Count, "catupiry"
.Add .Count, "molho simples"
.Add .Count, "tomate"
.Add .Count, "orégano"
End With
Set objPizza = New clsPizzaQueijo
Set objInterfacePizza = objPizza
Set ISelecioneInterface_Queijo = objInterfacePizza.Criar(nome, dictIngredientes, dictTamanhoPreco)
End Property
Now just run the modMain, 'method' Main and we will see in the immediate window:
Nome: Pizza de Calabresa
Ingredientes: Calabresa, cebola, molho simples, mussarela, azeitona, orégano, pimenta calabresa
Preco: 35,99
Nome: Pizza de Queijo
Ingredientes: Mussarela, parmesão, gorgonzola, catupiry, molho simples, tomate, orégano
Preco: 20,99
I recommend using this latter approach.
Follow the link in the example worksheet: