quarta-feira, 8 de agosto de 2012

Aula 8 - Arrays e Encapsulamento


Na última aula (aula 7 - http://linubr.blogspot.com.br/2012/08/aula-7-estruturas-de-condicao-e.html) chegamos a trabalhar com algumas estruturas de repetição e condição disponíveis na linguagem Java. Nessa aula abordaremos a utilização de Arrays no Java e como manipular esses Arrays com estruturas de repetição. Além disso entraremos também no importante conceito de Encapsulamento. No final do paper discutiremos um exercício para empregar os assuntos discutidos.
O que são Arrays?
Quem nunca trabalhou com Array?
Arrays são vetores homogêneos de tamanho estático utilizados para armazenar um conjunto de dados. Para manipular os dados de um Array é necessário trabalhar com um índice que apontará para todas as posições alocadas, variando entre 0 e o último elemento do Array. Note que a primeira posição do índice num Array é 0. Existem outras linguagens, como o pascal, por exemplo, em que o primeiro elemento do Array é 1. Essa é uma questão que devemos atentar, pois se num Array de 5 posições nosso primeiro elemento é o de índice 0, o último elemento terá o índice 4.
Nesse caso, como o Array é estático, se tentássemos acessar a posição de índice 5 (que neste exemplo, não existe), teríamos um erro no programa (esse erro seria em tempo de execução, ou seja, enquanto o programa está rodando).
Antes ainda de estudar a sintaxe dos Arrays, vejamos alguns conceitos.
índice: como vimos, o índice é um ponteiro que define em qual posição do Array estamos em determinado trecho de código.
length: atributo que indica a quantidade de elementos que existem no Array (tamanho do Array).
Posição do primeiro elemento: como já vimos, o primeiro elemento está localizado no índice 0.
Posição do último elemento: o último elemento estará localizado na posição length - 1.
Qual é a sintaxe dos Arrays?
Os comandos para se criar um Array no Java são muito simples. Todavia, existem algumas variações. Veremos cada uma delas adiante. O formato de criação de um Array mais comportado pode ser visualizado abaixo.
Veja que a sintaxe é muito simples. Para acessarmos os valores do meuArray, devemos informar o nome da variável e passar um índice entre colchetes. Essa regra é válida para leitura ou escrita de dados no Array. Veja abaixo um exemplo.
int meuArray[] = new int[ 5 ];
meuArray[ 0 ] = 50;
meuArray[ 1 ] = 51;
meuArray[ 2 ] = 52;
meuArray[ 3 ] = 80;
meuArray[ 4 ] = 100;
System.out.println( meuArray[ 2 ] );
O primeiro comando instanciou o Array na memória (não se preocupe com isso agora, no próximo tópico vamos explicar melhor). Em seguida os valores 50, 51, 52, 80 e 100 foram guardados nas posições 0, 1, 2, 3 e 4, respectivamente. Por último o valor guardado na posição 2 (que é 52) será exibido no prompt. Uma foto desse Array, após a definição dos valores, é mostrada abaixo.
Além dessa sintaxe, existem outras formas de se criar os Arrays. Vejamos as possibilidades.
int a[];
a = new int[ 5 ];
Aqui optou-se em primeiro definir a variável a como sendo um Array de inteiros. Nesse ponto, o Array ainda não existe - ou seja, você não poderá alocar valores em suas posições. O comando seguinte é responsável pela criação do Array a na memória. Veja que, como já havíamos definido a como sendo um Array, não podemos repetir os colchetes no segundo comando.
Veja outra alternativa abaixo.
int b[] = new int[] { 4 , 5 , 6 , 7 , 8 };
Esse formato de sintaxe é bem interessante. Observe que nesse comando, além de definirmos que b é um Array, já inicializamos ele na memória como um Array de 5 posições (porque existem 5 números separados por virgula dentro das chaves), sendo que os valores 4, 5, 6, 7 e 8 já foram alocados nas posições 0, 1, 2, 3 e 4, respectivamente.
Uma versão ainda mais resumida desse comando é mostrada abaixo.
int c[] = { 4 , 5 , 6 , 7 , 8 };
O efeito aplicado ao Array c é igual ao contemplado para o Array b. A diferença aqui é que omitimos o trecho do código que cria a instância do array (que seria o comando new int[]), porém o Java coloca (por baixo dos panos) essa sintaxe para nós. Opcionalmente a representação indicada acima é mais adequada do ponto de vista que torna a declaração mais enxuta. Todavia, talvez o código mostrado na criação do Array b seja mais legível para os programadores. Fica a seu critério escolher uma das alternativas.
Como o Java trata os Arrays na memória?
Esse é um dos itens mais importantes que devemos frisar no entendimento dos Arrays. Talvez você tenha observado que usamos as palavras criar na memória, instanciar e até mesmo o comando new.
Para quê, até esse momento do curso, nós empregamos esses termos?
É isso mesmo pessoal! O Array é um objeto! Quando usamos o termo instanciar estamos falando que a JVM criou na Heap (aquela parte da memória onde ficam os objetos) um novo objeto. Por isso precisamos acrescentar o comando new na criação dos Arrays (exceto na opcão reduzida do comando - nesse caso o Java coloca o new para nós).
Por conveniência da assimilação do conteúdo, vamos representar um trecho de código e sua consequência na Stack e Heap da JVM.
int d[];
A foto da memória nesse instante pode ser visualizada abaixo. Opcionalmente não estamos indicando o nome do método em que a variável d está sendo criada. Isso não é necessário nesse exemplo.
Veja agora o próximo comando.
d = new int[ 3 ];
Aqui a variável d passou a apontar para um novo objeto Array na memória. Observe ainda que esse Array é de tamanho 3. O Java armazena o tamanho do Array no atributo length (como Array é um objeto, é mais correto chamar de atributo a variável length que fica armazenada dentro desse objeto na Heap).
Veja que além do atributo length, o próprio vetor também é armazenado na Heap. Como estamos trabalhando com uma Array de tipos primitivos inteiros, todos eles são inicializados com zero. Veja agora a seguência abaixo.
d[ 0 ] = 50;
d[ 2 ] = 51;
A partir desses comandos os valores dos elementos de índice 0 e 2 foram alterados, respectivamente, para 50 e 51. Veja que o elemento de índice 1 permanece zerado.
Essa foi uma demonstração de como o Java trabalha com Arrays. Num exercício que veremos no final desse capítulo teremos oportunidade de utilizar esses recursos.
Como manipular Arrays de forma eficiente
Bom... até agora já vimos como se cria Arrays e como se define (ou acessa) seus valores. Imagine agora que você precise criar um Array do tipo primitivo char com 1000 posições. Você também deseja guardar o caracter ‘a’ em todos os elementos.
Ahhh, essa é fácil! É só instanciar o Array e sair definindo seus valores pelos índices. Vamos lá então...
    char caracteres[] = new char[ 1000 ];
    caracteres[ 0 ] = 'a';
    caracteres[ 1 ] = 'a';
    caracteres[ 2 ] = 'a';
    caracteres[ 3 ] = 'a';
    caracteres[ 4 ] = 'a';
    caracteres[ 5 ] = 'a';
    ...
Algum tempo depois...
É claro que existem formas mais fáceis de fazer isso! Com um pouco de sorte agente até consegui evitar o L.E.R.
É só usar estruturas de repetição!! Podemos optar em criar uma estrutura com o FOR variando entre 0 e 999 e atribuir os valores ‘a’ para todos os elementos do Array. Vejamos um exemplo.
    char caracteres[] = new char[ 1000 ];
    for (int indice = 0; indice < 1000; indice++) {
      caracteres[ indice ] = 'a';
    }
Dessa forma percorremos todo o Array caracteres e guardamos em cada um de seus elementos o valor ‘a’. Veja que a variável indice assumirá valores menores que 1000. Sabendo que existe um atributo length no objeto Array que foi instanciado na memória e que esse atributo guarda exatamente o tamanho do Array, uma forma mais elegante de codificar o exemplo acima seria:
    char caracteres[] = new char[ 1000 ];
    for (int indice = 0; indice < caracteres.length; indice++) {
      caracteres[ indice ] = 'a';
    }
Assim, mesmo que no futuro a definição do tamanho do Array mude de 1000 para 50.000, por exemplo, nossa estrutura de repetição seria capaz de alimentar todos os elementos do Array (pois ela foi montada para precorrer do 0 ao length do próprio Array).
Opcionalmente nós também poderíamos ter implementado esse fluxo através da estrutura de repetição WHILE. Veja abaixo um exemplo.
    char caracteres[] = new char[ 1000 ];
    int indice = 0;
    while ( indice < caracteres.length ) {
      caracteres[ indice ] = 'a';
      indice++;
    }
Essa alternativa também funciona, porém precisamos nos preocupar em incrementar o valor de indice dentro das chaves do WHILE - para não provocar um looping infinito.
Senhoras e senhores, estamos prontos para lidar com Arrays!
O poderoso e necessário recurso do Encapsulamento
Sejam bem-vindos ao segundo pilar da Orientação a Objetos. Até o momento exploramos bastante os conceitos de Abstração. A partir de agora vamos aprender como proteger os atributos dos objetos, organizar responsabilidades de forma coesa e esconder a complexidade das classes. Poisé, isso tudo é possível com o Encapsulamento.
Para discutirmos esses assuntos, optei em tratá-los separadamente.
Atributos protegidos
Até o momento, em nossos programas exemplos, avaliamos a existência de atributos e comportamentos para os objetos. Em alguns pontos chegamos a criar métodos para definir os valores dos atributos (como foi o caso do método definirCapMax() no exercício da garrafa - http://linubr.blogspot.com.br/2012/08/aula-6-resolvendo-o-exercicio-da.html). Todavia, mesmo com esses detalhes de implementação estávamos sujeitos a problemas diversos de integridade dos atributos.
Mas afinal, o que seria a integridade de um atributo?
Depende! Vamos analisar um pequeno estudo de caso de uma classe Pessoa.
Por enquanto só modelamos um atributo para essa classe. Mesmo sem o entendimento do sistema para o qual essa classe estaria sendo modelada, podemos deduzir uma invariante básica para esse atributo. Seria algo como:

  • O atributo idade deve possuir valor superior ou igual a 0 e inferior ou igual a 130 anos.
Somos capazes de perceber claramente que uma idade com valor negativo não seria possível, independente do sistema. Dessa forma, podemos então criar um método definirIdade() - pelo qual vamos assegurar essa propriedade.
Perfeito. Vamos agora repassar esse entendimento para o código Java.
public class Pessoa {
  int idade;
  void definirIdade(int i) {
    if ( i >= 0 && i <= 130 ) {
      idade = i;
    } else {
      System.out.println( "Idade invalida!" );
    }
  }
}
Dessa forma, outras classes do sistema poderiam utilizar do método definirIdade() para configurar um estado íntegro do objeto Pessoa. Vejamos, por exemplo, um código de uma classe Executora auxiliar.
public class Executora {
  public static void main(String args[]) {
    Pessoa p = new Pessoa();
    p.definirIdade( -10 );
  }
}
Veja que, mesmo que a classe Executora faça uma tentativa de definição inválida para o atributo idade através do método definirIdade(), nós estaremos preservando o estado íntegro desse atributo - e consequentemente o estado íntegro do objeto Pessoa como um todo.
Podemos entender que existe uma boa coesão nessa modelagem, pois a classe Pessoa conseguiu preservar sua integridade por si só - assumindo sua responsabilidade no sistema.
Todavia não podemos ainda afirmar que os atributos estão protegidos. Veja uma alternativa da classe Executora indicada abaixo.
public class Executora {
  public static void main(String args[]) {
    Pessoa p = new Pessoa();
    p.definirIdade( -10 );
    p.idade = -50;
  }
}
O aconteceria se compilássemos e execuássemos essas duas classes?
Não ocorreria nenhum erro de compilação ou execução, todavia, assim que o comando p.idade = -50; for executado, o estado do objeto Pessoa deixará de estar íntegro.
Esse é exatamente o ponto que precisamos discutir. Veja que a classe Executora deveria ter como ponto único de acesso ao atributo idade o método definirIdade(), por outro lado, nada em nosso código foi definido para restringir o acesso direto ao atributo idade.
Qual seria a ideia? E se o Java (e a Orientação a Objetos) nos permitisse definir uma regra de visibilidade que inviabilizasse o acesso ao atributo idade do lado de fora da classe Pessoa...
Imagine uma situação onde pudéssemos fazer com que todos os atributos do objeto pudessem ficar trancafiados e inacessíveis diretamente. Isso tornaria obrigatório o uso de métodos específicos para alterar e obter o valor dos atributos. Pois é exatamente isso que faremos na codificação a seguir.
public class Pessoa {
  private int idade;
  void definirIdade(int i) {
    if ( i >= 0 && i <= 130 ) {
      idade = i;
    } else {
      System.out.println( "Idade inválida!" );
    }
  }
}
Note o comando private destacado. Esse comando é usado para definir o tipo de visibilidade privada, onde somente a própria classe poderá acessar diretamente o atributo em questão (que é exatamente o que ocorre dentro do método definirIdade() - ou seja, esse método deve e pode acessar o atributo).
Por outro lado, todos os objetos de fora dessa classe não terão acesso ao atributo, ficando obrigatório o uso dos métodos definidos na classe Pessoa. Isso significa que o código abaixo apresentará um erro de compilação.
public class Executora {
  public static void main(String args[]) {
    Pessoa p = new Pessoa();
    p.definirIdade( -10 );
    p.idade = -50;
  }
}
Como o atributo recebeu a visibilidade private, qualquer classe externa não poderá acessá-lo diretamente.
Veja que a classe Pessoa pode ser compilada normalmente. Todavia, ao compilarmos a classe Executora, a mensagem de erro “idade has private access in Pessoa” foi exibida e o programa não será compilado. Isso significa justamente que ninguém poderá acessar esse atributo. A única forma que Executora terá acesso à idade será por intermédio do método definirIdade(). Re-editando o código de Executora, temos.
public class Executora {
  public static void main(String args[]) {
    Pessoa p = new Pessoa();
    p.definirIdade( -10 );
  }
}
Podemos agora compilar as classes sem problemas. Ao executar o programa, temos o seguinte resultado.
Veja que ao tentar definir um valor inválido para a idade, a classe Executora gerou uma mensagem de erro no prompt (que havia sido definida dentro da classe Pessoa).
Da mesma forma que Executora não pode acessar o atributo idade diretamente para definir um valor, ela também não poderá acessá-lo para obter o valor. Isso significa que precisaremos criar um método na classe Pessoa para permitir que as outras classes do sistema possam obter o valor de idade. Vamos re-escrever a classe Pessoa criando esse método de acesso.
public class Pessoa {
  private int idade;
  void definirIdade(int i) {
    if ( i >= 0 && i <= 130 ) {
      idade = i;
    } else {
      System.out.println( "Idade invalida!" );
    }
  }
  int obterIdade() { return idade; }
}
Re-escrevendo também a classe Executora (para utilizar esse novo método).
public class Executora {
  public static void main(String args[]) {
    Pessoa p = new Pessoa();
    p.definirIdade( -10 );
    p.definirIdade( 30 );
    System.out.println( "A idade da pessoa e: " + p.obterIdade() );
  }
}
Assim, após a execução do programa, temos:
Considerando apenas essas duas classes, podemos dizer que a classe Pessoa está encapsulada. Mas existe vida além da visibilidade private...
Outras visibilidades
A UML e o Java nos oferecem quatro tipos de visibilidade:
Sintaxe no Java
Notação na UML
Definição
private
-
A atributo ou método poderá ser visualizado e utilizado apenas dentro da classe no qual foi declarado.
~
O atributo ou método poderá ser visualizado e utilizado por elementos que estejam declarados dentro do mesmo pacote, no qual está inserida a classe que o declarou - isso também considera a própria classe.
Obs1. Como ainda não estudamos a definição dos pacotes, assuma por enquanto a ideia de que uma classe pertence ao mesmo pacote de outra quando estão fisicamente localizadas dentro de um mesmo diretório do sistema operacional. Atenção: a definição formal será explicada no futuro!
Obs2. Veja que não existe comando no Java correspondente à visibilidade de pacote. Para definir um atributo ou método com essa visibilidade, basta não colocar nada.
protected
#
O atributo ou método poderá ser visualizado e utilizado apenas dentro da classe que o declarou, pelas classes do mesmo pacote e por suas classes descendentes (herança - ou seja, as classes filhas da classe que o declarou).
Obs1. Essa definição ficará mais clara quando estudarmos herança (próximo tópico dos pilares da orientação a objetos).
Obs2. A definição de protected na UML é distinta da utilizada no Java. Na UML um atributo ou método protegido só pode ser acessado dentro da própria classe ou pelas classes descendentes. No Java, além dessas duas possibilidades, as classes do mesmo pacote também terão acesso. A regra do protected da UML é mais restritiva.
public
+
O atributo ou método declarado como público poderá ser acessado ou modificado por qualquer elemento do sistema.
No exemplo da classe Pessoa usamos a visibilidade private. Agora pergunto: qual seria a visibilidade dos métodos definirIdade() e obterIdade()?
Exatamente! Eles deveriam ser públicos. Entenda da seguinte maneira: nós garantimos a regra para o atributo idade através dos métodos de acesso obterIdade() e definirIdade(). Isso significa que qualquer classe do sistema que fizer o acesso a esse atributo por esses métodos não tornará o objeto Pessoa inconsistente. Portanto a forma mais correta seria declarar esses métodos como públicos.
Re-escrevendo o código da classe Pessoa com essas considerações, nós teremos o resultado abaixo.
public class Pessoa {
  private int idade;
  public void definirIdade(int i) {
    if ( i >= 0 && i <= 130 ) {
      idade = i;
    } else {
      System.out.println( "Idade invalida!" );
    }
  }
  public int obterIdade() { return idade; }
}
Veja que agora todos os elementos da classes possuem visibilidade definida.
Seria errado definir um atributo como público?
Errado não! Mas é pouco comum. Geralmente os atributos devem obedecer regras específicas de domínio (ou regras para que seus estados fiquem consistentes). Definir um atributo como público permitirá o acesso direto. Em algumas situações talvez possamos considerar válido qualquer valor que um atributo assumir.
Qual deve ser a visibilidade das variáveis internas aos métodos da classe?
Atenção: o conceito de visibilidade só faz sentido aos atributos e métodos da classe. A variável interna utilizada nos métodos só existe dentro do escopo do método - não podendo receber atribuições de visibilidade.
Até hoje, nas outras aulas, nós nunca falamos sobre a visibilidade dos atributos e métodos das classes. Ou seja, não colocamos nada em suas definições. Quando não definimos a visibilidade desses elementos, qual visibilidade o Java considera?
Na tabela apresenta anteriormente, chegamos a comentar que a visibilidade package no Java não possui correspondente em comando. Logo, sempre que omitirmos a visibilidade dos atributos ou métodos, eles serão automaticamente considerados com a visibilidade de pacote.
Isso significa que até hoje todos os atributos e métodos que criamos ficaram, por default, com a visibilidade package.
Seria errado definir um método como privado?
Essa pergunta é tão interessante que vamos tratá-la em um tópico separado, abaixo.
Escondendo a complexidade do código
No mundo real, que operação principal um piloto deve exercer sobre um carro para frear?
Obs. Na foto, a mulher está pisando no acelerador. Mas imagine que ela esteja pisando no freio.
É isso mesmo! Para frear o carro o piloto deve pressionar o pedal de freio. Mas o que acontece no carro quando esse pedal é pressionado?
Veja que uma série de operações internas ao carro são executados para efetivamente diminuir a velocidade. A pergunta é: o piloto precisa saber o que acontece por trás dos panos?
Se transportarmos esse entendimento para a Orientação a Objetos, observe que a classe Carro possui, além do comportamento público de pressionarFreio(), comportamentos internos (privados), tais como obterFluidoFreio(), transmitirFluido(), acionarFreios(), entre outros.
Em nenhum momento o objeto Piloto, que estará interagindo diretamente com uma instância da classe Carro, utilizará esses métodos internos. O mais correto então, segundo as intenções do Encapsulamento, seria definir tais métodos como privados. Assim, Piloto não precisaria nem poderia acessar - no português claro: restringimos seu acesso a esses métodos para evitar que ele faça uma cagada!!!
Esse entendimento de configurar métodos internos ao funcionamento da classe como privados nos ajuda a esconder a complexidade da classe. Em nenhum outro trecho do sistema além da classe Carro saberíamos da existência desses métodos. Isso ajuda no processo de desenvolvimento Orientado a Objetos como um todo.
Como seriam essas classes no Java? Veja abaixo um exemplo de implementação.
public class Carro {
  private int qtdeFluidoFreio;
  private int velocidade;
  public void pressionarFreio() {
    int fluido = obterFluidoFreio();
    transmitirFluido( fluido );
  }
  private int obterFluidoFreio() {
    if ( qtdeFluidoFreio >= 2 ) {
      qtdeFluidoFreio -= 2;
      return 2;
    }
    return 0;
  }
  private void transmitirFluido(int fluido) {
    acionarFreios(fluido);
  }
  private void acionarFreios(int fluido) {
    if ( velocidade > 0 && fluido == 2 ) {
      velocidade--;
    }
  }
  // Outras operações de carro
}
Considerando que a classe Piloto fosse similar a uma classe Executora (essa não seria uma boa modelagem, mas vamos deixar assim mesmo).
public class Piloto {
  public static void main(String args[]) {
    Carro carro = new Carro();
   
    // Outras operações de carro
    carro.pressionarFreio();
  }
}
Veja que Carro possui muitos comportamentos, mas todos eles foram ocultados para os objetos do sistema. O Encapsulamento também prega essa ideia.
Como representar a visibilidade na UML
Na tabela apresentada no início do tópico anterior chegamos a mostrar a representação de cada um dos tipos de visibilidade na UML. Veja abaixo um resumo.
Codificando essa classe no Java temos:
public class ClasseExemplo {
  private int atributoPrivado;
  int atributoPacote;
  protected int atributoProtegido;
  public int atributoPublico;
  private void metodoPrivado() {}
  void metodoPacote() {}
  protected void metodoProtegido() {}
  public void metodoPublico() {}
}
Tente absorver a sintaxe de cada uma das opções. Confesso que o uso da visibilidade package é menos explorada se comparada às outras. Confesso também que você só poderá empregar a visibilidade protected quando estudarmos Herança. Todavia, daqui pra frente, em todos nossos exercícios devemos considerar as propriedades do Encapsulamento, principalmente as visibilidades private e public - que nos serão mais necessárias por enquanto.
Exercício de Fixação sobre Arrays e Encapsulamento
Dessa vez vamos abordar uma estratégia de exercício diferente: eu vou me abstrair (por enquanto) da modelagem e descrever uma pequena situação para vocês. Para resolver o exercício vocês devem realizar a modelagem do sistema e codificar essa modelagem no Java. Os preceitos da Abstração e Encapsulamento devem ser considerados.
Requisitos Funcionais do Sistema 
  • Criar um sistema que permita o cadastro de uma turma que pode conter no máximo 10 alunos; 
  • O sistema permite informar a nota de cada aluno; 
  • No final o sistema deve calcular a média da turma.
Estou sendo evasivo de propósito! Precisamos tentar abstrair uma situação textual e ser capazes de visualizar quais objetos, atributos e métodos são necessários na Orientação a Objetos. Além disso temos também de ser capazes de transformar os objetos em classes no Java. Esse é o objetivo desse exercício. Ahh sim, claro: você precisará considerar as visibilidades dos atributos e métodos. Antes de começar, peço que leia o conteúdo abaixo - que mostrará um conceito que você vai precisar para resolver.
Como modelar a associação entre dois objetos na UML?
A modelagem desse exercício necessidade de uma característica da UML que ainda não destacamos: associação entre os objetos. Se observarmos os requisitos funcionais do sistema percebemos que uma Turma é composta por vários alunos. No Java podemos definir que a classe Turma tem um Array de Alunos (já é uma dica também!!). Na UML, a forma de se representar isso no diagrama de classes seria.
Veja que foram definidos duas classes. A associação entre elas indica, graças aos valores 1 e *, que: 
  • uma Turma possui no mínimo zero e no máximo muitos Alunos; e 
  • uma Aluno deve estar associado a uma Turma - no caso desse sistema, não faz sentido que um Aluno não esteja em nenhuma Turma.
Considerando que uma Turma na realidade só pode conter no máximo 10 Alunos e que não faz sentido existir uma Turma sem nenhum Aluno, podemos refinar essa modelagem para:
Observe que acrescentei ainda a palavra alunos próximo da classe Aluno. Isso significa que no código, a classe Turma conterá um Array privado (note o sinal de - antes de alunos) de nome alunos. Como ainda não vimos esse tipo de transposição (associação entre objetos para código Java), vou ajudá-los mostrando como ficaria essas duas classes no Java. Primeiro a classe Aluno:
public class Aluno {
}
É... realmente ela ainda não tem nada. Você vai melhorar essa classe quando resolver o exercício. Já na classe Turma, podemos notar a associação com Aluno.
public class Turma {
  private Aluno[] alunos = new Aluno[ 10 ];
}
Veja que criamos um Array privado de nome alunos já instanciado com 10 elementos na Heap. Cabe a você agora pensar nos outros atributos e métodos que essa classe deveria ter.
Agora é com você!
Caro leitor, postei de propósito essa figura abaixo apenas para chamar sua atenção! Tente resolver esse exercício antes de olhar uma prévia do resultado adiante (o exercício só será resolvido na aula 9). Uma dica: seria interessante começar pelo modelo de casos de uso. Em seguida faça o diagrama de classes. Se houver necessidade, implemente outros diagramas. À princípio esses dois já são suficientes para o exercício.
Resolução em Sala de Aula
Como eu já havia comentado, esse material está sendo desenvolvido concorrentemente com o estudo de Java na empresa. Pedi ao pessoal para modelar em grupo uma solução para o exercício. Vou postar agora as fotos do resultado que eles chegaram em sala.
Bom, devido a qualidade péssima das fotos do meu celular, deixo a critério da curiosidade de vocês tentar descobrir o que está escrito nas imagens. Caso não consiga ler, não tem problema!! Na próxima aula vou representar essa modelagem em diagrama UML legíveis. Veremos também a codificação desse exercício.
É isso ai pessoal, obrigado pela leitura!
Abraços, Guilherme Pontes

Nenhum comentário:

Postar um comentário