.NET

19 ago, 2014

Programação Orientada a Objetos em 10 lições práticas – Parte 05

Publicidade

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 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, que não pode ser instanciada diretamente e na qual 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 – Parte 05

 

Nesta aplicação você vai aprender a:

  • Cria uma classe abstrata usando a palavra-chave MustInherit
  • Criar classe derivadas a partir da classe abstrata

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

É importante salientar que a ferramenta usada é gratuita e 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. Logo, temos duas classes concretas: a classe Conta e a classe Poupanca.

Se pensarmos em um banco do mundo real (lembre-se que as 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 específico 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 fôssemos 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 são apropriadas à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 e permite que se defina métodos e propriedades que estejam apenas declarados e que sejam de implementação obrigatória, mas não podem ser instanciados.

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 pode 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:

vbn_oop41

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

  • 1 Label – Text= Conta;
  • 1 Combobox – name= cboConta
  • 1 Label – Text=Operação
  • 1 Combobox – cboOperacao, propriedade Items (Collection): Deposito e Saque
  • 1 Label – Text=Valor
  • 1 TextBox – txtValor
  • 2 Button – btnExecutar e btnImprimir
  • 1 TextBox – txtDeclaracao

O leiaute do formulário é visto abaixo:

vbn_oop42

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;

vbn_oop33 (1)

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 está 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 está definida conforme mostrado 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, uma vez 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:

vbn_oop43

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: 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 dois 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

Exemplo:

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, quanto 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):

  • Overridable – declara que o método pode ser sobreposto nas classes que herdarem da classe base/pais;
  • Overrides – indica que o método da classe filha irá sobrepor o método da classe pai.

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 a seguir:

 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 e selecione o template Class. 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, mas sim 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 é o diagrama de classes que permite ilustrar as classes, seus métodos e propriedades.

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

  • Nome
  • Atributos
  • Métodos

net_uml3

Os diagrama de classes ilustram os atributos e as operações de uma classe e as restrições com 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:

  • + público – visível em qualquer classe
  • # protegido – qualquer descendente pode usar
  • – privado – visível somente dentro da classe

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

vbn_oop44

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 três 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:

net_uml5

  • Associação: são relacionamentos estruturais entre instâncias e especificam quais objetos de uma classe estão ligados a objetos de outras classes. Podemos ter associação unitá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, nos quais 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:

vbn_oop46

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:

vbn_oop46

 

vbn_oop47

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.