[Pesquisar este blog]

quinta-feira, 8 de março de 2018

POO::Plena-18-Especificadores e Modificadores

POO-P-17-Associações-agregação POO-P-19-Membros estáticos
As linguagens de programação Java e C# possuem um conjunto especial de palavras reservadas cuja aplicação afeta como os membros de uma classe podem ser utilizados ou, até mesmo, como funcionam. São os especificadores e modificadores da linguagem.

Especificadores

Os especificadores existentes estabelecem a visibilidade do membro onde se aplicam, ou seja, determinam o seu acesso e, por isso, são conhecidos também como especificadores de acesso [2][4][5][6][7]. As linguagens de programação Java e C# possuem três especificadores de acesso: public, protected, private, visto no post sobre Encapsulamento

public

Determina o acesso público, no qual os membros de uma classe têm utilização interna e externa irrestrita. Como ficam expostos na interface das classes, podem ser usados livremente pelas suas instâncias de maneira que os atributos públicos têm seus conteúdos acessados e alterados sem qualquer restrição; e os métodos públicos podem ser acionados arbitrariamente.

private

Estabelece o acesso privado, ou seja, define que o método ou atributo de classe assim declarado tem apenas uso interno, não sendo acessível externamente. Provê a ocultação dos membros assim declarados, servindo usualmente para compor a infraestrutura de uma classe.

protected

Indicar que membros de uma classe que não podem ser utilizados por meio de suas instâncias, mas apenas na construção de novas classes. Os membros protegidos, compõem uma interface de programação da classe, ou seja, um conjunto de atributos e operações destinados exclusivamente aos programadores que usarão tal classe como base para criação de outras.

Como visto no post sobre Encapsulamento, existe um quarto nível de acessibilidade denominado pacote (package) que não possui uma palavra reservada própria [4][7]. É o acesso padrão (implícito), quando outro especificador não é indicado. Seu efeito é tornar o membro público para as demais classes e instâncias presente no mesmo pacote, mas private para quaisquer outras.

O quadro que segue mostra o efeito da aplicação dos especificadores de acesso aos membros de uma classe, considerando as situações de implementação (codificação) e instanciação (criação de objetos dentro de programas).


Modificadores

Os modificadores determinam o funcionamento do membro, isto é, seu comportamento durante a execução dos programas, assim afetam como acontece a geração do seu respectivo código. Existem vários modificadores nas linguagens Java e C#: abstract, final, native, static, strictfp, synchronized, transient e volatile.

O quadro abaixo resume os efeitos provocados pelo uso destes modificadores, assim como sua aplicação típica.

O modificador static, cujo uso é bastante comum, será tratado no próximo post, que aborda os membros estáticos.

Os modificadores abstract e final serão abordados nos posts envolvendo herança e classes abstratas.

Os modificadores synchronized e volatile, destinados a programação com threads; transient, para uso junto a serialização; strictfp, para indicação de regras mais estritas para processamento de cálculos com uso de números em ponto flutuante; assim como native, empregado com o Java Native Inteface (JNI); são muito específicos e não serão abordados nesta série.

Particularidades do Java

A linguagem de programação Java, em particular, não possui os modificadores signed e unsigned existentes em outras, assim todos os seus tipos primitivos numéricos (byte, short, int, long, float e double) sempre podem representar valores positivos e negativos.

A notação ... (elipse) funciona como um modificador especial para tornar variável a lista de argumentos fornecida para um método (ou varargs) [7]. Seu uso permite que um método receba um número variável e arbitrário de valores, de maneira mais flexível que a recepção de arrays, pois seu uso admite o uso do método sem argumentos, com qualquer número de argumentos separados por vírgula (como uma lista) ou também um array.

O método que segue é do tipo varargs, ou seja, pode receber qualquer número de argumentos do tipo int, somando-os e retornando o resultado da soma.
public int somar(int ... args) {
int total = 0;
for (int i=0; i<args.length; i++) { total += args[i]; }
return total;
}

Observe que, internamente, os argumentos variáveis são tratados como se fizessem parte de um array, para o qual o número de elementos é indicado por length. Segue alguns exemplos de uso deste método.
int t1 = somar(); // uso válido, que retorna zero
int t2 = somar(0, 12, 34, 56);
int array[] = { 1, 2, 3, 4 };
int t3 = somar(array);

Finalmente, os métodos podem receber outros parâmetros, desde que aquele modificado com ... seja o último de sua assinatura.

Particularidades do C#

A linguagem de programação C# possui alguns modificadores próprios, como ref, out e param.

Por padrão a passagem de parâmetros em C# ocorre por valor, ou seja, uma cópia do valor ou do objeto indicado como argumento do método é passada como valor de seus parâmetros, de modo que se tal parâmetro é alterado dentro do método, nada ocorre com o valor original passado como argumento.

O modificador ref indica que o parâmetro será passado por referência, ou seja, o método receberá o endereço do valor ou objeto, de modo que sua alteração no interior do método acarreta mudanças no argumento usado para acionar o método, pois na verdade se trata da mesma variável ou objeto.

O modificador out, semelhante a ref, faz com que o parâmetro seja passado por referência, mas não necessita ser inicializado, ou seja, é geralmente utilizado para carregar valores para fora do método, possibilitando a existência de múltiplos valores de retorno. Por outro lado, parâmetros out devem ser inicializados dentro dos métodos onde são declarados.

Também existe modificador params, o qual permite que um método receba um número variável e arbitrário de valores, de maneira mais flexível que a recepção de arrays, pois seu uso admite o uso do método sem argumentos, com qualquer número de argumentos separados por vírgula (como uma lista) ou também um array.

O método que segue tem sua lista de parâmetros modificada com params, permitindo que receba qualquer número de argumentos do tipo int, somando-os e retornando o resultado da soma.
public int somar(params int args) {
int total = 0;
foreach (int v in args) { total += v; }
return total;
}

Observe que, como em Java, os argumentos variáveis são tratados como se fizessem parte de um array, possibilitando o uso de foreach. Segue alguns exemplos de uso deste método.
int t1 = somar(); // uso válido, que retorna zero
int t2 = somar(0, 12, 34, 56);
int array[] = { 1, 2, 3, 4 };
int t3 = somar(array);

Finalmente, os métodos podem receber outros parâmetros, desde que aquele modificado com params seja o último de sua assinatura.

Uso combinado de especificadores e modificadores

Os modificadores podem ser usados junto dos especificadores de acesso [7], por exemplo:

private static int MAX;
public static final double AVO = 6.22E23;
public static void main(String[] args) { ... }

protected transient boolean state;
public volatile long count;
private native byte AX;
public strictfp double computeSerie(double[] data) { ... } 

public synchronized void turn() { ... }
public abstract Object[] toArray() { ... }

Desta forma, muitas possibilidades diferentes podem ser escolhidas pelo programador.

POO-P-17-Associações-agregação POO-P-19-Membros estáticos

Referências Bibliográficas

[1] JAMSA, K.; KLANDER, L.. Programando em C/C++: a bíblia. São Paulo: Makron Books, 1999.
[2] PAGE_JONES, M.. Fundamentos do Desenho Orientado a Objeto com UML. São Paulo: Makron Books, 2001.
[3] SOMMERVILLE, I.. Software Engineering. 6th. Ed. Harlow: Pearson, 2001.
[4] DEITEL, H.M.; DEITEL, P.J.. Java: como programar. 6a. Ed. São Paulo: Pearson Prentice-Hall, 2005.
[5] SAVITCH, W.. C++ Absoluto. São Paulo: Pearson Addison-Wesley, 2004.
[6] JANDL JR., P. Introdução ao C++. São Paulo: Futura, 2003.
[7] JANDL JR., P.. Java - guia do programador. 3a. ed. São Paulo: Novatec, 2015.
[8] RUMBAUGH, J.; BLAHA, M.; PREMERLANI, W.; EDDY, F.; LORENSEN, W.. Object-oriented modeling and design. Englewoods Cliffs: Prentice-Hall, 1991.
[9] STROUSTRUP, B.. The C++ Programming Language. 3rd Ed. Reading: Addison-Wesley, 1997.
[10] LANGSAM, Y.; AUGENSTEIN, M. J.; TENENBAUM, A. M.. Data structures using C and C++. 2nd Ed. Upper Saddle River: Prentice-Hall, 1996.
[11] WATSON, K.; NAGEL, C.; PEDERSEN, J.H.; REID, J.D.; SKINNER, M.; WHITE, E.. Beginning Microsoft Visual C# 2008. Indianapolis: Wiley Publishing, 2008.

segunda-feira, 5 de março de 2018

POO::Plena-17-Associações-agregação de objetos

POO-P-16-Associações-composição POO-P-18-Especificadores e Modificadores
A implementação de sistemas OO envolve o uso de vários objetos inter-relacionados, ou seja, múltiplas instâncias de tipos diferentes, cujas as relações requeridas, lógicas ou de negócios, são obtidas por meio de construções conhecidas como associações.
As associações ou vínculos podem existir entre objetos diferentes, de um mesmo tipo ou de tipos diferentes [2], permitindo a realização de serviços ou a execução de operações, materializando construções ou relacionamentos entre as entidades envolvidas.

Além das associações binárias, isto é, das associações diretas entre duas classes, vistas no post Associações, existem as associações todo/parte que são muito importantes devido a grande frequência de sua ocorrência.

As associações todo/parte permitem expressão a relação existente entre as partes de um todo. Um dos tipos comuns de associação todo/parte é a composição, vista no post anterior. O outro tipo comum é a agregação, tratado a seguir.

Agregações

Uma característica fundamental de uma agregação (agregate) é que o objeto agregado (o todo) pode, potencialmente, existir sem seus constituintes (as partes).

 Considere:
  • Um empresa sem funcionários.
  • Um banco sem correntistas.
  • Um clube sem sócios.
  • Um proprietário sem propriedades.
  • Um container vazio.
Embora possa parecer estranho, tais situações são tanto legalmente, como fisicamente possíveis. Isto torna as agregações bastante diferentes das composições, mesmo sendo um outro tipo de associação todo/parte.

Além disso, a qualquer instante de tempo, um objeto constituinte (parte) pode participar de qualquer número de agregados distintos. Observe que uma pessoa pode manter várias contas correntes, possuir diversas propriedades e bens, além de ser sócia de vários clubes.

O tempo de vida dos objetos constituintes (partes) é usualmente diferente do tempo de suas agregações, tanto maior, como menor. Isto significa que podemos ter agregados (todos) que duram mais que suas partes (membros), tais como agremiações esportivas seculares, que duram mais que seus membros. Também é possível a situação inversa, onde as partes duram mais que a agregação (todo); como um grupo de pessoas que realiza uma viagem ou empresas que duram menos do que seus proprietários ou funcionários.

As agregações tendem a ser homeômeras ou homomorfas, ou seja, as partes constituintes do agregado são, geralmente, do mesmo tipo. É frequente que as agregações sejam usadas para expressar relações entre entidades.

Nos diagramas de classes UML as associações todo/parte do tipo agregação usam diamantes abertos, posicionados do lado do objeto agregado. Tipicamente existe apenas um tipo associado devido a homomorfia, com multiplicidade 0..* ou 0..n. O figura que segue ilustra este tipo de associação.


As associações numa agregação, tal como ocorre na composição, também podem ser navegáveis. A escolha da navegabilidade unidirecional e seu sentido, ou da navegabilidade bidirecional entre o objeto agregado e seus objetos participantes deve levar em conta:
  • O quão frequentemente a agregação precisa ser navegada de parte a parte.
  • A necessidade de remoção de participantes específicos.
  • Que os objetos participantes podem ser reutilizados em outras agregações (situação onde se sugere que os participantes possam referencias o objeto agregado).
Outro aspecto bastante comum e similar às composições é que as mensagens enviadas ao objeto agregado se propaguem para todos os seus participantes.


Exemplo de Agregação

Considere a classe simples Java/C# que segue, a qual possui operações de acesso e mutação do atributo nome, que não pode ser vazio ou nulo; e um construtor que exige um nome.

public class Socio {
   private String nome;

   public Socio(String nome) { setNome(nome); }

   public String getNome() { return nome; }

   public void setNome(String nome) {
      if (nome == null || nome.length()==0) {
         throw new RuntimeException(
            "Nome nao pode ser nulo ou vazio.");
      }
      this.nome = nome;
   }
}

Esta classe Socio será usada em associação a uma classe Clube, cujo código Java é dado a seguir.

public class Clube {
   private ArrayList<Socio> sociedade;

   public Clube() {
      sociedade = new ArrayList<>();
   }

   public void addSocio(Socio socio) {
      if (socio == null) {
         throw new RuntimeException("Socio nao pode ser nulo.");
      }
      sociedade.add(socio);
   }
   public int numOfSocios() { return sociedade.size(); }
   public Socio getSocio(int n) { return sociedade.get(n); }
   public Socio removeSocio(int n) { return sociedade.remove(n); }
}

Um objeto do tipo Clube pode ser criado como segue:
Clube clube = new Clube():

Nesta situação, a instância clube criada não contém qualquer sócio, ou seja, é um agregado vazio. Isto pode ser verificado com:
System.out.println("Num. socios clube = " + clube.numOfSocios());

Novos sócios podem ser adicionados livremente com:
Socio s1 = new Socio("Peter");
Socio s2 = new Socio("César");
clube.addSocio(s1);
clube.addSocio(s2);
clube.addSocio(new Socio("Benedito"));

Isto mostra que os objetos Clube (todo) são agregações de Socio (participante), podendo conter zero ou mais instâncias de seus participantes.

Os participantes podem ser eventualmente removidos da agregação com:
Socio socioRemovido = clube.removeSocio(1);

Observe que o índice fornecido para o método removeSocio(int) é um inteiro entre [0, numOfSocios()).

Composição e Agregação

Tendo visto as duas formas mais comuns de associações todo/parte, é conveniente compará-las resumidamente:
  • Composições representam associações todo/parte, onde todas as partes (componentes), de tipos iguais ou diferentes, devem existir na quantidade adequada para que o todo possa ser considerado completo. Composições são adequadas para expressar o inter-relacionamento sistêmico entre elementos.
  • Agregações expressam associações todo/parte, onde as partes (participantes/constituintes/membros) são geralmente do mesmo tipo, mas que podem participar, simultaneamente, de várias agregações diferentes. As agregações podem estar vazias e, ainda assim, existirem, pois comumente expressam relações entre o todo e suas partes.

Considerações finais

É impossível construir sistemas nos quais não existam associações entre as classes identificadas. Cada tipo de associação permite estabelecer uma relação distinta entre suas partes.

As associações binárias permitem estabelecer vínculos entre classes, geralmente denotando uma relação de uso. Também podem existir associações ternárias ou de maior cardinalidade; e até mesmo classe modeladas com o papel de representar associações particulares.

Nas associações, cada classe envolvida possui um papel e uma multiplicidade, o que permite modelar as situações existentes no mundo real. Em particular, as construções todo/parte, ou seja, composições e agregações, representam situações especiais das associações.

As composições permitem representar sistemas de objetos, onde todos os componentes devem estar presentes para que seja válida. Já as agregações possibilitam representar relações entre objetos, podem estar vazias ou conter um número variável de participantes.

Conhecer e empregar corretamente as associações é fundamental na POO!

POO-P-16-Associações-composição POO-P-18-Especificadores e Modificadores

Referências Bibliográficas

[1] JAMSA, K.; KLANDER, L.. Programando em C/C++: a bíblia. São Paulo: Makron Books, 1999.
[2] PAGE_JONES, M.. Fundamentos do Desenho Orientado a Objeto com UML. São Paulo: Makron Books, 2001.
[3] SOMMERVILLE, I.. Software Engineering. 6th. Ed. Harlow: Pearson, 2001.
[4] DEITEL, H.M.; DEITEL, P.J.. Java: como programar. 6a. Ed. São Paulo: Pearson Prentice-Hall, 2005.
[5] SAVITCH, W.. C++ Absoluto. São Paulo: Pearson Addison-Wesley, 2004.
[6] JANDL JR., P. Introdução ao C++. São Paulo: Futura, 2003.
[7] JANDL JR., P.. Java - guia do programador. 3a. ed. São Paulo: Novatec, 2015.
[8] RUMBAUGH, J.; BLAHA, M.; PREMERLANI, W.; EDDY, F.; LORENSEN, W.. Object-oriented modeling and design. Englewoods Cliffs: Prentice-Hall, 1991.
[9] STROUSTRUP, B.. The C++ Programming Language. 3rd Ed. Reading: Addison-Wesley, 1997.
[10] LANGSAM, Y.; AUGENSTEIN, M. J.; TENENBAUM, A. M.. Data structures using C and C++. 2nd Ed. Upper Saddle River: Prentice-Hall, 1996.
[11] WATSON, K.; NAGEL, C.; PEDERSEN, J.H.; REID, J.D.; SKINNER, M.; WHITE, E.. Beginning Microsoft Visual C# 2008. Indianapolis: Wiley Publishing, 2008.