No primeiro artigo desta série vimos o que são design patterns e porque utilizá-los. Vimos também um dos padrões mais importantes e também um dos mais simples: o padrão Strategy e o aplicamos a um exemplo real. Neste artigo vamos abordar dois padrões não menos importantes: Template Method e Decorator.

O padrão Template Method vai nos ajudar a garantir que os desenvolvedores trabalhando em uma aplicação (muitas vezes nós mesmos) não esqueçam nenhum passo de determinada operação através do encapsulamento de um algoritmo. Já o padrão Decorator vai nos mostrar como compor algoritmos de forma que possamos acumular comportamentos.

Relembrando o Strategy

Vimos no artigo anterior que é importante encapsular o que muda. Foi o que fizemos quando percebemos que o cálculo de impostos mudaria de acordo com diversas regras e o separamos do método de fechar uma nota fiscal (NF). Encapsular é algo muito interessante, soa bonito, e as pessoas se interessam quando falamos nisso, então vamos explorar essa ideia um pouco mais.

Veja na Listagem 1 o código base da classe de NFs, de interface e suas implementações, onde o padrão Strategy foi aplicado (ligeiramente modificado desde o último artigo). A Figura 1 mostra o diagrama de classes.


'classe base, com o padrão Strategy aplicado, delegando
'o trabalho de cálculo de imposto à interface IInfoImposto
Public MustInherit Class NFSemDP
 Protected _Imposto As IInfoImposto
 Private _decTotalNF, _decTotalImpostos As Decimal
 Private _blnFechada As Boolean = False

 Public Property Imposto() As IInfoImposto
     Get
         Return _Imposto
     End Get
     Set(ByVal value As IInfoImposto)
         _Imposto = value
     End Set
 End Property

 Public Property TotalNF() As Decimal
     Get
         Return _decTotalNF
     End Get
     Set(ByVal value As Decimal)
         If _blnFechada Then
             Throw New Exception("NF Fechada")
         End If
         _decTotalNF = value
     End Set
 End Property

 Public Property TotalImpostos() As Decimal
     Get
         Return _decTotalImpostos
     End Get
     Private Set(ByVal value As Decimal)
         _decTotalImpostos = value
     End Set
 End Property

 Public MustOverride ReadOnly Property Local() As String

 Public Sub FechaNF()
     TotalImpostos = Imposto.CalculaImposto(Me.TotalNF)
     _blnFechada = True
 End Sub
End Class

'implentação simples da classe de imposto para a cidade de gramado
Public Class NFGramado
 Inherits NFSemDP

 Public Sub New()
     _Imposto = New ImpostoRS
 End Sub

 Public Overrides ReadOnly Property Local() As String
     Get
         Return "Gramado"
     End Get
 End Property

End Class

'interface simples de imposto, apenas com o método de cálculo
Public Interface IInfoImposto
 Function CalculaImposto(ByVal decValorNF As Decimal) As Decimal
End Interface

'implementação da classe de impostos para todo o RS, utilizada pela cidade de gramado
Public Class ImpostoRS
 Implements IInfoImposto
 Public Function CalculaImposto(ByVal decValorNF As Decimal) 
 As Decimal Implements IInfoImposto.CalculaImposto
     Const decAliquotaICMS@ = 0.12D
     Const decAliquotaISS@ = 0.03D
     Return Decimal.Round(decValorNF * decAliquotaICMS) + 
     Decimal.Round(decValorNF * decAliquotaISS, 2)
 End Function
End
Listagem 1. Classe da Nota Fiscal apenas com padrão Strategy aplicado
Diagrama de classes apenas com padrão Strategy
Figura 1. Diagrama de classes apenas com padrão Strategy

Note que a implementação das classes concretas de cálculo de imposto (ImpostoSP, ImpostoRS e ImpostoBA) pouco importa à classe de imposto e suas herdeiras. Tudo que elas querem é chamar o método CalculaImposto. A classe ImpostoSP é praticamente igual à classe ImpostoRS, mudando apenas as alíquotas, e um imposto a mais (Imposto IXYZ). Veja-a na Listagem 2.


Public Class ImpostoSP
    Implements IInfoImposto
    Public Function CalculaImposto(ByVal decValorNF As Decimal) As 
    Decimal Implements IInfoImposto.CalculaImposto
        Const decAliquotaICMS@ = 0.18D
        Const decAliquotaISS@ = 0.05D
        Const decAliquotaIXYZ@ = 0.01D
        Return Decimal.Round(decValorNF * decAliquotaICMS) + 
        Decimal.Round(decValorNF * decAliquotaISS, 2) + 
        Decimal.Round(decValorNF * decAliquotaIXYZ, 2)
    End Function
End Class
Listagem 2. Classe de cálculo de impostos – muito semelhante à anterior (ImpostoRS)

Veja agora que a classe ImpostoBA tem 2 métodos a mais, ambos privados e com função de auxiliar no cálculo dos impostos. Diferentemente da classe ImpostoRS e ImpostoSP, ela foi codificada mais explicitamente. Veja sua implementação na Listagem 3.


Public Class ImpostoBA
    Implements IInfoImposto
    Public Function CalculaImposto(ByVal decValorNF As Decimal) 
    As Decimal Implements IInfoImposto.CalculaImposto
        Dim decValorICMS@ = CalculaICMS(decValorNF)
        Dim decValorISS@ = CalculaISS(decValorNF)
        Dim decValorTotal@ = decValorICMS + decValorISS
        Return decValorTotal
    End Function
    'método para auxiliar o cálculo do imposto ICMS
    Private Function CalculaICMS(ByVal decValorNF As Decimal) As Decimal
        Return Decimal.Round(decValorNF * 0.17D)
    End Function
    'método para auxiliar o cálculo do imposto ISS
    Private Function CalculaISS(ByVal decValorNF As Decimal) As Decimal
        Return Decimal.Round(decValorNF * 0.04D, 2)
    End Function
End Class
 ... 

Quer ler esse conteúdo completo? Tenha acesso completo