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:
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:
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:
- Propriedade ID() – propriedade abstrata que identifica uma conta;
- 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:
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:
- 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.
- 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 :
- Conta – classe base abstrata;
- Poupanca – classe derivada concreta;
- 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
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:
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:
- Associações: agregação e composição
- Generalização (herança)
- Dependências
As representações usam a seguinte notação:
- 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:
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.