VB 2005 - Polimorfismo com classes Abstratas


Vamos supor que você precisa desenvolver um projeto para calculo de áreas e que de início deverá calcular a área do retângulo, do triângulo e do quadrado.

Como um bom conhecedor dos conceitos da programação orientada a objetos(poo) você cria um novo projeto no Visual Basic 2005 Express e imediatamente no menu Project inclui uma classe usando a opção Add Class dando ao arquivo o nome Areas.vb.

Nota: Para saber mais sobre conceitos da programação orientada a objetos leia o meu artigo: Revisitando conceitos OOP

A seguir você decide, baseado na sua experiência na poo, criar um classe chamada Retangulo com as seguintes características:

O código da classe esta listado abaixo:

Public Class Areas

    Public Class Retangulo

        Private _altura As Double
        Private _base As Double

        Public Sub New(ByVal h As Double, Bl base As Double)
            Me.Altura = h
            Me.Base = b
        End Sub

        Public Property Altura() As Double
            Get
                Return _altura
            End Get
            Set(ByVal value As Double)
                _altura = value
            End Set
        End Property
     
       Public Property Base() As Double
            Get
                Return _base
            End Get
            Set(ByVal value As Double)
                _base = value
            End Set
        End Property

        Public Function getArea() As Double
            Return _altura * _base
        End Function
    End Class
End Class

Muito bem, você já pode obter a área do retângulo usando o método getArea() e definindo as propriedades Altura e Base. Até aqui nada de especial.

Você parte então todo entusiasmado para criar sua segunda classe , a classe Triangulo, que irá calcular a área do triângulo. O código usado esta mostrado a seguir:

    Public Class Triangulo

        Private _altura As Double
        Private _base As Double

        Public Sub New(ByVal base As Double, ByVal altura As Double)
           Me.Altura = h
            Me.Base = b
        End Sub

        Public Property Altura() As Double
            Get
                Return _altura
            End Get
            Set(ByVal value As Double)
                _altura = value
            End Set
        End Property
     
       Public Property Base() As Double
            Get
                Return _base
            End Get
            Set(ByVal value As Double)
                _base = value
            End Set
        End Property

        Public Function getArea() As Double
            Return ( _base * _altura) / 2
        End Function

    End Class

Para encerrar você cria a classe Quadrado contendo uma propriedade , o construtor e o método getArea(), conforme código abaixo:

Public Class Quadrado

        Private _lado As Double
        Public Sub New(ByVal lado As Double)
            Me.Lado = lado
        End Sub

        Public Property Lado() As Double
            Get
                Return _lado
            End Get
            Set(ByVal value As Double)
                _lado = value
            End Set
        End Property

        Public Function getArea() As Double
            Return _lado * _lado
        End Function
    End Class

Após terminar as classes você implementou em um formulário , form1.vb do projeto , o código para fazer os testes.

A interface criada usou apenas três botões conforme figura abaixo:

O código usado no formulário é mostrado a seguir:
 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim r As New Areas.Retangulo(10, 20)
        MsgBox("Area do retangulo de base=10 e altura=20 : " & r.GetArea())
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim r As New Areas.Triangulo(10, 20)
        MsgBox("Area do triangulo de base=10 e altura=20  : " & r.getArea())
    End Sub

    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        Dim r As New Areas.Quadrado(10)
        MsgBox("Area do quadrado de lado 10 : " & r.getArea())
    End Sub

Após terminar todo este trabalho você analisou melhor o seu código e percebeu o seguinte:

Atento a estes detalhes você se convenceu que pode otimizar o código usado. Você então resolveu aplicar seus conhecimentos avançados de orientação a objeto e usar o conceito de classes abstratas.

Nota:  Veja mais sobre este assunto no meu artigo : Classes Abstratas

No VB.NET uma classe é definida como abstrata através do uso do modificador MustInherit. Então para criar uma classe abstrata do VB.NET você precisa preceder a palavra chave Class com o modificador MustInherit .

Um método que possui somente a assinatura(declaração) sem qualquer implementação pode também ser chamado de um método abstrato. Para declarar um método como abstrato no VB .NET você precisa usar o modificador MustOverride pois os métodos abstratos precisam ser sobrescritos e implementados nas classes filhas com a mesma assinatura com o qual foi definido na classe pai.

Uma classe precisa ser definida como abstrata se possuir pelo menos um método abstrato; ela pode no entanto conter diversos métodos não abstratos. Lembre-se que você não pode instanciar uma classe Abstrata como faz com uma classe comum visto que ela não esta totalmente implementada.

Com base nestes conceitos você definiu uma classe Forma com o seguinte código:

Public MustInherit Class Forma

        Private _x As Double
        Private _y As Double

        Public Sub New()
        End Sub

        Public Sub New(ByVal valor As Double)
            Me.X = valor
        End Sub

        Public Sub New(ByVal valor1 As Double, ByVal valor2 As Double)
            Me.X = valor1
            Me.Y = valor2
        End Sub

        Public Property X() As Double
            Get
                Return _x
            End Get
            Set(ByVal value As Double)
                _x = value
            End Set
        End Property

        Public Property Y() As Double
            Get
                Return _y
            End Get
            Set(ByVal value As Double)
                _y = value
            End Set
        End Property

        Public MustOverride Function GetArea() As Double
    End Class

A classe Forma é uma classe abstrata(MustInherit) e possui as seguintes características:

Note que o método foi definido sem qualquer implementação e deverá ser sobrescrito nas classes filhas.

Agora podemos redefinir a classe Retangulo que deverá herdar da classe Forma. Ao final a classe deverá ficar assim:

    Public Class Retangulo
        Inherits Forma

        Public Sub New(ByVal lado As Double, ByVal base As Double)
            MyBase.New(lado, base)
        End Sub

        Public Overrides Function getArea() As Double
            Return Me.X * Me.Y
        End Function
    End Class


A nova classe Retangulo agora possui um construtor contendo a seguinte instrução: MyBase.New(lado, base)

Esta instrução chama o construtor da super classe (a classe Forma)

A nova classe Retangulo sobrescreve o método getArea() e foi implementando com a mesma assinatura da classe abstrata(Forma) e retornando o valor da área do retângulo.

Vamos fazer o mesmo com a classe Triangulo que deverá ficar com o seguinte código:

    Public Class Triangulo
        Inherits Forma

        Public Sub New(ByVal base As Double, ByVal altura As Double)
            MyBase.New(base, altura)
        End Sub

        Public Overrides Function getArea() As Double
            Return (Me.X * Me.Y) / 2
        End Function

    End Class

A nova classe Triangulo também possui um construtor que chama o construtor da classe Pai e implementa o método getArea() com a mesma assinatura da classe abstrata Forma, mas retornando o valor da área do triângulo.

A classe Quadrado também deve ser alterada. Veja como ficou o seu código:

    Public Class Quadrado
        Inherits Forma

        Public Sub New(ByVal lado As Double)
            MyBase.New(lado)
        End Sub

        Public Overrides Function getArea() As Double
            Return Me.X * Me.X
        End Function
    End Class

Na classe Quadrado temos o método getArea() também sobrescrevendo o método definido na classe Pai e definido com a mesma assinatura mas retornando o valor da área do quadrado.

Agora só falta alterar o código do formulário para testar cada uma das classes que agora herdam da classe abstrata Forma. O código deverá ser alterado conforme abaixo:

Public Class Form1

    Dim f As Areas.Forma = Nothing

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        f = New Areas.Retangulo(10, 20)
        MsgBox("Area do retangulo de base=10 e altura=20 : " & f.getArea())
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        f = New Areas.Triangulo(10, 20)
        MsgBox("Area do triangulo de base=10 e altura=20  : " & f.getArea())
    End Sub

    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        f = New Areas.Quadrado(10)
        MsgBox("Area do quadrado de lado 10 : " & f.getArea())
    End Sub
End Class

Uma observação importante que deve ser feita ao código acima é que agora estamos sempre usando a mesma variável(variável de referência e não um objeto concreto) para tratar as instâncias de todas as classes filhas : Retangulo, Triangulo e Quadrado ; a variável f .

Note também que a mesma variável f agora é usada para chamar o mesmo método getArea() obtendo resultado diferentes.

Quando você usa o mesmo método com a mesma assinatura e com diferentes implementações e diferentes execuções obtendo resultados diferentes você tem o que chamamos de Polimorfismo.

Definição Acadêmica:

O termo polimorfismo, etimologicamente, quer dizer "várias formas"; todavia, na Informática, e em particular no universo da OOP, é definido como sendo um código que possui "vários comportamentos" ou que produz "vários comportamentos"; em outras palavras, é um código que pode ser aplicado à várias classes de objetos. De maneira prática isto quer dizer que a operação em questão mantém seu comportamento transparente para quaisquer tipos de argumentos; isto é, a mesma mensagem é enviada a objetos de classes distintas e eles poderão reagir de maneiras diferentes. Um método polimórfico é aquele que pode ser aplicado à várias classes de objetos sem que haja qualquer inconveniente.

Nelson Abu Sanra Rahal Júnior
Mestre em Ciência da Computação (UFSC),
Doutorando em Ciência da Computação (UFSC)
Outra definição:
Polimorfismo, originário do grego, significa "muitas formas" (poli = muitas, morphos = formas). Ele permite que referências de tipos de classes mais abstratas representem o comportamento das classes concretas que referenciam. Assim, um mesmo método pode apresentar várias formas, de acordo com seu contexto. O polimorfismo é importante pois permite que a semântica de uma interface seja efetivamente separada da implementação que a representa.
http://pt.wikipedia.org/wiki/Polimorfismo

Então, sem querer querendo, você acabou usando um recurso muito valioso da programação orientada a objetos: a reutilização de código com encapsulamento e herança.

Até o próximo artigo...

Referências:


José Carlos Macoratti