How to implement the Factory project standard in VBA?

Asked

Viewed 85 times

1

I would like some methodology or example to try to simulate the application of the Factory project standard in VBA.

  • 8

    Wow, I never thought I’d see project and VBA pattern in the same sentence.

1 answer

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:

inserir a descrição da imagem aqui

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.

inserir a descrição da imagem aqui

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:

Factory Method 2 - Stackoverflow - EN

Browser other questions tagged

You are not signed in. Login or sign up in order to post.