1
I would like some methodology or example to try to simulate the application of the Factory project standard in VBA.
1
I would like some methodology or example to try to simulate the application of the Factory project standard in VBA.
2
I’m using the example of the book "Use the Head - Project Patterns", which is from a pizzeria.
I have used two approaches to have Fachory Method in VBA:
1) Using a collection to avoid using many nested IF’s but for this all the creation interfaces I use need to have the same signature.
2) I create an interface that selects my creation interfaces, so I can have the factory method whose objects can have creation methods with different signatures, in addition to avoiding the use of many nested IF’s.
First approach:
Here I create the Ipizza interface, where all pizzas must implement it.
'todas as pizzas implementam IPizza
Property Get getNome() As String: End Property
Property Get getIngredientes() As String: End Property
Property Get getPreco() As Double: End Property
Then I isolate the method to create a pizza on another interface. In this approach, each Pizza class should implement its pizza creation interface. In the example we have clsPizzaCalabresa and clsPizzaQueijo, which implement respectively Icriarpizzacalabresa and Icriarpizzaqueijo.
class: Icriarpizzacalabresa
'a classe clsPizzaCalabresa deve implementar ICriarPizzaCalabresa
Function criar(ByVal nomeDaPizza As String, ByVal ingredientes As String, ByVal preco As Double) As IPizza: End Function
class: Icriarpizzaqueijo
'a classe clsPizzaQueijo deve implementar ICriarPizzaQueijo
Function criar(ByVal nomeDaPizza As String, ByVal ingredientes As String, ByVal preco As Double) As IPizza: End Function
Note they have the same signature and they return the same Ipizza interface.
Missing the standard module, type Enum.
The standard module of type Enum, modEnumPizza looks like this:
Public Enum enumTypePizza
CALABRESA
QUEIJO
End Enum
The clsPizzaCalabresa class looks like this:
Implements IPizza
Implements ICriarPizzaCalabresa
Private Type TType
nome As String
ingredientes As String
preco As Double
End Type
Private this As TType
'interface para a criação de uma pizza de Calabresa
Private Function ICriarPizzaCalabresa_criar(ByVal nomeDaPizza As String, ByVal ingredientes As String, ByVal preco As Double) As IPizza
With this
.nome = nomeDaPizza
.ingredientes = ingredientes
.preco = preco
End With
'retorno a referência da interface IPizza de uma instancia clsPizzaCalabresa
Set ICriarPizzaCalabresa_criar = Me
End Function
Private Property Get IPizza_getNome() As String
IPizza_getNome = this.nome
End Property
Private Property Get IPizza_getIngredientes() As String
IPizza_getIngredientes = this.ingredientes
End Property
Private Property Get IPizza_getPreco() As Double
IPizza_getPreco = this.preco
End Property
And finally, the class clsPizzaQueijo:
Implements IPizza
Implements ICriarPizzaQueijo
Private Type TType
nome As String
ingredientes As String
preco As Double
End Type
Private this As TType
'interface para a criação de uma pizza de Queijo
Private Function ICriarPizzaQueijo_criar(ByVal nomeDaPizza As String, ByVal ingredientes As String, ByVal preco As Double) As IPizza
With this
.nome = nomeDaPizza
.ingredientes = ingredientes
.preco = preco
End With
'retorno a referência da interface IPizza de uma instancia clsPizzaQueijo
Set ICriarPizzaQueijo_criar = Me
End Function
Private Property Get IPizza_getNome() As String
IPizza_getNome = this.nome
End Property
Private Property Get IPizza_getIngredientes() As String
IPizza_getIngredientes = this.ingredientes
End Property
Private Property Get IPizza_getPreco() As Double
IPizza_getPreco = this.preco
End Property
Now we will see how the Factory class of this first approach looks:
Implements ICriarPizzaCalabresa
Implements ICriarPizzaQueijo
Private Type TType
'Mas poderia ser um dicionário, Referencias => Microsoft Scripting Runtime
objCollection As Collection
End Type
Private this As TType
Private Sub Class_Initialize()
Dim objInterfacePizzaQueijo As ICriarPizzaQueijo
Dim objInterfacePizzaCalabresa As ICriarPizzaCalabresa
'estou obtendo a refencia da interface ICriarPizzaQueijo e ICriarPizzaQueijo
'e guardando em variaveis
Set objInterfacePizzaQueijo = Me
Set objInterfacePizzaCalabresa = Me
'para evitar o uso de IF's aninhados, uso uma colecao.
'Mas poderia ser um dicionario
Set this.objCollection = New Collection
With this.objCollection
'guardo as refencias nesta colecao, onde a chave eh do tipo Enum,
'neste caso o enum QUEIJO e CALABRESA, do modulo 'modEnumPizza'
.Add objInterfacePizzaQueijo, CStr(enumTypePizza.QUEIJO)
.Add objInterfacePizzaCalabresa, CStr(enumTypePizza.CALABRESA)
End With
'as variaveis com as refencias as interfaces estao guardadas na colecao
'e por isso nao precisamos mais delas
Set objInterfacePizzaQueijo = Nothing
Set objInterfacePizzaCalabresa = Nothing
End Sub
Function CriarPizza(ByVal enumPizza As enumTypePizza, ByVal nome As String, ByVal ingredientes As String, ByVal preco As Double) As IPizza
Dim objInterface As Object
Set objInterface = this.objCollection(CStr(enumPizza))
'Estamos chamando o metodo por ligacao tardia ('late binding')
'mas note que para isso todos os metodos das interfaces implementadas
'precisam ter a mesma assinatura
Set CriarPizza = objInterface.criar(nome, ingredientes, preco)
End Function
Private Function ICriarPizzaCalabresa_criar(ByVal nomeDaPizza As String, ByVal ingredientes As String, ByVal preco As Double) As IPizza
Dim objPizza As clsPizzaCalabresa
Dim objInterfacePizza As ICriarPizzaCalabresa
Set objPizza = New clsPizzaCalabresa
Set objInterfacePizza = objPizza
Set ICriarPizzaCalabresa_criar = objInterfacePizza.criar(nomeDaPizza, ingredientes, preco)
Set objPizza = Nothing
Set objInterfacePizza = Nothing
End Function
Private Function ICriarPizzaQueijo_criar(ByVal nomeDaPizza As String, ByVal ingredientes As String, ByVal preco As Double) As IPizza
Dim objPizza As clsPizzaQueijo
Dim objInterfacePizza As ICriarPizzaQueijo
Set objPizza = New clsPizzaQueijo
Set objInterfacePizza = objPizza
Set ICriarPizzaQueijo_criar = objInterfacePizza.criar(nomeDaPizza, ingredientes, preco)
Set objPizza = Nothing
Set objInterfacePizza = Nothing
End Function
And to see all this work, we created the standard module called modMain, which looks like this:
Modulus:
Sub Main()
Dim objPizza As IPizza
Dim objFactory As Factory
Set objFactory = New Factory
Set objPizza = 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 exit 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 link to download the example: Factory Method - Stackoverflow - EN.xlsm
End of part one
2) I create an interface that selects my creation interfaces, so I can have the factory method whose objects can have creation methods with different signatures in addition to avoid using many nested IF’s.
I refactored the previous example by adding little complexity. The novelty is that now we have to inform the ingredients within Factory class, in addition to the price being depending on the size of the pizza, which is Enum type.
Follow the standard modules.
module: modEnum size
Public Enum enumTypeTamanho
PEQUENA
MEDIA
GRANDE
FAMILIA
End Enum
module: modMain
Sub Main()
Dim objPizza As IPizza
Dim objFactory As Factory
Set objFactory = New Factory
Debug.Print
'Antes era assim: Set objPizza = 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: Iselecioneinterface
Property Get Calabresa(ByVal nome As String) As IPizza: End Property
Property Get Queijo(ByVal nome As String) As IPizza: End Property
Now they follow the pizza classes
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 for 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 modMain, 'method' Main and we’ll 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 last approach.
Follow the sheet link of this example:
Browser other questions tagged vba factory
You are not signed in. Login or sign up in order to post.
Wow, I never thought I’d see project and VBA pattern in the same sentence.
– Maniero