VB .NET - Programação Orientada a Objetos (em 10 lições práticas) - V


VB .NET é uma linguagem orientada a objetos.

Até o momento apresentamos os conceitos da programação orientada a objetos afetos à linguagem VB .NET usando uma aplicação bem simples de controle de saldos bancários onde definimos uma classe base Conta e uma classe Poupanca que herda dessa classe base. Vimos como criar métodos e propriedades na classe base e como especializar essa classe criando classes derivadas. Vimos assim como usar os recursos da herança, sobrescrever um método e como interagir como controles de formulários em uma aplicação Windows Forms.

Neste artigo vamos criar uma classe abstrata, uma classe que não pode ser instanciada diretamente e que você é obrigado a herdar para usar seus atributos e métodos e mostrar outros conceitos da programação OOP.

Os exemplos mostrados aqui foram construídos com base nos exemplos do livro : OOP with Microsoft Visual Basic .NET and Microsoft Visual C# Step by Step.

- Definindo uma classe base como uma classe abstrata - V

Nesta aplicação você vai aprender a :

Objetivo: Criar uma pequena aplicação para controlar os saques , depósitos e saldos de uma conta pessoal usando os conceitos programação orientada a objetos na linguagem VB .NET.

Recursos usados : Visual Studio 2012 Express for Windows desktop

Nota: Neste momento já se encontra disponível a versão 2013 : http://www.microsoft.com/visualstudio/eng/2013-downloads

É importante salientar que a ferramenta usada é gratuita, não possui restrições sendo totalmente funcional. Ao fazer o download da ferramenta você também pode baixar o pacote de idioma para localizar o produto para a língua portuguesa.

Problema: Você deseja controlar sua conta bancária pessoal registrando os saques, depósitos e controlando o saldo da conta usando os conceitos da programação orientada a objetos da linguagem VB .NET.

Conceitos Básicos - Classe abstrata

Vamos pensar um pouco sobre o que fizemos até agora. Criamos uma classe base Conta a partir da qual criamos a classe derivada Poupanca. Temos assim duas classes concretas : a classe Conta e a classe Poupanca.

Se pensarmos em um banco do mundo real(lembre-se que classes devem modelar o mundo real) iremos constatar que na verdade não podemos abrir uma conta genérica. Muito provavelmente o banco irá nos oferecer um tipo de conta especifico para escolha. Em geral temos a conta corrente, a conta poupança, a conta especial,etc.

Dessa forma a nossa classe Conta deveria definir o comportamento comum de um conta genérica mesmo que nunca fossemos criar uma conta desse tipo.

Lembre que a classe Conta possui a propriedade ID cujo formato depende do tipo da conta o que não justifica implementar essa propriedade na classe Conta pois sua implementação é feita em cada classe derivada.

Outro fato importante é que a propriedade ID é uma propriedade essencial, pois identifica uma conta, deveria haver uma mecanismo que torna obrigatório a sua implementação em toda classe derivada da classe base.

Como a classe Conta é uma classe concreta ela pode ser instanciada (usando a palavra-chave new) e isso pode não ser muito indicado visto que existe a chance de se adicionar funcionalidades na classe base que não é apropriado às classes derivadas. Deveria haver uma maneira de não permitir que uma classe base seja instanciada sem perder o recurso da herança.

É exatamente isso que uma classe abstrata faz, ela permite o recurso da herança, permite que se defina métodos e propriedades que estejam apenas declarados e que sejam de implementação obrigatória e não pode ser instanciada.

Vamos então criar uma classe base Conta abstrata e nessa classe vamos definir apenas o comportamento comum a uma conta genérica implementando alguns métodos e deixando outros como métodos abstratos. Ao declarar um método como abstrato a classe se torna abstrata.

Resumo:
Uma classe abstrata é uma classe que não pode ser instanciada. Você não pode criar um objeto a partir de uma classe abstrata.
- Uma classe abstrata pode ser herdada e geralmente serve como classe base para outras classes.
- Uma classe abstrata pode conter métodos abstratos e métodos comuns. Uma classe abstrata também podem possuir construtores, propriedades, indexadores e eventos.
- Uma classe abstrata não pode ser estática (Shared). Uma classe abstrata não pode ser selada (
NotInheritable).
- Uma classe abstrata pode herdar de outra classe abstrata.

Métodos Abstratos:
- Um método abstrato é um método que não possui implementação na classe abstrata. Um método abstrato possui somente a definição de sua assinatura. A sua implementação deve ser feita na classe derivada.
- Um método abstrato é um método virtual e deve ser implementado usando o modificador Overrides.
- Um método abstrato somente pode existir em uma classe abstrata.
- Um método abstrato não pode usar o modificador Shared.

Construindo o projeto

Abra o Visual Studio 2012 Express for desktop e clique em New Project;

Selecione o template : Visual Basic -> Windows -> Windows Forms Application;

Informe o nome OOP e clique no botão OK:

seguir vamos incluir os seguintes controles no formulário form1.vb :

O leiaute do formulário é visto abaixo:

Criando a classe base abstrata, as propriedades e métodos abstratos e concretos

No menu PROJECT clique em Add Class;

Selecione o template Class e informe o nome Conta.vb e clique em Add;

Para definirmos uma classe abstrata no VB .NET temos que usar a palavra-chave MustInherit antes da palavra Class;

Vamos começar definindo a propriedades e os métodos abstratos na classe:

  1. Propriedade ID() - propriedade abstrata que identifica uma conta
  2. Método ImprimirDeclaracao() - método abstrato que deverá exibir informações sobre depósitos, saques e saldos

Veja como deve ficar a nossa classe Conta :

Public MustInherit Class Conta

    Public MustOverride ReadOnly Property ID() As String
    Public MustOverride Function ImprimirDeclaracao() As String

End Class

Note que temos apenas a declaração da propriedade e do método mas não a sua implementação. A implementação destes membros deverá ser feita pelas classes derivadas.

A declaração dos membros usa a palavra-chave MustOverride que indica que eles precisam ser definidos em cada classe derivada. A declaração de membros usando a palavra-chave MustOverride deve ser feita obrigatoriamente em uma classe abstrata usando a palavra-chave MustInherit. Uma classe abstrata pode conter membros abstratos e não abstratos ou concretos.(mas isso não é obrigatório)

A classe abstrata Conta deverá possuir os métodos Deposito e Saque e a propriedade Saldo definidos e implementados conforme abaixo:

PPublic MustInherit Class Conta

    Public MustOverride ReadOnly Property ID() As String
    Public MustOverride Function ImprimirDeclaracao() As String

    Public Function Deposito(ByVal valor As Decimal) As Decimal
        _saldo += valor
        _totalDepositos += valor
        Return _saldo
    End Function

    Public Overridable Function Saque(ByVal valor As Decimal) As Decimal
        _saldo -= valor
        _totalSaques += valor
        Return _saldo
    End Function

    Private _saldo As Decimal = 0D
    Public ReadOnly Property Saldo() As Decimal
        Get
            Return _saldo
        End Get
    End Property

End Class
Eu estou usando a sintaxe anterior a versão 4.0 para que o código seja compatível com as versões anteriores.

Para ver a nova sintaxe veja o meu artigo sobre propriedades autoimplementadas:
VS2010 - VB .NET - Novos Recursos - Macoratti.net

O método Saque esta definido como Overridable indicando que ela poderá ser sobrescrito na classe derivada.

Vamos acrescentar duas novas propriedades na classe abstrata Conta para calcular o total de depósitos e saques de uma conta: TotalDepositos e TotalSaques cuja implementação esta definida conforme mostrada a seguir:

PPublic MustInherit Class Conta

    Public MustOverride ReadOnly Property ID() As String
    Public MustOverride Function ImprimirDeclaracao() As String

    Public Function Deposito(ByVal valor As Decimal) As Decimal
        _saldo += valor
        _totalDepositos += valor
        Return _saldo
    End Function

    Public Overridable Function Saque(ByVal valor As Decimal) As Decimal
        _saldo -= valor
        _totalSaques += valor
        Return _saldo
    End Function

    Private _saldo As Decimal = 0D
    Public ReadOnly Property Saldo() As Decimal
        Get
            Return _saldo
        End Get
    End Property
  Private _totalDepositos As Decimal = 0D
    Public ReadOnly Property TotalDepositos() As Decimal
        Get
            Return _totalDepositos
        End Get
    End Property

    Private _totalSaques As Decimal = 0D
    Public ReadOnly Property TotalSaques() As Decimal
        Get
            Return _totalSaques
        End Get
    End Property

End Class
Observe que os métodos Saque e Deposito mantém os campos _totalSaques e _totalDepositos

As propriedades TotalDepositos e TotalSaques são somente leitura.

Ao sobrescrever o método Saque as classes derivadas não poderão acessar aos campos _totalSaques e _totalDepositos  vistos que eles são campos definidos como Private.

Criando a classe derivada Poupanca

Vamos agora criar a classe Poupanca derivada da classe Conta.

No menu PROJECT clique em Add Class;

Selecione o template Class e informe o nome Poupanca.vb e clique em Add;

A seguir inclua a declaração Inherits Conta para indicar que a classe Poupanca herda da classe Conta;

Como a classe Conta é uma classe base abstrata e possui a propriedade ID e o método ImprimirDeclaracao definidos como abstratos o Visual Studio irá criar automaticamente uma implementação vazia desses membros na classe derivada visto que eles são de implementação obrigatória em toda a classe derivada:

Vamos definir a implementação desses membros conforme mostra o código a seguir:

Public Class Poupanca
    Inherits Conta

    Public Overrides Function ImprimirDeclaracao() As String

        Dim declaracao As String = String.Format("{1}{0}" & _
                                  "Saldo Inicial : R$0.00{0}Depositos: {2:C}{0}" & _
                                  "Saques: {3:C}{0} Juros: {4:C}{0}" & _
                                  "Saldo Final: {5:C}{0} ", _
                                   New Object() {ControlChars.CrLf, Me.ID, _
                                   Me.TotalDepositos - _totalJuros, _
                                   Me.TotalSaques, Me._totalJuros, Me.Saldo})

        Return declaracao

    End Function

    Public Overrides ReadOnly Property ID As String
        Get
            Return _titular + "-P"
        End Get
    End Property

End Class
Aqui mantivemos a mesma definição para identifica uma conta de poupança.

A conta será identifica pelo nome do titular seguido do sufixo "-P".

A plataforma .NET possuí um método para formatar string chamado Format, na classe String. Este método exige 2 parâmetros de entrada:

1- O formato: Indica a como será feita a apresentação das informações.
Assim se quisermos formatar um número para ter 5 casas decimais, é aqui que informamos.

2- O dado a ser formatado: Este parâmetro é do tipo object, e suporta qualquer tipo de dado: Inteiro, Decimal, Double, etc.

Porém será gerada uma exception caso a formatação não for possível, por isso é necessário usar o método com cautela.

Sintaxe:
Public Shared Function Format (format As String,ParamArray args As Object()) As String

Ex:Teste = String.Format("Formatação de string com {0} parâmetro. ", _
                                     "Agora são {1}. Valor numérico: {2}", 1, Now(), 15.5)

Note que tanto a propriedade como o método usam o modificador Overrides. Veja abaixo um lembrete de seu significado. (Lembre que o método da classe Pai deve usar Overridable)

A seguir vamos definir o construtor da classe derivada Poupanca como abaixo:

 Private _titular As String
    Public Sub New(ByVal titular As String)
        _titular = titular
    End Sub

A classe Poupanca deverá ser especializada pela definição da propriedade Juros() e do método AdicionarJuros() definidos como segue:

 Private _juros As Decimal = 0.01D
    Public Property Juros() As Decimal
        Get
            Return _juros
        End Get
        Set(ByVal Value As Decimal)
            _juros = Value
        End Set
    End Property

    Private _totalJuros As Decimal = 0D
    Public Function AdicionarJuros() As Decimal
        Dim juros As Decimal = _juros * Me.Saldo
        _totalJuros += juros
        Me.Deposito(juros)
        Return Me.Saldo
    End Function

Criando a classe derivada ContaCorrente

Vamos criar outra classe derivada da classe base Conta.

No menu PROJECT clique em Add Class;

Selecione o template Class e informe o nome ContaCorrente.vb e clique em Add;

A classe ContaCorrente deverá implementar a propriedade ID() identificando uma conta pelo nome do titular e o sufixo "-CC" e o método ImprimirDeclaração() conforme o código abaixo:

Public Class ContaCorrente
    Inherits Conta

    Public Overrides ReadOnly Property ID As String
        Get
            Return _titular + "-CC"
        End Get
    End Property


    Private _titular As String
    Public Sub New(ByVal titular As String)
        _titular = titular
    End Sub

    Public Overrides Function ImprimirDeclaracao() As String

        Dim declaracao As String = String.Format("{1}{0}" & _
                                   "Saldo Inicial : R$0.00{0}Depositos: {2:C}{0} Saques: {3:C}{0} Saldo Final: {4:C}{0} ", _
                                   New Object() {ControlChars.CrLf, Me.ID, Me.TotalDepositos, Me.TotalSaques, Me.Saldo})
        Return declaracao
    End Function

End Class

Temos assim 3 classes em nosso projeto :

  1. Conta - classe base abstrata;
  2. Poupanca - classe derivada concreta;
  3. ContaCorrente - classe derivada concreta;

Não seria bom se pudéssemos usar uma ferramenta para descrever graficamente o relacionamento entre as classes ?

A UML - Unified Modeling Language é uma ferramenta gráfica que pode ser usada para descrever projetos orientado a objetos. Dessa forma a UML não é um método é uma linguagem de modelagem designada para especificar, visualizar, construir e documentar um sistema.

Ferramentas de desenvolvimento como a UML permitem que desenvolvedores possam discutir projetos usando um vocabulário comum. Tais ferramentas também diminuem a ambiguidade em uma especificação de um projeto de software.

Um dos recursos que a UML oferece para é o diagrama de classes que permitem ilustrar as classes, seus métodos propriedades.

A representação de uma classe usa um retângulo dividido em três partes:

  • nome
  • atributos
  • métodos


Os diagrama se classes ilustram atributos e operações de uma classe e as restrições como que os objetos podem ser conectados ; descrevem também os tipos de objetos no sistema e os relacionamentos entre estes objetos que podem ser : associações e abstrações.
 

Para poder representar a visibilidade dos atributos e operações em uma classe utiliza-se as seguintes marcas e significados:

Para mostrar o uso da UML vamos definir o diagrama de classe para a nossa classe Conta:

A UML reconhece três tipos mais importantes de relações: dependência, associação e generalização (ou herança).

Geralmente as classes não estão sós e se relacionam entre si. O relacionamento e a comunicação entre as classes definem responsabilidades , temos 3 tipos :

  1. Associações :  Agregação e composição
  2. Generalização (herança)
  3. Dependências

As representações usam a seguinte notação :

  • Associação : São relacionamentos estruturais entre instâncias e especificam que objetos de uma classe estão ligados a objetos de outras classes. Podemos ter associação uniária, binária , etc.

    A associação pode existir entre classes ou entre objetos. Uma associação entre a classe Professor e a classe disciplina (um professor ministra uma disciplina) significa que uma instância de Professor (um professor específico) vai ter uma associação com uma instância de Disciplina. Esta relação significa que as instâncias das classes são conectadas, seja fisicamente ou conceitualmente.[Nicolas Anquetil]
     
  • Dependência - São relacionamentos de utilização no qual uma mudança na especificação de um elemento pode alterar a especificação do elemento dependente. A dependência entre classes indica que os objetos de uma classe usam serviços dos objetos de outra classe.

A representação UML para o relacionamento entre as 3 classes do nosso projeto é visto a seguir:

Testando as classes no projeto Windows Forms

Vamos agora testar a utilização das classes criadas no projeto em uma aplicação Windows Forms.

A nossa aplicação deverá exibir no formulário as contas existentes no primeiro combobox e o tipo de operação desejada no segundo.

O usuário poderá selecionar uma conta,uma operação e clicar no botão Executar para executar a operação ou no botão Imprimir para exibir as informações da conta selecionada.

Abra o formulário form1.vb e declare as variáveis contaPoupancaMacoratti e contaCorrenteMacoratti que são instâncias da classe Poupanca e ContaCorrente:

Dim contaPoupancaMacoratti As New Poupanca("Macoratti")
Dim contaCorrenteMacoratti As New ContaCorrente("Macoratti")

Vamos definir também uma variável contaSelecionada do tipo Conta:

Dim contaSelecionada As Conta

No evento Load do formulário vamos preencher o controle cobConta com as instâncias da classe Poupanca e ContaCorrente criadas anteriormente e atribuir o valor selecionado deste controle e do controle cboOperação para exibir o primeiro item; vamos preencher o TextBox - txtValor com o valor "50";

 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.cboConta.Items.Add(contaPoupancaMacoratti)
        Me.cboConta.Items.Add(contaCorrenteMacoratti)
        Me.cboConta.SelectedIndex = 0
        Me.cboOperacao.SelectedIndex = 0
        Me.txtValor.Text = "50"
    End Sub

No evento SelectedIndexChanged do combobox cboConta definimos o item selecionado como um objeto e convertemos o objeto para o tipo Conta atribuindo-o á variável contaSelecionada:

 Private Sub cboConta_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboConta.SelectedIndexChanged
        Dim item As Object = Me.cboConta.SelectedItem
        contaSelecionada = CType(item, Conta)
    End Sub

O evento Click do botão Imprimir chama a rotina ExibeDeclaracao:

 Private Sub btnImprimir_Click(sender As Object, e As EventArgs) Handles btnImprimir.Click
        ExibeDeclaracao()
    End Sub

A rotina ExibeDeclaracao() limpa o conteúdo do TextBox e chama o método ImprimirDeclaracao() da conta selecionada:

  Private Sub ExibeDeclaracao()
        txtDeclaracao.Clear()
        txtDeclaracao.Text = contaSelecionada.ImprimirDeclaracao()
    End Sub

No evento Click do botão Executar verificamos o tipo da operação selecionada: Saque ou Deposito e usando o objeto contaSelecionada executamos a operação na conta escolhida. Ao final chamamos a rotina ExibeDeclaracao():

 Private Sub btnExecutar_Click(sender As Object, e As EventArgs) Handles btnExecutar.Click
        Dim valor As Decimal = 0D
        valor = Convert.ToDecimal(txtValor.Text)
        If cboOperacao.SelectedIndex = 0 Then
            contaSelecionada.Deposito(valor)
        ElseIf cboOperacao.SelectedIndex = 1 Then
            contaSelecionada.Saque(valor)
        End If
        ExibeDeclaracao()
    End Sub

Abaixo vemos exemplos de execução para Poupanca e ContaCorrente:

Pegue o projeto completo aqui:  ControleBancario.zip

Na próxima aula vou apresentar o conceito de interfaces e mostrar como podemos usar os seus recursos na linguagem VB .NET.

Referências:


José Carlos Macoratti