Naked Objects
Introdução
Osvaldo Kotaro Takai João Eduardo Ferreira (takai,jef)@ime.usp.br
Mini-curso SBBD/SBES – out-2004
Completeza Comportamental
As pessoas acham que estão usando a OO para projetar e desenvolver sistemas; mas não estão.
Elas ignoram o princípio mais fundamental da OO que é a completeza comportamental:
um objeto deve modelar, por completo, o
comportamento das coisas que ele representa.
A completeza comportamental é essencial porque ela é a chave para obter o principal benefício da orientação a objetos:
a habilidade de lidar com as mudanças inesperadas nos requisitos.
Separação dos Dados e Processos
Ao invés disso, elas projetam sistemas que separam o
processo de seus dados, embora revestidos com
linguagens e tecnologias OO.
A separação dos processos de seus dados pode estar
relacionada principalmente a inércia:
Isto é, as pessoas aprenderam a projetar sistemas dessa forma e têm dificuldades de pensar de outra maneira.
Objeto Dado
Objeto Processo
Objeto Inércia
Separação dos Dados e Processos
A inércia individual é reforçada por práticas organizacionais que forçam a separação dos
processos de seus dados, mesmo que o projetista de software queira adotar uma abordagem mais pura de OO:
Orientação a processos de negócio.
Interfaces de usuário otimizadas a tarefas.
Métodos orientados a use-cases.
O padrão Model-View-Controller.
Desenvolvimento de software baseado em componentes.
Objeto Dado
Objeto Processo
Objeto Práticas Organizacionais
Separação dos Dados e Processos
Essas cinco forças são consideradas as melhores práticas da OO.
Não se pode simplesmente descartá-las, pois:
São práticas conscientes que claramente geram benefícios.
Foram projetadas para reduzir riscos conhecidos no processo de desenvolvimento.
Porém, essas práticas provocam um efeito colateral:
desencorajam o projeto de objetos comportamentalmente completos.
Abordagem alternativa para projetar sistemas
A alternativa é projetar sistemas que tratem os usuários como solucionadores de problemas.
Muitos negócios já possuem alguns sistemas que são, por natureza, problemas a serem resolvidos.
Todos os programas de desenho, do PowerPoint aos sistemas CAD/CAE, são desse tipo, assim como as planilhas eletrônicas.
No entanto, na maioria dos negócios, esses
sistemas não são considerados ‘tradicionais’.
Abordagem alternativa para projetar sistemas
Os sistemas tradicionais estão normalmente
preocupados com o processamento padronizado das transações de negócio, e são otimizados para um conjunto de tarefas, os quais são quase
sempre implementados como processos
seqüenciais.
The Incredible Machine
The Incredible Machine (www.sierra.com) é um grande exemplo de um sistema de problema a ser resolvido, e que claramente revela sua
estrutura orientada a objetos ao usuário.
The Incredible Machine
É apresentado ao usuário um problema a ser resolvido – neste caso um balão de prata que tem que ser
estourado ...
The Incredible Machine
... usando algumas das
peças disponíveis (objetos), sendo que cada um
pode ser
inspecionado
para revelar suas propriedades
físicas e seus
comportamentos:
The Incredible Machine
Inicialmente, nós usamos uma
almofada
antigravitacional e um bloco de madeira para desviar a bola sobre o êmbolo.
The Incredible Machine
… a qual irá detonar o TNT por controle
remoto. Usando uma corda e duas polias e um
contrapeso, nós ligamos o balde tal que ele pegue a bola:
The Incredible Machine
O contra-peso é um balde furado, e quando uma quantidade
suficiente de água vazar, o balde e a bola caem sobre a tesoura, liberando o balão, o qual irá estourar quando encostar no
alfinete acima:
As ‘melhores práticas’ no projeto de sistemas de negócio contemporâneos dividem uma aplicação em quatro camadas principais
Apresentação
Aplicação, Processo ou Controlador de Use-Case
Modelo de objetos de domínio
Persistência
Apresentação
Aplicação, Processo ou Controlador de Use-Case
Modelo de objetos de domínio
Persistência
O Padrão Naked Objects elimina a camada
controlador encapsulando todas as funcionalidades
de negócio nos objetos entidade
E tem uma camada de apresentação genérica que automaticamente reflete o modelo de objetos de domínio como uma interface de usuário orientada a objetos
Apresentação
Aplicação, Processo ou Controlador de Use-Case
Modelo de objetos de domínio
Persistência
Para um sistema standalone, ou para prototipação, é também possível autogerar a persistência a partir do
mesmo modelo de domínio
Presentation
Aplicação, Processo ou Controlador de Use-Case
Modelo de objetos de domínio
Persistência
Naked Objects
Introdução ao Framework
Naked Objects
O que é?
O framework Naked Objects é uma pacote de
software open-source que facilita a construção
completa de aplicações de negócio a partir de
naked objects (objetos comportamentalmente
completos).
O que o pacote contém?
Um conjunto de classes Java. A mais
importante classe desse conjunto é NakedObject, da qual você deriva subclasses para definir cada uma das classes de objetos de negócio (tal como Cliente). As outras classes do pacote fornecem funcionalidades a serem usadas dentro dos
objetos de negócio.
O que o pacote contém?
Um ambiente de execução, que inclui:
Um mecanismo de visualização que cria, em tempo real, uma representação manipulável pelo usuário de qualquer naked object que o usuário precisar acessar.
Um mecanismo de persistência, o qual torna o naked objects persistente via uma classe de persistência específica. O
framework já vem com uma classe de persistência que armazena cada naked object como um arquivo XML. Isso é bom para realizar a prototipação rápida, mas não é escalável.
Classes de persistência mais sofisticadas devem ser escritas em sistemas reais.
O que o pacote contém?
Ambos os mecanismos funcionam de forma
autônoma: eles usam um mecanismo comum de reflexão para inspecionar suas definições de
objetos de negócio quando necessários.
O programador não precisa se preocupar
diretamente com os mecanismos de visualização ou de persistência.
O ambiente de execução também fornece
interface entre os naked objects e outros serviços de infra-estrutura tais como para segurança e
autorização.
O que o pacote contém?
Um conjunto de frameworks de teste. O framework de testes unitários é uma simples extensão do popular framework JUnit, tratando as capacidades específicas dos naked objects.
Existe um framework separado para testes de aceitação, o qual facilita a confecção dos testes de aceitação antes de escrever as funcionalidades (como defende a Extreme Programming). Como a maioria desses cenários de teste representam
tarefas comuns executadas pelos usuários, este
framework tem a vantagem adicional de poder
gerar a documentação do usuário dessas tarefas
diretamente a partir da definição dos testes.
O que é necessário para a execução?
O Naked Objects precisa do Java versão 1.1 ou superior para a sua execução. Para desenvolver aplicações, será necessário uma IDE ou um JDK.
Para executar exemplos, somente o JRE é necessário.
É importante assegurar que o Java esteja instalado apropriadamente e que o seguinte
comando funcione (digite o comando abaixo na janela comandos do DOS):
java -version
O que é necessário para a execução?
Após pressionar <enter>, os detalhes da versão Java deverá aparecer:
java version "1.4.1_02"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_02-b06) Java HotSpot(TM) Client VM (build 1.4.1_02-b06, mixed mode)
Se nada aparecer ou se uma mensagem de erro aparecer, então será necessário instalar ou
reinstalar o JDK adequado para o SO em uso.
O que é preciso baixar?
Para executar um exemplo para ver como
trabalha uma aplicação Naked Objects, baixe o seguinte programa:
http://www.nakedobjects.org/downloads/ecs-demo.zip
Para desenvolver uma aplicação Naked Objects, é necessário baixar o framework NakedObjects:
http://www.nakedobjects.org/downloads/nakedobjects1_0_2.zip
.
Desempacote o arquivo no diretório c:\
Para facilitar o desenvolvimento, é interessante utilizar algum ambiente de desenvolvimento Java. No caso, sugere-se o IDE Eclipse.
A idéia básica
A tela a seguir exibe uma aplicação Naked Object típica em uso.
É um sistema de declaração de despesas que permite aos empregados preparar e gerenciar a manutenção das despesas.
Cada ícone da tela representa um objeto onde os
maiores, à esquerda, representam as classes ou
tipo de objetos disponíveis no sistema.
A idéia básica
A idéia básica
As classes de objetos com as quais o usuário pode iniciar o trabalho (criar novas
instâncias, encontrar
instâncias existentes, etc.) é exibido na janela de classes.
Cada ícone representa uma
classe.
A idéia básica
Novas instâncias de um objeto podem ser criadas arrastando-se a classe para a área de trabalho.
Ou através do menu pop-up acessado com o botão direito do
mouse.
A idéia básica
Cada objeto criado está disponível ao usuário e quando exibidos aparecem na área
restante da
tela.
A idéia básica
Cada objeto pode ser visualizado de formas diferentes – coleções pode ser visualizadas como tabelas e listas, Empregados como
formulários, etc.
Tabela Lista
Formulário Texto
A idéia básica
Cada objeto de
negócio também tem um menu que é usado para acessar as
capacidades desse objeto.
Esse menu pode ser acessado
pressionando-se o botão direito sobre o título do objeto.
Título
A idéia básica
Os objetos de valor – texto, número, datas – que um objeto de negócio possui,
podem ser editados usando o teclado e o mouse.
Texto
Número (Moeda) Data
A idéia básica
Os objetos de negócio podem também ser arrastados e soltos sobre outros objetos de
negócio ou dentro de campos que permitem esse
tipo de objeto (indicado em cor verde).
A idéia básica
Para construir sistemas com base no Naked Objects, o importante é se concentrar nos
objetos de negócio – Cliente, Chamada, Pedido, Fatura, Vôo, Aeroporto, Avião, Equipe, etc. – e deixar a interface e persistência para o
framework.
Esses objetos são definidos como classes Java seguindo um pequeno número de convenções de codificação.
A implementação da interface é fornecida pelo
Naked Objects.
Exemplo 1
A idéia básica
Exemplo 1
Vamos supor que temos a missão de construir uma aplicação que gerencie os papéis existentes em um projeto (Programador,
Analista, Líder de Projeto, etc.), bem como os
empregados que exercem esses papéis.
O diagrama de classes em
UML é apresentado ao lado:
Empregadonom eProjeto
nome criaLíder()
Papel
nome 0..*
0..1 0..*
0..1 0..*
0..*
0..*
0..*
Objetos da classe Projeto
referenciam objetos da classe Papel.
Objetos da classe Papel referenciam objetos da
classe Empregado.
Exemplo 1
A aparência da
aplicação é exibida ao
lado:
Exemplo 1
Posso criar um novo projeto (instância ou objeto da classe
Projeto),
simplesmente
arrastando-o para
uma área livre:
Exemplo 1
Agora posso digitar o nome do projeto.
Vamos chamá-lo de
“River Fisher”.
River Fisher
Exemplo 1
Com o cursor do
mouse sobre o objeto, agora chamado “River Fisher”, pressiono o botão direito do
mouse para visualizar
o menu pop-up.
River FisherExemplo 1
Seleciono a opção
“Criar Lider”, para criar um novo objeto da
classe Papel.
Por default, essa opção irá, automaticamente, preencher o atributo Nome desse novo
objeto com a String:
“Líder de Projeto”.
River Fisher
Exemplo 1
Quero agora associar um empregado ao
papel “Líder de Projeto”.
Para isso, arrasto o ícone da classe
Empregado para o
campo Empregado do objeto Líder de Projeto
essa é uma forma
abreviada de criar um objeto.
Note que o campo fica esverdeado.
River Fisher
Exemplo 1
Para dar um nome ao objeto Empregado
recém criado posso, dar um duplo-clique sobre o ícone do
objeto Empregado que aparece no campo
Empregado do objeto
“Líder de Projeto”.
E então, digitar o nome: “Jef”.
River Fisher
Exemplo 1
A fim de verificar se o objeto Jef foi de fato criado, basta
pressionar o botão
direito do mouse sobre o ícone da classe
Empregado e
selecionar a opção
Empregados do menu pop-up.
Isso fará com que todos os objetos pertencentes à classe Empregado
sejam mostrados.
River Fisher
Exemplo 1
Antes de prosseguir, exercite este
exemplo no seu computador e assegure- se de que tenha entendido a metáfora da interface:
“arrastar e soltar”
bem como a forma de acessar o menu pop-up.
Para tanto, execute o arquivo:
ExecutarExemplo1.bat
O Código da Classe Empregado
Exemplo 1
O Código da Classe Empregado
Começando pelo mais simples, vamos criar a classe Empregado.
Empregado
nom e
Projeto
nome criaLíder()
Papel
nome 0..*
0..1 0..*
0..1 0..*
0..*
0..*
0..*
O Código da Classe Empregado
A classe Empregado possui apenas um atributo: nome, supondo que isso seja o
suficiente para distinguí-lo.
O código completo da classe Empregado que
utiliza o framework Naked Objects é exibido ao lado:
Empregado
nom e
importorg.nakedobjects.object.AbstractNakedObject;
importorg.nakedobjects.object.Title;
importorg.nakedobjects.object.value.TextString;
public classEmpregadoextendsAbstractNakedObject { private finalTextStringnome= newTextString();
publicTextString getNome() { returnnome;
}
publicTitle title() { returnnome.title();
} }
O Código da Classe Empregado
A indicação de que a classe Empregado é um naked
object é realizada
estendendo-se a classe Empregado da classe AbstractNakedObject.
A classe
AbstractNakedObject está definida no pacote
importado:
org.nakedobjects.object.AbstractNakedObject.
importorg.nakedobjects.object.AbstractNakedObject;
importorg.nakedobjects.object.Title;
importorg.nakedobjects.object.value.TextString;
public classEmpregado extendsAbstractNakedObject{ private finalTextString nome = newTextString();
publicTextString getNome() { returnnome;
}
publicTitle title() { returnnome.title();
} }
AbstractNakedObject
Empregado
nome
O Código da Classe Empregado
O atributo nome é do tipo TextString e é utilizado para armazenar
informações textuais simples.
A definição do tipo
TextString está definido no pacote:
org.nakedobjects.object.value.TextString.
Note que todo atributo de um objeto naked object é exibido pela interface
gerada pelo framework Naked Object como um
campo, no caso Nome com N maiúsculo.
importorg.nakedobjects.object.AbstractNakedObject;
importorg.nakedobjects.object.Title;
importorg.nakedobjects.object.value.TextString;
public classEmpregado extendsAbstractNakedObject { private finalTextStringnome= newTextString();
publicTextString getNome() { returnnome;
}
publicTitle title() { returnnome.title();
} }
Atributo Campo
O Código da Classe Empregado
O atributo nome é
declarado como private final para indicar que ele deve ser inicializado antes de ser usado e que não pode ser diretamente alterado.
A alteração somente é permitida pela interface fornecida pelo Naked Objects.
importorg.nakedobjects.object.AbstractNakedObject;
importorg.nakedobjects.object.Title;
importorg.nakedobjects.object.value.TextString;
public classEmpregado extendsAbstractNakedObject { private finalTextStringnome= newTextString();
publicTextString getNome() { returnnome;
}
publicTitle title() { returnnome.title();
} }
Interface
O Código da Classe Empregado
getNome() é um método public o qual permite que outras classes consultem o conteúdo do atributo
nome; no caso Jef.
importorg.nakedobjects.object.AbstractNakedObject;
importorg.nakedobjects.object.Title;
importorg.nakedobjects.object.value.TextString;
public classEmpregado extendsAbstractNakedObject { private finalTextStringnome= newTextString();
publicTextStringgetNome() { returnnome;
}
publicTitle title() { returnnome.title();
} }
O Código da Classe Empregado
title() é um método public o qual permite que outras classes consultem o título do objeto.
Neste exemplo, o título do objeto é igual ao valor do atributo nome convertido para o tipo Title, que no caso é Jef.
Note que o método title retorna um objeto do tipo Title, o qual está
especificado em:
org.nakedobjects.object.Title.
importorg.nakedobjects.object.AbstractNakedObject;
importorg.nakedobjects.object.Title;
importorg.nakedobjects.object.value.TextString;
public classEmpregado extendsAbstractNakedObject { private finalTextStringnome= newTextString();
publicTextString getNome() { returnnome;
}
publicTitle title() { returnnome.title();
} }
Título
Conversão de TextString para Title Título
O Código da Classe Empregado
Para poder testar (ou explorar) essa classe naked object, crie uma classe chamada Run
(poderia ter outro nome), como ilustra o código ao lado.
Note que a classe Run
estende a classe do Naked Objects Exploration que está definida em:
org.nakedobjects.Exploration.
import org.nakedobjects.Exploration;
import org.nakedobjects.object.NakedClassList;
public class RunextendsExploration{ public static void main(String args[]) {
newRun();
}
public void classSet(NakedClassList classes) { classes.addClass(Empregado.class);
} }
O Código da Classe Empregado
A classe Run possui um método chamado main;
isso indica ao JVM que o início da execução do programa começa nessa classe.
Quando o método main é executado, ele cria uma instância de sua própria classe: um objeto Run.
import org.nakedobjects.Exploration;
import org.nakedobjects.object.NakedClassList;
public class RunextendsExploration { public static void main(String args[]) {
newRun();
}
public void classSet(NakedClassList classes) { classes.addClass(Empregado.class);
} }
O Código da Classe Empregado
Agora que o objeto Run foi criado, o framework pode, então, chamar o método classSet, o qual adiciona a classe Empregado
(addClass) na lista de classes que será
disponibilizado ao usuário.
No caso foi acrescentado apenas uma classe:
Empregado.
import org.nakedobjects.Exploration;
import org.nakedobjects.object.NakedClassList;
public class RunextendsExploration { public static void main(String args[]) {
newRun();
}
public void classSet(NakedClassList classes) { classes.addClass(Empregado.class);
} }
O Código da Classe Empregado
Pode-se notar que, embora a classe se chame
Empregado, o nome apresentado na lista de classes é Empregados (plural).
O framework sempre faz isso.
Por default o framework adiciona
automaticamente um ‘s’
no final de todos os nomes de classe.
Veremos como mudar isso posteriormente.
import org.nakedobjects.Exploration;
import org.nakedobjects.object.NakedClassList;
public class RunextendsExploration { public static void main(String args[]) {
newRun();
}
public void classSet(NakedClassList classes) { classes.addClass(Empregado.class);
} }
Plural
Singular
O Código da Classe Empregado
Copie o código da classe Empregado (ao lado) num arquivo com o nome
Empregado.java.
Para isso, utilize um editor de textos, por exemplo, o Noteped (Bloco de Notas).
O nome do arquivo tem que ser exatamente esse:
Empregado.java e não empregrado.java.
Salve o arquivo
Empregado.java dentro de um diretório, por exemplo, c:\exemplo1.
import org.nakedobjects.object.AbstractNakedObject;
import org.nakedobjects.object.Title;
import org.nakedobjects.object.value.TextString;
public class Empregado extendsAbstractNakedObject {
private finalTextString nome = newTextString();
publicTextString getNome() { returnnome;
}
publicTitle title() { returnnome.title();
} }
O Código da Classe Empregado
Faça o mesmo com a classe Run:
Copie o código da classe Run (ao lado) num arquivo com o nome Run.java.
E salve o arquivo Run.java dentro do mesmo diretório onde está
Empregado.java, por exemplo, c:\exemplo1.
import org.nakedobjects.Exploration;
import org.nakedobjects.object.NakedClassList;
public class RunextendsExploration { public static void main(String args[]) {
newRun();
}
public void classSet(NakedClassList classes) { classes.addClass(Empregado.class);
} }
O Código da Classe Empregado
Crie agora um diretório chamado images.
O Código da Classe Empregado
Copie do diretório generic- large-icons do framework Naked Objects, o arquivo CallCentreAgent32.gif.
O Código da Classe Empregado
E cole o arquivo
CallCentreAgent32.gif no diretório images.
O Código da Classe Empregado
Copie agora, do
diretório generic-small- icons, o arquivo
CallCentreAgent16.gif.
E cole-o no diretório images.
O Código da Classe Empregado
Renomeie os arquivos:
CallCentreAgent32.gif para Empregado32.gif
CallCentreAgent16.gif para Empregado16.gif
Isso fará com que o framework associe os ícones à classe
Empregado.
O Código da Classe Empregado
Para compilar, digite a seguinte linha de comando na janela DOS e pressione enter.
Isso irá gerar os arquivos:
Empregado.class
Run.class
O Código da Classe Empregado
Para executar, digite a seguinte linha de comando e pressione enter.
O Código da Classe Empregado
Após teclar enter, a capa do livro Naked Objects irá ser exibida e posteriormente, será apresentada a lista de classes contendo apenas a classe Empregado.
O Código da Classe Papel
Exemplo1
O Código da Classe Papel
A classe Papel é criado e disponibilizado da mesma forma que a classe
Empregado.
Vamos nos concentrar, portanto, em algumas diferenças, tal como a
associação da classe Papel com a classe Empregado.
Empregado
nom e
Projeto
nome criaLíder()
Papel
nome 0..*
0..1 0..*
0..1 0..*
0..*
0..*
0..*
Associação
O Código da Classe Papel
Os objetos naked objects implementam a associação como um atributo que
referencia objetos de uma outra classe naked objects.
No caso, a classe Papel terá que manter, no
máximo, uma referência à objetos da classe
Empregado (veja a multiplicidade: 0..1).
Multiplicidade
Empregado
nom e
Projeto
nome criaLíder()
Papel
nome 0..*
0..1 0..*
0..1 0..*
0..*
0..*
0..*
O Código da Classe Papel
Assim, a associação é implementada como um atributo simples, o qual
pode armazenar uma única referência para um objeto da classe Empregado.
Posteriormente, veremos como implementar
multiplicidades maiores que 1.
import org.nakedobjects.object.AbstractNakedObject;
import org.nakedobjects.object.Title;
import org.nakedobjects.object.value.TextString;
public class PapelextendsAbstractNakedObject { private finalTextString nome = newTextString();
privateEmpregadoempregado;
publicTextString getNome() { returnnome;
}
publicEmpregado getEmpregado() { resolve(empregado);
returnempregado;
}
public voidsetEmpregado(Empregado empregado) { this.empregado = empregado;
objectChanged();
}
publicTitle title() { returnnome.title();
} }
Associação
O Código da Classe Papel
Como qualquer atributo de classe, o atributo
empregado não pode ser acessado diretamente.
A única maneira de
acessar um atributo de classe deve ser através dos métodos de acesso denominados set e get.
Assim:
getEmpregado() é
utilizado para consultar um objeto associado da classe Empregado.
setEmpregado() é
utilizado para alterar um objeto associado da classe Empregado.
import org.nakedobjects.object.AbstractNakedObject;
import org.nakedobjects.object.Title;
import org.nakedobjects.object.value.TextString;
public class Papel extendsAbstractNakedObject { private finalTextString nome = newTextString();
privateEmpregadoempregado;
publicTextString getNome() { returnnome;
}
publicEmpregado getEmpregado() { resolve(empregado);
returnempregado;
}
public voidsetEmpregado(Empregado empregado) { this.empregado = empregado;
objectChanged();
}
publicTitle title() { returnnome.title();
} }
O Código da Classe Papel
O framework Naked Objects reconhece que getEmpregado e
setEmpregado são métodos de acesso vinculados ao
atributo empregado porque ele utiliza um mecanismo chamado reflexão.
Assim, quaisquer métodos que possuam prefixos get e set serão considerados como métodos de acesso a algum atributo existente.
import org.nakedobjects.object.AbstractNakedObject;
import org.nakedobjects.object.Title;
import org.nakedobjects.object.value.TextString;
public class Papel extendsAbstractNakedObject { private finalTextString nome = newTextString();
privateEmpregadoempregado;
publicTextString getNome() { returnnome;
}
publicEmpregado getEmpregado() { resolve(empregado);
returnempregado;
}
public voidsetEmpregado(Empregado empregado) { this.empregado = empregado;
objectChanged();
}
publicTitle title() { returnnome.title();
} }
O Código da Classe Papel
O método getEmpregado() é implementado
chamando o método resolve(empregado) do framework.
O método resolve
assegura que um objeto referenciado seja
recuperado corretamente do repositório de objetos.
Após o método resolve, carregar para a memória um objeto da classe
Empregado, referenciado pelo atributo empregado, essa referência é
retornada.
import org.nakedobjects.object.AbstractNakedObject;
import org.nakedobjects.object.Title;
import org.nakedobjects.object.value.TextString;
public class Papel extendsAbstractNakedObject { private finalTextString nome = newTextString();
privateEmpregadoempregado;
publicTextString getNome() { returnnome;
}
publicEmpregado getEmpregado() { resolve(empregado);
returnempregado;
}
public voidsetEmpregado(Empregado empregado) { this.empregado = empregado;
objectChanged();
}
publicTitle title() { returnnome.title();
} }
O Código da Classe Papel
O método setEmpregado () é implementado:
Atualizando o atributo da classe Papel: empregado, com a referência passada pelo parâmetro
empregado.
Depois disso, o método objectChanged() do framework é chamado.
O método
objectChanged() informa aos mecanismos de
visualização e de
persistência que o status do objeto da classe Papel foi alterado.
import org.nakedobjects.object.AbstractNakedObject;
import org.nakedobjects.object.Title;
import org.nakedobjects.object.value.TextString;
public class Papel extendsAbstractNakedObject { private finalTextString nome = newTextString();
privateEmpregadoempregado;
publicTextString getNome() { returnnome;
}
publicEmpregado getEmpregado() { resolve(empregado);
returnempregado;
}
public voidsetEmpregado(Empregadoempregado) { this.empregado= empregado;
objectChanged();
}
publicTitle title() { returnnome.title();
} }
O Código da Classe Papel
Copie o código da classe Papel (ao lado) num
arquivo com o nome Papel.java.
E salve esse arquivo dentro do mesmo diretório onde está Empregado.java e
Run.java foram salvos.
Por exemplo, c:\exemplo1.
import org.nakedobjects.object.AbstractNakedObject;
import org.nakedobjects.object.Title;
import org.nakedobjects.object.value.TextString;
public class Papel extendsAbstractNakedObject { private finalTextString nome = newTextString();
privateEmpregado empregado;
publicTextString getNome() { returnnome;
}
publicEmpregado getEmpregado() { resolve(empregado);
returnempregado;
}
public voidsetEmpregado(Empregado empregado) { this.empregado = empregado;
objectChanged();
}
publicTitle title() { returnnome.title();
} }
O Código da Classe Papel
Modifique a classe Run, para que ela adicione a classe Papel na lista de classes.
import org.nakedobjects.Exploration;
import org.nakedobjects.object.NakedClassList;
public class RunextendsExploration { public static void main(String args[]) {
newRun();
}
public void classSet(NakedClassList classes) { classes.addClass(Empregado.class);
classes.addClass(Papel.class);
} }
Adicionado
O Código da Classe Papel
Copie o arquivo Hammer16.gif e
Hammer32.gif para o diretório c:/Exemplo1/images.
O Código da Classe Papel
Renomeie os arquivos:
Hammer16.gif para Papel16.gif
Hammer32.gif para Papel32.gif.
O Código da Classe Papel
Compile:
Execute:
O Código da Classe Papel
Ao executar, a lista de classes ao lado irá ser exibida.
Agora é possível criar
objetos da classe Papel e associá-los á objetos da classe empregado.
O Código da Classe Papel
Uma coisa que está
incomodando é o nome da classe exibida na lista de ícones: Papels.
Para corrigir esse problema, podemos informar ao framework qual é o plural correto de Papel, que no caso é
Papéis.
Nome da classe + ‘s’
import org.nakedobjects.object.AbstractNakedObject;
import org.nakedobjects.object.Title;
import org.nakedobjects.object.value.TextString;
public class Papel extendsAbstractNakedObject { private finalTextString nome = newTextString();
privateEmpregado empregado;
public staticString pluralName() { return"Papéis";
}
publicTextString getNome() { returnnome;
}
publicEmpregado getEmpregado() { resolve(empregado);
returnempregado;
}
public voidsetEmpregado(Empregado empregado) { this.empregado = empregado;
objectChanged();
}
publicTitle title() { returnnome.title();
} }
O Código da Classe Papel
Para isso, acrescente o seguinte método
pluralName como ilustra código ao lado:
Após compilar e executar novamente, a seguinte lista de ícones será
exibida:
Com o plural correto
O Código da Classe Projeto
Exemplo1
Código da classe Projeto
A classe Projeto é criada e disponibilizada da mesma forma que as classes
Empregado e Papel.
Vamos nos concentrar, portanto, em algumas diferenças, tal como a associação de
multiplicidade 0..* entre as classes Projeto e Papel, e na criação do método
criaLider exibida como uma das opções do menu pop-up.
Empregado
nom e
Projeto
nome criaLíder()
Papel
nome 0..*
0..1 0..*
0..1 0..*
0..*
0..*
0..*
Associação
Multiplicidade
Código da classe Projeto
Para facilitar a visualização, as diretivas de
importação ao lado serão omitidas do código.
importjava.util.Enumeration;
importorg.nakedobjects.object.collection.InternalCollection;
importorg.nakedobjects.object.AbstractNakedObject;
importorg.nakedobjects.object.Title;
importorg.nakedobjects.object.control.About;
importorg.nakedobjects.object.control.ActionAbout;
importorg.nakedobjects.object.value.Case;
importorg.nakedobjects.object.value.TextString;
Código da classe Projeto
A classe Projeto pode conter múltiplos objetos da classe Papel.
Para isso, o tipo
InternalCollection do framework foi utilizado.
O tipo InternalCollection permite guardar uma
coleção de objetos de um tipo indicado.
Neste caso, o tipo indicado é a classe Papel.
A palavra this é também passada como
parâmetro para indicar que a classe Papel está contida nesta instância da classe Projeto.
public classProjeto extendsAbstractNakedObject { private finalTextString nome = newTextString();
private finalInternalCollectionpapeis = newInternalCollection(Papel.class, this);
publicTextString getNome() { returnnome;
}
publicInternalCollection getPapeis() { returnpapeis;
}
publicPapel actionCriaLider() {
Papel liderDeProjeto = (Papel) createInstance(Papel.class);
liderDeProjeto.getNome().setValue("Líder de Projeto");
papeis.add(liderDeProjeto);
returnliderDeProjeto;
}
publicAbout aboutActionCriaLider() { Enumeration e = getPapeis().elements();
while(e.hasMoreElements()) {
Papel role = (Papel) e.nextElement();
if(role.getNome().contains("líder de projeto", Case.INSENSITIVE)) { returnActionAbout.DISABLE;
} }
returnActionAbout.ENABLE;
}
publicTitle title() { returnnome.title();
} }
Código da classe Projeto
Como qualquer atributo de classe, o atributo papeis é private final.
Ele possui também um método get, permitindo que a coleção, com
referências a objetos da classe Papel, possa ser consultada pelo
framework utilizando o mecanismo de reflexão.
public classProjeto extendsAbstractNakedObject { private finalTextString nome = newTextString();
private finalInternalCollectionpapeis= newInternalCollection(Papel.class, this);
publicTextString getNome() { returnnome;
}
publicInternalCollectiongetPapeis() { returnpapeis;
}
publicPapel actionCriaLider() {
Papel liderDeProjeto = (Papel) createInstance(Papel.class);
liderDeProjeto.getNome().setValue("Líder de Projeto");
papeis.add(liderDeProjeto);
returnliderDeProjeto;
}
publicAbout aboutActionCriaLider() { Enumeration e = getPapeis().elements();
while(e.hasMoreElements()) {
Papel role = (Papel) e.nextElement();
if(role.getNome().contains("líder de projeto", Case.INSENSITIVE)) { returnActionAbout.DISABLE;
} }
returnActionAbout.ENABLE;
}
publicTitle title() { returnnome.title();
} } Lista de
objetos da classe
Papel
Código da classe Projeto
Só para recordar, a opção Cria Lider deve estar
acessível pressionando-se o botão direito do mouse sobre o título de um objeto da classe Projeto.
Ao selecionar essa opção, um novo objeto da classe Papel será criado
automaticamente com o campo Nome já
preenchido.
Campo nome preenchido automaticamente
Código da classe Projeto
A opção Cria Lider é implementada através dos métodos de ação (action method).
O framework adiciona uma opção no menu pop- up automaticamente para todo o método que
comece com a palavra actionCriaLider.
Assim, quando o usuário seleciona a opção Cria Lider do menu pop-up, o método actionCriaLider é executado.
public classProjeto extendsAbstractNakedObject { private finalTextString nome = newTextString();
private finalInternalCollection papeis = newInternalCollection(Papel.class, this);
publicTextString getNome() { returnnome;
}
publicInternalCollection getPapeis() { returnpapeis;
}
publicPapel actionCriaLider() {
Papel liderDeProjeto = (Papel) createInstance(Papel.class);
liderDeProjeto.getNome().setValue("Líder de Projeto");
papeis.add(liderDeProjeto);
returnliderDeProjeto;
}
publicAbout aboutActionCriaLider() { Enumeration e = getPapeis().elements();
while(e.hasMoreElements()) {
Papel role = (Papel) e.nextElement();
if(role.getNome().contains("líder de projeto", Case.INSENSITIVE)) { returnActionAbout.DISABLE;
} }
returnActionAbout.ENABLE;
}
publicTitle title() { returnnome.title();
} }
Código da classe Projeto
Quando o método actionCriaLider é
executado, ele cria uma nova instância da classe Papel através do método createInstance.
Instâncias de classes
naked objects não devem ser criadas usando o
operador padrão new da linguagem Java – isso não irá inicializá-las apropriadamente.
public classProjeto extendsAbstractNakedObject { private finalTextString nome = newTextString();
private finalInternalCollection papeis = newInternalCollection(Papel.class, this);
publicTextString getNome() { returnnome;
}
publicInternalCollection getPapeis() { returnpapeis;
}
publicPapel actionCriaLider() {
PapelliderDeProjeto = (Papel) createInstance(Papel.class);
liderDeProjeto.getNome().setValue("Líder de Projeto");
papeis.add(liderDeProjeto);
returnliderDeProjeto;
}
publicAbout aboutActionCriaLider() { Enumeration e = getPapeis().elements();
while(e.hasMoreElements()) {
Papel role = (Papel) e.nextElement();
if(role.getNome().contains("líder de projeto", Case.INSENSITIVE)) { returnActionAbout.DISABLE;
} }
returnActionAbout.ENABLE;
}
publicTitle title() { returnnome.title();
} }
Código da classe Projeto
A referência para a nova instância de classe Papel foi guardada na variável lideDeProjeto.
O nome desse novo objeto é inicialmente obtido usando o método getNome.
O método getNome
retorna o objeto da classe TextString, o qual define o método setValue.
Enfim, o método setValue permite alterar o nome do novo objeto para “Líder de Projeto”.
public classProjeto extendsAbstractNakedObject { private finalTextString nome = newTextString();
private finalInternalCollection papeis = newInternalCollection(Papel.class, this);
publicTextString getNome() { returnnome;
}
publicInternalCollection getPapeis() { returnpapeis;
}
publicPapelactionCriaLider() {
PapelliderDeProjeto= (Papel) createInstance(Papel.class);
liderDeProjeto.getNome().setValue("Líder de Projeto");
papeis.add(liderDeProjeto);
returnliderDeProjeto;
}
publicAbout aboutActionCriaLider() { Enumeration e = getPapeis().elements();
while(e.hasMoreElements()) {
Papel role = (Papel) e.nextElement();
if(role.getNome().contains("líder de projeto", Case.INSENSITIVE)) { returnActionAbout.DISABLE;
} }
returnActionAbout.ENABLE;
}
publicTitle title() { returnnome.title();
} }
Código da classe Projeto
O objeto da classe Papel, recém criado, com o
nome “Líder de Projeto” é adicionado à coleção de papeis associados ao objeto da classe Projeto.
No final, o objeto liderDeProjeto é retornado.
public classProjeto extendsAbstractNakedObject { private finalTextString nome = newTextString();
private finalInternalCollectionpapeis= newInternalCollection(Papel.class, this);
publicTextString getNome() { returnnome;
}
publicInternalCollection getPapeis() { returnpapeis;
}
publicPapelactionCriaLider() {
PapelliderDeProjeto= (Papel) createInstance(Papel.class);
liderDeProjeto.getNome().setValue("Líder de Projeto");
papeis.add(liderDeProjeto);
returnliderDeProjeto;
}
publicAbout aboutActionCriaLider() { Enumeration e = getPapeis().elements();
while(e.hasMoreElements()) {
Papel role = (Papel) e.nextElement();
if(role.getNome().contains("líder de projeto", Case.INSENSITIVE)) { returnActionAbout.DISABLE;
} }
returnActionAbout.ENABLE;
}
publicTitle title() { returnnome.title();
} }