Capítulo 9 Desenho de classes Escolha de classes a implementar Coesão e acoplamento Minimização de efeitos colaterais Consolidação de conceitos: métodos e campos estáticos, abrangência de variáveis locais e de membros de dados, pacotes Escolha de classes Uma classe representa um conceito único num domínio específico de problemas. O seu nome deve ser um substantivo que descreve o conceito. Alguns conceitos: da matemática: ponto, rectângulo, círculo da vida real: contas bancárias, máquina de venda Os objectos instanciados nas classes fazem o respectivo trabalho. Por exemplo, Scanner Existem classes utilitárias, que não têm objectos mas apenas métodos estáticos e constantes. É o caso da classe Math As classes de início de execução apenas devem ter o método main Deve-se evitar a tradução de acções em classes: Por exemplo, TalaoVencimento e não CalculaTalaoVencimento
Coesão A interface pública de uma classe demonstra coesão se todas as suas características estão relacionadas com o conceito a representar pela classe public class CashRegister public void enterpayment(int dollars, int quarters, int dimes, int nickels, int pennies) public static final double NICKEL_VALUE = 0.05; Solução pouco apropriada, pois envolve dois conceitos: caixa registadora e moeda public class Coin public Coin(double avalue, String aname) public double getvalue() public class CashRegister public void enterpayment(int coincount, Coin cointype) Solução apropriada, com duas classes Acoplamento Uma classe depende de outra se usa objectos dessa classe CashRegister depende de Coin para determinar o valor do pagamento Coin não depende de CashRegister O acoplamento diz-se elevado quando existem muitas dependências de classe Devemos minimizar o acoplamento de modo a minimizar o impacto decorrente de eventuais alterações posteriores de interface
Nível de acoplamento entre classes Conveniente o desenho de diagramas de classe para visualizar as relações entre classes. Por exemplo, através de UML: Unified Modeling Language, que é uma notação para análise e desenho orientado ao objecto (www.uml.org) Selectores, modificadores e classes imutáveis Selector: não altera o estado do parâmetro implícito double balance = account.getbalance(); Modificador: altera o objecto sob o qual foi chamado Classe imutável: classe que não tem métodos modificadores account.deposit(1000); É seguro fornecer referências a objectos de classes imutáveis já que estas não modificam os objectos String name = "John Q. Public"; String uppercased = name.touppercase(); // nome não é alterado - a classe String é imutável
Efeitos colaterais de métodos Modificação de informação vista do exterior public void transfer(double amount, BankAccount other) balance = balance-amount; other.balance = other.balance+amount; // modifica parâmetro explícito Sugestões conveniente não actualizar parâmetros explícitos evitar fazer modificações para além das relativas a parâmetros implícitos tentar dissociar input/output do trabalho efectivo das classes public void printbalance() // mensagem em português e dependente do System.out System.out.println("O saldo e " + balance +! ); Tentativa de modificação de um parâmetro do tipo primitivo Em Java, um método não consegue alterar parâmetros do tipo primitivo void transfer(double amount, double otherbalance) balance = balance - amount; não funciona otherbalance = otherbalance + amount; double savingsbalance = 1000; harryschecking.transfer(500, savingsbalance); System.out.println(savingsBalance);
Modificação de um parâmetro sem efeito no método que chama Modificação de um parâmetro sem efeito no método que chama
Chamada por valor e chamada por referência Chamada por valor: quando o método é chamado, os parâmetros do método são copiados para as variáveis de parâmetros Chamada por referência: os métodos podem modificar parâmetros O Java utiliza a chamada por valor Um método pode alterar o estado dos parâmetros de referência de objectos mas não pode substituir a referência a um objecto por outra public class BankAccount public void transfer(double amount, BankAccount otheraccount) balance = balance - amount; double newbalance = otheraccount.balance + amount; otheraccount = new BankAccount(newBalance); // não funciona Chamada por valor harryschecking.transfer(500, savingsaccount); modificação do parâmetro de referência ao objecto não tem efeito prático
Métodos estáticos Todo o método estático tem de estar numa classe e não é chamado para operar num objecto public class Financial public static double percentof(double p, double a) return (p / 100) * a; // outros métodos da área financeira Um motivo para a existência de métodos que não operam em objectos: o encapsulamento de cálculos que envolvam apenas números, os quais, não sendo objectos, não possibilitariam a chamada de métodos sobre eles Chamada com o nome da classe em vez do objecto double tax = Financial.percentOf(taxRate, total); A classe main é estática. Repare-se que quando é chamada ainda não existem objectos Membros de dados estáticos Um membro de dados estático pertence à classe, não a um objecto da classe. Denomina-se também campo da classe public class BankAccount private double balance; private int accountnumber; private static int lastassignednumber = 1000; // se não fosse // estático, cada instância da classe teria o seu próprio valor public BankAccount() // define o próximo número de conta e actualiza o campo estático lastassignednumber++; // atribui o número do campo actual ao número de conta deste banco accountnumber = lastassignednumber;
Inicialização de membros de dados estáticos (A) Não fazer nada. Neste caso, os campos terão 0 se forem números, false se forem valores booleanos ou null se forem objectos (B) Inicialização explícita (incluindo a forma pouco usual de bloco) public class BankAccount // executa uma só vez, quando a classe é carregada private static int lastassignednumber = 1000; public class BankAccount private static int lastassignednumber; static lastassignednumber = 1000; Membros de dados estáticos Os membros de dados estáticos devem ser sempre declarados como privados. Como excepção a esta regra, temos as constantes estáticas, que podem ser públicas ou privadas public class BankAccount // a referir como BankAccount.OVERDRAFT_FEE public static final double OVERDRAFT_FEE = 5; Não se deve utilizar em demasia os campos estáticos, com excepção das constantes
Campo estático e campos de instanciação Abrangência de variáveis locais Abrangência de uma variável: região do programa na qual se pode aceder à variável, que vai desde a sua declaração até ao fim do bloco que a engloba Por vezes, o mesmo nome de variável é usado em dois métodos distintos. Neste caso, as variáveis em causa são independentes uma da outra, logo as respectivas abrangências são disjuntas, i.e., não se sobrepõem public class RectangleTester public static double area(rectangle rect) double r = rect.getwidth() * rect.getheight(); return r; public static void main(string[] args) Rectangle r = new Rectangle(5, 10, 20, 30); double a = area(r); System.out.println(r);
Abrangência de variáveis locais A abrangência de uma variável local não pode conter a definição de outra variável com o mesmo nome Rectangle r = new Rectangle(5, 10, 20, 30); if (x >= 0) double r = Math.sqrt(x); // erro: não pode haver aqui outra variável r Mas podem coexistir variáveis locais com o mesmo nome desde que as respectivas abrangências não se sobreponham if (x >= 0) double r = Math.sqrt(x); // termina aqui a abrangência de r else Rectangle r = new Rectangle(5, 10, 20, 30); // é correcto ter agora // uma variável r Abrangência de membros da classe Os membros de dados privados possuem abrangência de classe: é possível aceder todos os membros em qualquer método da classe Um nome de campo ou método não classificado refere-se ao parâmetro this public class BankAccount public void transfer(double amount, BankAccount other) withdraw(amount); // i.e. this.withdraw(amount); other.deposit(amount);
Sobreposição de abrangência Uma variável local pode sobrepor-se a um campo com o mesmo nome. Isto porque a abrangência local prevalece sobre a abrangência de classe public class Coin private String name; private double value; // campo public double getexchangevalue(double exchangerate) double value; // variável local com o mesmo nome return value; O acesso a campos ensombrados será feito através da sua classificação com a referência this value = this.value * exchangerate; Organização de classes em pacotes Um pacote é um conjunto de classes relacionadas entre si. Para incluir uma classe num pacote, o ficheiro dessa classe deve conter como primeiro instrução package nomepacote; O nome de um pacote define-se através de um ou mais identificadores separados por pontos. Por exemplo: package ipb.exercicios; // classe IdentityCard pertence ao pacote ipb.exercicios public class IdentityCard
Organização de classes em pacotes Pacote Objectivo Exemplo java.lang Suporte da linguagem Math java.util Utilitários Random java.io Entrada e saída PrintScreen Java.awt Abstract Windowing Toolkit Color java.applet Applets Applet java.net Rede Socket java.sql Acesso a base de dados ResultSet java.swing Swing user interface JButton Especificação de pacote package packagename ; Exemplo: package ipb.exercicios; Objectivo: Para declarar que todas as classes do ficheiro pertencem a um determinado pacote
Importação de pacotes A utilização de uma classe pertencente a um pacote pressupõe a sua importação import java.util.scanner; Scanner in = new Scanner(System.in) A importação de todas as classes de um pacote pode ser feita através de uma única instrução import java.util.*; Uma classe pode ser utilizada sem importação, desde que se especifique na íntegra a sua proveniência java.util.scanner in = new java.util.scanner(system.in); Não é necessário importar classes do pacote java.lang, nem classes que pertencem ao mesmo pacote Nomes de pacotes e localização das classes A utilização de pacotes permite evitar conflitos de nomes java.util.timer vs. javax.swing.timer Os nomes dos pacotes não devem ser ambíguos e devem ser concordantes com o nome do caminho das directorias da respectiva localização O caminho de uma classe inclui a directoria base que possa conter as directorias do pacote package com.horstmann.bigjava