Prévia do material em texto
Projetando classes
Apresentação
Em programação e na orientação a objetos, existem elementos importantes que refletem no
comportamento dos objetos. A classe é um tipo abstrato de dados (TAD), utilizada para abstrair um
conjunto de objetos que sejam representados por características similares.
Para desenvolvermos aplicações utilizando o paradigma de orientação a objetos, podemos definir
que projetar classes é um importante desafio. Segundo Horstmann (2009), classes são coleções de
objetos; portanto, precisamos começar a atividade de programação identificando os objetos e as
classes às quais eles pertencem.
Nesta Unidade de Aprendizagem, você vai estudar os conceitos de coesão e acoplamento, que são
importantes para formar uma estrutura de classes consistente. Além disso, vai estudar o conceito
de pacotes, que são uma forma de organizar as classes do nosso projeto.
Bons estudos.
Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:
Identificar objetos e definir a quais classes eles pertencem.•
Definir coesão e acoplamento.•
Usar pacotes para organizar classes.•
Desafio
Projetar uma classe pode ser um bom desafio, mas, quando seguimos algumas regras e padrões,
nosso trabalho se torna menos árduo.
Analise o seguinte cenário:
Você trabalha como analista/programador em uma fábrica de softwares. Seu gerente solicitou que
você atuasse em um projeto para o novo cliente, criando uma aplicação eficiente para atender às
necessidades de controle de uma oficina mecânica. O principal objetivo é projetar uma aplicação
capaz de:
• automatizar o processo de manutenção de veículos;
• realizar o cadastro de clientes;
• controlar a entrada de veículos no setor de manutenção.
O módulo do sistema desenvolvido para cadastro de cliente precisa atender aos seguintes
requisitos:
• CPF;
• nome do cliente;
• endereço do cliente;
• telefone do cliente;
• e-mail do cliente.
O módulo do sistema desenvolvido para cadastro de veículos precisa atender aos seguintes
requisitos de armazenamento de dados:
• placa do veículo;
• modelo do veículo;
• ano do veículo;
• fabricante do veículo;
• cor do veículo.
Para executar essa tarefa, você precisará fazer o seguinte:
• Criar um pacote chamado "modelo" – no qual serão armazenadas as classes “modelo”.
• Criar um pacote chamado "controle" – no qual será armazenado o método main.
A classe deve ser nomeada como “Principal” e será utilizada para instanciar as classes modelo e
iniciar o programa. Seu gerente sugeriu o uso de uma linguagem de programação orientada a
objetos (POO). Também sugeriu o uso da plataforma Java, utilizando o IDE NetBeans. Você precisa
encaminhar o arquivo em formato .zip até o final do dia para oficializar a execução do projeto,
seguindo todas as instruções.
Infográfico
Os conceitos básicos sobre classes devem ser conhecidos pelos desenvolvedores que desejam
desenvolver com base em linguagens orientadas a objetos. Entre os princípios que evitam
problemas no uso de classes estão a coesão e o acoplamento. Ambos estão relacionados com os
módulos de um sistema e associados aos conceitos de classes e à sua aplicação à POO.
Enquanto a coesão é o grau de responsabilidade única bem definida para uma classe, o
acoplamento é o grau com o qual um módulo define sua dependência em relação aos demais, para
garantir sua funcionalidade.
Veja, no Infográfico, uma descrição sobre escolha de classes e os princípios de coesão e
acoplamento. Em seguida, você será desafiado com um quiz sobre as relações entre essas classes.
A imagem a seguir possui audiodescrição.
Aponte a câmera para o
código e acesse o link do
conteúdo ou clique no
código para acessar.
https://statics-marketplace.plataforma.grupoa.education/sagah/c1cff10f-8312-47ae-966a-1b8a47689529/223ed332-e425-4ca3-9ada-6611dcca5dec.png
Um projeto utilizando o modelo de orientação a objetos é estruturado em pacotes que armazenam
classes semelhantes. Portanto, compreender os conceitos de classe é fundamental. Você está
pronto? Para concluir o infográfico, você vai ser desafiado por meio de um teste.
A imagem a seguir possui audiodescrição.
Conteúdo interativo disponível na plataforma de ensino!
Conteúdo do Livro
Construir classes adequadas é fundamental no desenvolvimento de software orientado a objetos.
Uma classe bem projetada encapsula um conjunto de atributos e métodos que representam uma
entidade ou um conceito no domínio do problema. Uma boa classe deve ter uma responsabilidade
única e bem definida, seguindo o princípio da coesão. Isso significa que cada classe deve ser focada
em realizar uma tarefa específica, facilitando a compreensão, a manutenção e a reutilização do
código. Ao instanciar objetos de uma classe, estamos criando instâncias únicas e independentes
dessa classe. Além disso, escolher, nomear e estruturar bem as classes de uma aplicação, assim
como organizá-las em pacotes, é de extrema importância. Portanto, o domínio desses conceitos se
torna vital para o desenvolvimento de softwares.
Nesse capítulo do livro, você vai aprender como utilizar os conceitos de classes, métodos, atributos
e instanciar objetos na prática, utilizando a linguagem Java. Além disso, vai entender como é
importante ter alta coesão e baixo acoplamento em seus projetos, para que eles promovam uma
boa qualidade de software e sejam fáceis de serem mantidos. Por fim, você vai aprender mais sobre
pacotes e como utilizá-los para organizar seus projetos em Java.
Boa leitura.
Os elementos gráficos deste capítulo possuem audiodescrição. Para acessar o recurso,
clique aqui
https://creator-files.plataforma.grupoa.education/undefined/9108_Audiodescricao_Capitulo-2026-04-11T11:02:53-03:00.pdf
PROGRAMAÇÃO
ORIENTADA A
OBJETOS COM
BANCO DE DADOS
OBJETIVOS DE APRENDIZAGEM
> Identificar objetos e definir a quais classes eles pertencem.
> Definir coesão e acoplamento.
> Usar pacotes para organizar classes.
Introdução
Projetar boas classes é um aspecto fundamental no desenvolvimento de
software. Uma classe bem projetada é coesa, ou seja, tem uma única respon-
sabilidade claramente definida. A coesão garante que a classe seja focada
e específica, facilitando sua compreensão, manutenção e reutilização. Além
disso, ao instanciar objetos a partir de classes, podemos criar instâncias úni-
cas e independentes, permitindo a manipulação de dados e a execução de
funcionalidades específicas. Por meio dos atributos, podemos armazenar e
representar o estado dos objetos, enquanto os métodos nos permitem definir
o comportamento desses objetos.
Nesse âmbito, é importante entender a relação entre as classes e como
elas se comunicam entre si. O acoplamento entre classes deve ser gerenciado
adequadamente, evitando dependências excessivas para garantir um sistema
mais flexível e mais fácil de manter e modificar. Por meio dos pacotes, podemos
organizar e agrupar classes relacionadas, controlando a visibilidade e evitando
conflitos de nomes.
Neste capítulo, você vai aprender o que são objetos, como instanciá-los e
como acessar seus atributos e métodos. Além disso, vai aprender a diferenciar
princípios de coesão e acoplamento de classes. Por fim, vai entender como
organizar as classes de um projeto ao fazer uso de pacotes. A compreensão
Projetando classes
Nicolli Souza Rios Alves
desses conceitos é essencial para desenvolver um código modular, legível e de
fácil manutenção, resultando em um software de alta qualidade e eficiência.
Objetos
Na programação orientada a objetos, as classes desempenham um papel fun-
damental. Uma classe é uma estrutura que define um conjunto de propriedades
(atributos) e comportamentos (métodos) que os objetos desse tipo podem
ter (HORSTMANN, 2008). Ela funciona como um modelo ou uma planta para a
criação de objetos. Por meio da classe, podemos definir as características e
funcionalidades que os objetos terão.
Uma vez que a classe tenha sido definida, podemos criar instânciasdela,
que são chamadas de objetos. Os objetos são as entidades reais que existem
em tempo de execução, e podem interagir uns com os outros (HORSTMANN,
2008). Eles possuem um estado (representado pelos valores de seus atributos)
e podem realizar ações (invocando seus métodos).
Os objetos são criados com base na classe e herdam suas propriedades e
comportamentos. Cada objeto pode ter valores diferentes para seus atributos,
o que permite representar diferentes instâncias do mesmo tipo. Se tivermos,
por exemplo, uma classe chamada Carro, podemos criar vários objetos desse
tipo, como Carro1 e Carro2, cada qual com suas próprias características
específicas, como cor, modelo, ano, etc.
A programação orientada a objetos permite modelar o mundo real de
forma mais precisa, organizando o código em entidades independentes e
reutilizáveis. As classes fornecem uma abstração para representar conceitos
e entidades, enquanto os objetos permitem a criação de instâncias únicas
com suas próprias características e comportamentos (SCHILDT, 2015). Essa
abordagem promove modularidade, encapsulamento, reutilização de código
e simplificação do desenvolvimento de software.
Em Java, para instanciar um objeto, vamos precisar seguir os seguintes
passos (FINEGAN; LIGUORI, 2018):
Passo 1
Essa etapa diz respeito à definição da classe. Primeiramente, precisamos ter
uma classe definida. Vamos supor que temos uma classe chamada Carro,
com alguns atributos e métodos:
Projetando classes2
public class Carro {
private String marca;
private String modelo;
public Carro(String marca, String modelo) {
this.marca = marca;
this.modelo = modelo;
}
public void setMarca(String marca) {
this.marca = marca;
}
public String getMarca() {
return marca;
}
public void setModelo(String modelo) {
this.modelo = modelo;
}
public String getModelo() {
return modelo;
}
public void acelerar() {
// Implementação do método acelerar
}
public void frear() {
// Implementação do método frear
}
}
Projetando classes 3
Passo 2
Esta etapa diz respeito à criação de uma instância. Assim, podemos criar
uma instância (objeto) dessa classe utilizando o operador new, seguido do
nome da classe e dos parâmetros necessários para o construtor, se houver.
Por exemplo:
Carro meuCarro = new Carro(“Toyota”, “Corolla”);
Nesse exemplo, criamos um objeto chamado meuCarro, que é uma instân-
cia da classe Carro. O construtor da classe Carro recebe dois parâmetros
(marca e modelo) e os valores passados durante a criação do objeto são
"Toyota" e "Corolla".
Agora, podemos usar o objeto meuCarro para acessar seus atributos e
métodos. Por exemplo:
System.out.println(meuCarro.getMarca()); // Saída: Toyota
System.out.println(meuCarro.getModelo()); // Saída: Corolla
meuCarro.acelerar(); // Chamada do método acelerar
meuCarro.frear(); // Chamada do método frear
Dessa forma, podemos instanciar objetos em Java, criar várias instâncias
da mesma classe com diferentes valores para seus atributos e utilizar os
métodos definidos na classe para interagir com esses objetos.
Em Java, para acessar métodos e atributos de uma classe, precisamos ter
uma instância (objeto) da classe e usar o operador ponto (.) para referenciar
o objeto e acessar seus membros. Vamos entender como fazer isso (ARNOLD;
GOSLING; HOLMES, 2005).
Passo 1
A primeira etapa diz respeito a acessar atributos. Se o atributo for público
(declaração com o modificador public), podemos acessá-lo diretamente
usando o operador ponto. Por exemplo:
Carro meuCarro = new Carro();
meuCarro.marca = "Toyota";
meuCarro.modelo = "Corolla";
Projetando classes4
Se o atributo for privado (declaração com o modificador private), é
recomendado o uso de métodos de acesso (getters e setters) para ler e
alterar o valor do atributo. Por exemplo:
public class Carro {
private String marca;
private String modelo;
// getters
public String getMarca() {
return marca;
}
public String getModelo() {
return modelo;
}
// setters
public void setMarca(String marca) {
this.marca = marca;
}
public void setModelo(String modelo) {
this.modelo = modelo;
}
}
E então podemos acessar os atributos usando os métodos de acesso:
Carro meuCarro = new Carro();
meuCarro.setMarca("Toyota");
meuCarro.setModelo("Corolla");
System.out.println(meuCarro.getMarca()); // Saída: Toyota
System.out.println(meuCarro.getModelo()); // Saída: Corolla
Projetando classes 5
Passo 2
A segunda etapa diz respeito a acessar métodos. Para acessar um método
de uma classe, também precisamos de uma instância (objeto) da classe. Use
o operador ponto (.) para chamar o método. Por exemplo:
Carro meuCarro = new Carro();
meuCarro.acelerar();
meuCarro.frear();
Se o método tiver parâmetros, precisamos passar os valores corretos
durante a chamada. Por exemplo:
public class Carro {
// ...
public void ligarMotor(int cilindradas) {
// Implementação do método ligarMotor
}
}
E então podemos chamar o método e passar o valor do parâmetro da
seguinte forma, por exemplo:
Carro meuCarro = new Carro();
meuCarro.ligarMotor(2000);
Essas são as formas básicas de acessar métodos e atributos de uma
classe em Java. Lembre-se de que os modificadores de acesso (como vimos
public e private) afetam a forma como os membros da classe podem ser
acessados de outras partes do código.
Até aqui, exploramos alguns conceitos fundamentais da programação
orientada a objetos. Aprendemos o que são objetos, como instanciá-los e como
acessar seus atributos e métodos. Vimos como criar classes que definem a
estrutura e o comportamento dos objetos. Na próxima seção, vamos dar um
passo adiante e mergulhar nos princípios de coesão e acoplamento de classes.
Projetando classes6
Ao nomear classes, atributos, métodos e objetos em programação
orientada a objetos, é importante seguir algumas boas práticas para
garantir um código claro, legível e fácil de entender. Eis algumas diretrizes para
nomeação (ARNOLD; GOSLING; HOLMES, 2005):
� Nomeclatura descritiva: escolha nomes que sejam descritivos e reflitam cla-
ramente a finalidade e o papel do elemento no código. Evite nomes genéricos
ou abreviações confusas. Prefira nomes completos e claros que facilitem a
compreensão.
� Atente-se à capitalização consistente: siga uma convenção de capitalização
consistente ao nomear suas classes, atributos, métodos e objetos. Utilize o for-
mato camelCase para nomes compostos, começando com uma letra maiúscula
para cada palavra subsequente (exemplo: minhaClasse, meuAtributo).
� Evite abreviações excessivas: embora algumas abreviações possam ser acei-
táveis e amplamente reconhecidas na comunidade de desenvolvimento,
evite abreviações excessivas que possam tornar o código difícil de entender.
Priorize nomes completos e claros.
� Use substantivos e verbos: normalmente, as classes e os objetos são nomea-
dos com substantivos, representando entidades ou conceitos no domínio do
problema. Os atributos representam características ou propriedades dessas
entidades e também devem ser nomeados com substantivos descritivos. Já
os métodos, que representam ações ou comportamentos, devem ser nome-
ados com verbos ou expressões verbais que indiquem o que o método faz.
� Tenha consistência em convenções de nomes: siga as convenções de nomes
estabelecidas pela linguagem de programação que você está utilizando. Em
Java, por exemplo, a convenção geralmente é utilizar camelCase para nomes
de atributos e métodos, e PascalCase para nomes de classes.
� Evite nomes ambíguos: evite usar nomes que possam causar confusão ou
ambiguidade. Escolha nomes que sejam distintos o suficiente para evitar
confusões durante a leitura e a manutenção do código.
� Evite nomes muito longos: embora seja importante fornecer nomes descritivos,
evite nomes excessivamentelongos que dificultem a leitura e a digitação.
Procure um equilíbrio entre a clareza e a concisão.
� Evite prefixos ou sufixos desnecessários: a menos que exigido por conven-
ções específicas ou para evitar conflitos de nomes, evite o uso de prefixos
ou sufixos desnecessários em seus nomes. Os nomes devem ser claros o
suficiente por si só.
Lembrando que essas são diretrizes gerais e podem variar dependendo das
práticas específicas da linguagem de programação ou do framework utilizado.
Projetando classes 7
Princípios de coesão e acoplamento
A manutenibilidade de software é a capacidade de um sistema ser facilmente
mantido e modificado ao longo do tempo. Um software bem mantido é aquele
que permite correções de erros, melhorias e inclusão de novas funcionalidades
de forma eficiente, sem introduzir problemas ou causar impactos indesejados
em outras partes do sistema (SCHACH, 2010). Agora vamos compreender dois
critérios úteis para analisar a qualidade da interface pública de uma classe:
a coesão e o acoplamento, que são conceitos fundamentais na programação
orientada a objetos e que desempenham um papel importante na manute-
nibilidade do software.
Coesão
Em programação orientada a objetos, a coesão refere-se à organização interna
de uma classe, ou seja, o quão bem seus membros estão relacionados e traba-
lham juntos para cumprir uma única responsabilidade. Uma classe coesa tem
uma responsabilidade clara e específica, e todos os seus atributos e métodos
estão diretamente relacionados a essa responsabilidade. Isso resulta em
um código mais organizado, legível e de fácil compreensão (SCHACH, 2010).
A coesão desempenha um papel fundamental na manutenibilidade do
software, pois uma classe coesa é mais fácil de ser mantida e modificada
ao longo do tempo. Quando os membros de uma classe estão intimamente
relacionados e trabalham em conjunto para cumprir uma única responsabili-
dade, as alterações numa parte específica da classe têm um impacto mínimo
nas outras partes do sistema (SCHACH, 2010). Isso reduz o risco de efeitos
colaterais indesejados e facilita a identificação e correção de erros.
Além disso, a coesão também facilita a reutilização de código. Uma classe
coesa contém um conjunto claro de funcionalidades relacionadas, o que torna
mais fácil identificar e extrair partes do código que possam ser reutilizadas
em outros contextos. Isso promove a modularidade e reduz a duplicação de
código, tornando o sistema mais flexível e manutenível.
Ao desenvolver um software, é importante buscar a coesão em todas
as classes do sistema. Isso envolve identificar a responsabilidade única de
cada classe e garantir que seus membros estejam diretamente relacionados
a essa responsabilidade (SCHACH, 2010). É uma prática recomendada manter
as classes pequenas, focadas e coesas, permitindo que sejam facilmente
compreendidas e modificadas quando necessário.
Projetando classes8
Vamos considerar um exemplo de uma classe chamada Calculadora, que
possui métodos para realizar operações matemáticas simples. Essa classe
apresenta a seguinte implementação:
public class Calculadora {
public int somar(int a, int b) {
return a + b;
}
public int subtrair(int a, int b) {
return a - b;
}
public int multiplicar(int a, int b) {
return a * b;
}
public double dividir(int a, int b) {
return (double) a / b;
}
}
Nesse exemplo, a classe Calculadora está coesa, já que todos os méto-
dos estão relacionados à sua responsabilidade única de realizar operações
matemáticas. Cada método representa uma operação específica, como soma,
subtração, multiplicação e divisão.
A classe Calculadora é coesa por vários motivos:
� Responsabilidade única: a classe tem uma única responsabilidade, que
é realizar operações matemáticas. Todos os métodos da classe estão
diretamente relacionados a essa responsabilidade.
� Funcionalidades relacionadas: todos os métodos realizam operações
matemáticas e estão relacionados ao propósito da classe. Eles traba-
lham em conjunto para cumprir a responsabilidade da classe.
� Legibilidade: a classe é fácil de entender e de ler. Os nomes dos mé-
todos são claros e descritivos, indicando a operação matemática que
eles realizam.
Projetando classes 9
� Manutenibilidade: essa classe coesa facilita a manutenção do código.
Se houver a necessidade de adicionar uma nova operação matemática,
basta criar um novo método relacionado à responsabilidade da classe.
Não é necessário alterar outros métodos existentes ou adicionar fun-
cionalidades não relacionadas.
Vamos ver agora uma versão modificada da classe Calculadora, dessa
vez de tal modo que ela não está coesa:
public class Calculadora {
public int somar(int a, int b) {
//implementação método somar
}
public int subtrair(int a, int b) {
// implementação método subtrair
}
public int multiplicar(int a, int b) {
// implementação método multiplicar
}
public double dividir(int a, int b) {
// implementação método dividir
}
public void escreverLog(String mensagem) {
// Método para escrever uma mensagem de log no sistema
System.out.println("Log: " + mensagem);
}
public boolean validarEntradas(int a, int b) {
// Método para validar as entradas antes de realizar
as operações
return a >= 0 && b >= 0;
}
Projetando classes10
public int calcularPotencia(int base, int expoente) {
// Método para calcular a potência de um número
return (int) Math.pow(base, expoente);
}
}
Nesta versão modificada, a classe Calculadora não está coesa, uma vez
que possui métodos que vão além de sua responsabilidade principal, que
é realizar operações matemáticas. Podemos perceber essa falta de coesão
nos seguinte métodos:
� escreverLog: este método não está diretamente relacionado à
responsabilidade da classe de realizar operações matemáticas. Ele
lida com a funcionalidade de escrever mensagens de log, que é uma
preocupação à parte.
� validarEntradas: embora seja importante validar as entradas, esse
método não faz parte da responsabilidade principal da classe. A vali-
dação das entradas pode ser tratada em um contexto diferente, como
em um serviço de entrada de dados.
� calcularPotencia: este método calcula a potência de um número,
que é uma operação matemática, mas não é uma operação básica como
soma, subtração, multiplicação e divisão. A inclusão desse método à
classe Calculadora introduz uma funcionalidade adicional e não
relacionada à responsabilidade principal da classe.
Essa falta de coesão torna a classe menos legível e mais difícil de entender
e de manter. Também pode levar a uma maior dependência de outras partes do
sistema, pois a classe lida com funcionalidades não relacionadas. Além disso,
qualquer alteração em determinada parte da classe pode afetar inadvertida-
mente outras partes, aumentando a complexidade e a fragilidade do código.
É importante buscar a coesão em todas as classes do sistema, garantindo
que cada uma delas tenha uma responsabilidade clara e bem definida (SCHACH,
2010). Dessa forma, é possível obter um código mais organizado, legível e de
fácil manutenção.
Projetando classes 11
Acoplamento
O acoplamento refere-se ao quanto as classes estão interconectadas e de-
pendentes umas das outras. Ele descreve o nível de conhecimento que uma
classe tem sobre outras e como elas se comunicam entre si (SCHACH, 2010).
Um alto acoplamento indica uma forte dependência entre as classes, enquanto
um baixo acoplamento indica uma independência maior entre elas, como
ilustra a Figura 1.
Figura 1. Níveis de acoplamento entre as classes: (a) alto; (b) baixo.
A B
Quando duas classes estão acopladas, uma alteração em uma delas pode
ter um impacto direto na outra. Isso ocorre porque as classes acopladas estão
intimamente ligadas e compartilham informações e dependências. Quando
uma classe é modificada, é necessário verificar se isso afeta outras classes
que dela dependem,o que pode levar a alterações em várias partes do sistema.
Desse modo, um alto acoplamento pode levar a vários problemas (SCHACH,
2010). Primeiramente, isso torna o código mais difícil de ser compreendido,
mantido e reutilizado, já que as alterações em uma classe podem ter efeitos
colaterais em outras partes do sistema, exigindo um esforço adicional para
garantir que todas as dependências sejam atualizadas corretamente. Além
disso, um alto acoplamento dificulta os testes unitários, pois fica mais difícil
isolar as classes para testá-las de forma independente.
Por outro lado, um baixo acoplamento é desejável, pois indica uma maior
independência entre as classes. Cada classe tem uma responsabilidade bem
definida e se comunica com outras classes apenas por meio de interfaces
específicas. Isso permite que as classes sejam modificadas de forma isolada,
Projetando classes12
sem afetar o funcionamento de outras partes do sistema. Além disso, um
baixo acoplamento facilita a reutilização de código, pois as classes podem
ser extraídas e utilizadas em diferentes contextos sem arrastar consigo
muitas dependências.
Para reduzir o acoplamento entre classes em um sistema, podemos
adotar as seguintes práticas (ARNOLD; GOSLING; HOLMES, 2005;
SCHACH, 2010):
� Princípio da responsabilidade única: devemos garantir que cada classe tenha
uma única responsabilidade bem definida. Evitemos que uma classe realize
múltiplas tarefas, pois isso aumenta o acoplamento com outras classes.
Dividimos as responsabilidades em classes separadas para manter um aco-
plamento baixo.
� Injeção de dependência: em vez de criar dependências diretamente dentro
de uma classe, devemos injetá-las por meio de construtores, métodos ou
propriedades. Isso permite que as dependências sejam fornecidas externa-
mente, facilitando a substituição e o teste de componentes independentes.
A utilização de frameworks de injeção de dependência pode ajudar nesse
processo.
� Interfaces e abstrações: devemos utilizar interfaces e abstrações para inte-
ragir com outras classes. Isso permite que escrevamos código dependente
de interfaces em vez de implementações concretas. Dessa forma, é possível
substituir facilmente as implementações subjacentes sem afetar o código
que as utiliza, reduzindo o acoplamento.
� Padrões de projeto (design patterns): devemos utilizar padrões de projeto,
como o Observer ou o Strategy, para reduzir o acoplamento entre classes.
Esses padrões promovem a comunicação indireta e flexível entre objetos,
diminuindo o acoplamento direto.
� Camadas e módulos: devemos organizar seu código em camadas ou módulos
bem definidos, pois isso ajuda a limitar a comunicação e a dependência entre
classes pertencentes a diferentes camadas ou módulos. Nesse caso, o reco-
mendado é utilizar interfaces ou serviços para estabelecer a comunicação
entre essas camadas, reduzindo o acoplamento direto.
� Refatoração: devemos regularmente revisar e refatorar nosso código para
reduzir o acoplamento. Para isso, é preciso identificar as dependências des-
necessárias e maneiras de reduzi-las. Isso pode envolver a reorganização
do código, a extração de interfaces, a criação de classes intermediárias ou
a aplicação de padrões de projeto.
Projetando classes 13
Vamos analisar agora um exemplo de uma classe em Java com alto
acoplamento:
public class Pedido {
private Cliente cliente;
private Estoque estoque;
private NotificacaoService notificacaoService;
public Pedido(Cliente cliente) {
this.cliente = cliente;
this.estoque = new Estoque();
this.notificacaoService = new NotificacaoService();
}
public void adicionarItem(ItemPedido item) {
if (estoque.verificarDisponibilidade(item.getPro-
duto(), item.getQuantidade())) {
estoque.atualizarEstoque(item.getProduto(), item.
getQuantidade());
notificacaoService.enviarMensagem(cliente.getE-
mail(), "Item adicionado ao pedido.");
} else {
notificacaoService.enviarMensagem(cliente.getE-
mail(), "Item não disponível em estoque.");
}
}
}
Neste exemplo, a classe Pedido está acoplada de forma significativa
a outras classes, como Cliente, Estoque e NotificacaoService. Isso
pode ser considerado um alto acoplamento, já que a classe Pedido tem
dependências diretas dessas classes e as utiliza internamente para realizar
suas operações.
O acoplamento ocorre porque a classe Pedido cria instâncias das classes
Estoque e NotificacaoService dentro do construtor e faz chamadas
diretas aos métodos dessas classes no método adicionarItem(). Essa
dependência direta torna a classe Pedido fortemente acoplada a essas
classes específicas.
Projetando classes14
Esse alto acoplamento pode ser problemático, porque qualquer modifica-
ção nas classes Estoque ou NotificacaoService pode exigir alterações
na classe Pedido. Além disso, a classe Pedido não pode ser facilmente
substituída por outra implementação que utiliza classes diferentes para
estoque ou notificações, o que reduz a flexibilidade e reutilização do código.
Para reduzir o acoplamento nesse exemplo, seria possível aplicar princípios
como a injeção de dependência (dependency injection) (HORSTMANN, 2008).
Ao invés de instanciar diretamente as dependências dentro da classe Pedido,
as instâncias das classes Estoque e NotificacaoService poderiam ser
passadas como parâmetros no construtor do Pedido, permitindo uma maior
flexibilidade e desacoplamento entre as classes.
Vamos analisar agora um exemplo de uma classe em Java com baixo
acoplamento:
public class CarrinhoCompras {
private List itens;
private ServicoEstoque servicoEstoque;
private ServicoNotificacao servicoNotificacao;
public CarrinhoCompras(ServicoEstoque servicoEstoque,
ServicoNotificacao servicoNotificacao) {
this.itens = new ArrayList();
this.servicoEstoque = servicoEstoque;
this.servicoNotificacao = servicoNotificacao;
}
public void adicionarItem(ItemProduto item) {
if (servicoEstoque.verificarDisponibilidade(item.
getProduto(), item.getQuantidade())) {
servicoEstoque.atualizarEstoque(item.getProduto(),
item.getQuantidade());
servicoNotificacao.enviarMensagem("Item adicionado
ao carrinho.");
itens.add(item);
Projetando classes 15
} else {
servicoNotificacao.enviarMensagem("Item não dis-
ponível em estoque.");
}
}
// Restante da implementação da classe
}
Neste exemplo, a classe CarrinhoCompras tem baixo acoplamento, pois
suas dependências (ServicoEstoque e ServicoNotificacao) são passadas
para ela através do construtor. Isso permite que a classe seja independente
das implementações específicas dessas dependências, o que aumenta sua
flexibilidade e reutilização.
A classe CarrinhoCompras não instancia diretamente as dependências,
mas recebe-as como argumentos no construtor (dependency injection). Dessa
forma, a classe não está fortemente acoplada às implementações concretas
dos serviços de estoque e notificação.
Essa abordagem de baixo acoplamento facilita a substituição das imple-
mentações de ServicoEstoque e ServicoNotificacao por outras classes
que implementam as mesmas interfaces ou superclasses. Além disso, ela
permite que a classe CarrinhoCompras seja facilmente testada de forma
isolada, usando implementações de teste para os serviços de estoque e
notificação.
Agora que entendemos a importância da coesão e do baixo acoplamento
no design de classes, estamos prontos para avançar para o próximo tópico:
pacotes.
Pacotes
Um dos princípios fundamentais na programação orientada a objetos é a
organização e estruturação do código de forma eficiente e modular. Uma das
maneiras de alcançar isso é pelo uso de pacotes. Os pacotes são unidades
lógicas de agrupamento de classes e outros elementos relacionados, pro-
porcionando uma maneira de organizar e gerenciar o código em projetos de
software (ARNOLD; GOSLING; HOLMES, 2005).
Projetando classes16
Um pacote é umaforma de agrupar classes e outros elementos relacio-
nados numa única unidade. Ele proporciona uma maneira de organizar e
estruturar o código, facilitando a reutilização, manutenção e colaboração
entre desenvolvedores (SCHACH, 2010).
Os pacotes ajudam a evitar conflitos de nomes, pois permitem a definição
de namespaces (mecanismo utilizado em programação para evitar conflitos
de nomes entre diferentes entidades, como classes, funções, variáveis, etc.)
separados para diferentes partes do código. Isso significa que podemos ter
classes com o mesmo nome em pacotes diferentes sem causar conflitos.
A Figura 2 mostra a organização de um projeto-exemplo com nome MARATONA-
-JAVA-DEVDOJO, que tem a disposição dos arquivos-fonte da biblioteca, que
corresponde à disposição de seus pacotes.
Figura 2. Organização de pastas e pacotes de um projeto Java no Visual Studio Code.
Além disso, os pacotes fornecem um mecanismo para controlar a visi-
bilidade dos elementos dentro deles. Podemos especificar quais classes
e membros são visíveis para outras partes do código, definindo-os como
públicos, protegidos, privados ou com acesso de pacote. Isso ajuda a garantir
o encapsulamento e a modularidade do código.
Os pacotes também oferecem vantagens em termos de reutilização de
código. Ao agrupar classes relacionadas em um pacote, fica mais fácil iden-
tificar e utilizar as classes necessárias em diferentes partes do projeto (PE-
CINOVSKÝ, 2013). Além disso, pacotes bem definidos e organizados podem
ser empacotados em bibliotecas ou módulos independentes, que podem ser
compartilhados e reutilizados em outros projetos.
Projetando classes 17
Em linguagens como Java, os pacotes são representados por diretórios
no sistema de arquivos. Cada pacote tem um nome único que segue uma
convenção de nomenclatura em formato de nome de domínio reverso, como
com.example.mypackage. Essa estrutura de diretórios reflete a hierarquia
dos pacotes e facilita a localização e organização das classes correspondentes.
Para utilizar pacotes e organizar as classes de um projeto, você pode seguir
os seguintes passos (PECINOVSKÝ, 2013; SCHILDT, 2015):
1. Definir uma estrutura de diretórios: começamos criando uma estrutura
de diretórios correspondente aos pacotes em seu sistema de arquivos.
Se, por exemplo estivermos usando Java e houver um pacote chamado
com.example.projeto, criamos um diretório com, dentro do qual
haverá outro diretório chamado exemplo e, dentro deste, um diretório
chamado projeto.
2. Nomear pacotes: devemos escolher nomes significativos e descritivos
para nossos pacotes, seguindo uma convenção de nomenclatura em
formato de nome de domínio reverso. Se, por exemplo, nosso projeto
é um aplicativo de vendas, podemos ter pacotes como com.example.
projeto.clientes, com.example.projeto.produtos, etc.
3. Organizar as classes em pacotes: devemos mover nossas classes para
os diretórios correspondentes aos pacotes que definimos. É preciso
garantir que cada arquivo fonte esteja localizado no diretório correto,
de acordo com sua declaração de pacote no início do arquivo. Se, por
exemplo, a classe pertencer ao pacote com.example.projeto.clien-
tes, movemos o arquivo para o diretório com/example/projeto/
clientes.
4. Gerenciar as dependências entre pacotes: é preciso garantir que os pa-
cotes tenham dependências claras e limitadas entre si. Isso significa que
as classes em um pacote devem depender apenas de classes em outros
pacotes quando necessário, evitando dependências desnecessárias.
Devemos manter a coesão em mente, agrupando classes relacionadas
em pacotes para facilitar o entendimento e a manutenção do código.
5. Definir a visibilidade dos membros: dentro de cada pacote, definimos
a visibilidade dos membros (classes, métodos, atributos) de acordo
com as necessidades. Use os modificadores de acesso adequados,
como public, protected ou private, para controlar a visibilidade
e garantir o encapsulamento adequado.
Projetando classes18
6. Importar classes de outros pacotes: ao utilizar uma classe de outro
pacote no nosso código, vamos precisar importá-la usando a decla-
ração import. Isso permite que a gente consiga acessar as classes
de outros pacotes sem precisar digitar o nome completo do pacote
sempre que usá-las.
Ao organizar nossas classes em pacotes, precisamos seguir uma estrutura
lógica e coerente, agrupando classes relacionadas em pacotes adequados.
Essa abordagem facilitará a navegação, a manutenção e a reutilização do
código em nosso projeto, tornando-o mais modular e coeso. Além disso, o uso
correto de pacotes ajuda a evitar conflitos de nomes e melhora a legibilidade
e a escalabilidade do projeto.
Nesse capítulo, pudemos explorar diversos conceitos fundamentais da
programação orientada a objetos. Aprendemos a instanciar objetos a par-
tir de uma classe, utilizando o operador new para criar instâncias únicas e
independentes. Com isso, pudemos acessar os métodos e atributos desses
objetos, permitindo a manipulação dos dados e a execução das funcionalidades
definidas na classe. Além disso, compreendemos a importância da coesão
e do acoplamento, de forma a aumentar a legibilidade, a flexibilidade e a
manutenibilidade do código. Por fim, conhecemos os pacotes, que permitem
agrupar classes relacionadas, controlar a visibilidade e evitar conflitos de
nomes. Conhecer esse conceito é fundamental para manter um código es-
truturado, facilitar a colaboração entre desenvolvedores e promover o reuso.
Todos esses conceitos estudados são essenciais para desenvolver projetos
de software mais robustos, eficientes e de fácil manutenção.
Referências
ARNOLD, K..; GOSLING, J.; HOLMES, D. The Java programming language. 4th ed. Boston:
Addison Wesley Professional, 2005.
FINEGAN, E.; LIGUORI, R. OCA Java SE 8: guia de estudos para o exame 1Z0-808. Porto
Alegre: Bookman, 2018.
HORSTMANN, C. Conceitos de computação com Java: compatível com Java 5 & 6. 5. ed.
Porto Alegre: Bookman, 2008.
PECINOVSKÝ, R. OOP: learn object oriented thinking & programming. Živonín: Tomáš
Bruckner, 2013.
SCHACH, S. R. Engenharia de software: os paradigmas clássico e orientado a objetos.
7. ed. Porto Alegre: AMGH, 2010.
SCHILDT, H. Java para iniciantes: crie, compile e execute programas Java rapidamente.
6. ed. Porto Alegre: Bookman, 2015.
Projetando classes 19
Leituras recomendadas
BLOCH, J. Effective Java. 3rd ed. Boston: Addison-Wesley Professional, 2018.
ESTEVÃO. Como criar minha primeira classe em Java. [S. l.]: DevMedia, 2019. Disponivel
em: https://www.devmedia.com.br/como-criar-minha-primeira-classe-em-java/38940.
Acesso em: 14 jun. 2023.
REGES, S.; STEPP, M. Building Java programs: a back to basics approach. 3rd ed. New
York: Pearson, 2014.
RONALDO. Aprender Java: boas práticas para um bom projeto de software. [S. l.]:
DevMedia, 2013. Disponível em: https://www.devmedia.com.br/aprender-java-boas-
-praticas-para-um-bom-projeto-de-software/29713. Acesso em: 14 jun. 2023.
Os links para sites da web fornecidos neste capítulo foram todos
testados, e seu funcionamento foi comprovado no momento da
publicação do material. No entanto, a rede é extremamente dinâmica; suas
páginas estão constantemente mudando de local e conteúdo. Assim, os edito-
res declaram não ter qualquer responsabilidade sobre qualidade, precisão ou
integralidade das informações referidas em tais links.
Projetando classes20
Dica do Professor
Para começarmos um projeto, o primeiro passo é identificar quais objetos devem ser criados para a
aplicação. As classes servem para classificar os objetos, como o próprio nome diz.
Existem diversos recursos que precisam ser utilizados dentro de um programa, como os métodos,
que definem quais as ações serão executadas. Da mesma forma, as boas práticas de nomeação de
classes devem ser adotadas para padronizar a estrutura de um programa.
Nesta Dica do Professor, você vai ver aspectos importantes da POO, selecionando práticas e
requisitos que devem ser analisadosna fase de planejamento de um programa.
Os elementos gráficos deste capítulo possuem audiodescrição. Para acessar o recurso,
clique aqui
Aponte a câmera para o código e acesse o link do conteúdo ou clique no código para acessar.
https://creator-files.plataforma.grupoa.education/undefined/9108_Audiodescricao_Dica_Professor-2026-04-11T11:22:30-03:00.pdf
https://fast.player.liquidplatform.com/pApiv2/embed/cee29914fad5b594d8f5918df1e801fd/a15f37c0a4bfd79bc5edabc748b90e84
Exercícios
1) É importante que o desenvolvedor compreenda a importância de manter as tarefas a serem
executadas na construção de um código de maneira organizada. Assim como as boas
práticas, os processos também devem ser conduzidos na ordem correta, considerando
também a importância de cada um deles.
Em orientação a objetos, como devemos começar as atividades de programação?
A) Identificando os atributos.
B) Identificando os métodos.
C) Identificando os requisitos funcionais da aplicação.
D) Identificando objetos e as classes às quais eles pertencem.
E) Identificando nomes de classes.
2) Ao criar um aplicativo, podem ser criadas diferentes classes, o que também traz maior
coesão ao programa. Se uma classe com o nome “Cadastro” for criada, ainda não estará claro
qual será sua responsabilidade, mas, se uma segunda classe, denominada “Cliente”, for
criada, é evidente que seu objetivo será tratar da criação do objeto “Cliente”.
Considerando as informações sobre as classes, o que é correto afirmar?
A) Nomearmos uma classe utilizando um verbo que define o objetivo dessa classe.
B) Uma classe deve ser criada para representar vários conceitos do domínio do problema.
C) Se você não pode afirmar, a partir do nome da classe, o que um objeto da classe
supostamente deve fazer, provavelmente você não está no caminho certo.
D) Uma categoria útil de classes pode ser descrita como atores. Essas classes servem para iniciar
um programa.
E) Uma prática comum é nomear métodos com algum substantivo.
3)
No contexto de desenvolvimento de software, em diversas situações, os desenvolvedores
passam por problemas ou encontram bugs que precisam ser corrigidos, por influenciarem
negativamente a funcionalidade de um aplicativo. Para evitar tais defeitos, alguns princípios
podem ser aplicados, em conjunto com as boas práticas de programação.
Referente a coesão e acoplamento, o que se pode afirmar?
A) Uma classe coesa representa uma solução bem estruturada no que se refere à criação do
objeto.
B) A interface pública de uma classe é coesa se abrange todos os requisitos funcionais do
sistema.
C) Quando a interface pública de uma classe referencia vários conceitos, é um bom sinal de que
pode ser hora de utilizar classes separadas.
D) Acoplamento refere-se à dependência que as classes têm em relação aos seus métodos.
E) Se muitas classes de um programa dependerem umas das outras, dizemos que o acoplamento
entre as classes é baixo.
4) É uma boa prática evitar conflitos gerados pela nomeação incorreta de classes. Existem
recursos que facilitam a escrita do código e auxiliam no processo de manter os objetos e
todos os outros componentes atualizados. Os pacotes facilitam a busca ou pesquisa de
classes, interfaces e enumerações.
Ainda sobre os pacotes, o que é correto afirmar?
A) São uma forma de organizar os métodos.
B) São um modificador de acesso.
C) Servem para iniciar programas.
D) Criamos objetos a partir das definições de um pacote.
E) São um conjunto de classes relacionadas.
Analise o código abaixo:
/*
package media;
5)
public class calcularMedia {
private double nota1;
private double nota2;
private double media;
private int matricula;
private String nome;
public void calcularMedia(double nota1, double nota2){
this.nota1 = nota1;
this.nota2 = nota2;
media = (nota1 + nota2)/2;
}
public void cadastrarAluno(int cod, String matricula){
this.cod=cod;
this.matricula = matricula;
}
}
*/
O que é correto afirmar?
A) A classe “calcularMedia” segue a regra geral para nomes de classes.
B) O método “calcularMedia” não irá executar a expressão aritmética.
C) Essa classe não está dentro de nenhum pacote.
D) Essa classe não apresenta coesão.
E) A classe está totalmente escrita de forma correta.
Na prática
A POO é um modelo de programação de software em que os objetos manipulados são abstrações
de objetos do mundo real. Os objetos são as classes abstraídas do mundo real. Desse modo, um
projeto que utiliza o modelo de orientação a objetos é estruturado em pacotes que armazenam
classes semelhantes, exigindo que você conheça como é a relação entre essas classes.
Nesta Na Prática, você irá aprender como os pacotes funcionam.
A imagem a seguir possui audiodescrição.
Aponte a câmera para o
código e acesse o link do
conteúdo ou clique no
código para acessar.
https://statics-marketplace.plataforma.grupoa.education/sagah/730c72d3-404f-4c28-932a-b4fc3cabee9c/e194dd0b-093a-4fcf-ad0a-c062d6162caa.png
Saiba mais
Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do professor:
Como criar uma classe com atributos
Veja este vídeo, que ensina a criar uma classe simples utilizando o eclipse, para que você possa
praticar. O vídeo aborda dicas importantes para criação de um projeto.
Aponte a câmera para o código e acesse o link do conteúdo ou clique no código para acessar.
Java: declaração e utilização de classes
O texto aborda a sintaxe para declaração de uma classe. São trazidos exemplos de como utilizar
uma classe em Java. Você também verá exemplos de instanciação de uma classe.
Aponte a câmera para o código e acesse o link do conteúdo ou clique no código para acessar.
https://www.youtube.com/embed/dauyBWO9-Vs
https://www.devmedia.com.br/java-declaracao-e-utilizacao-de-classes/38374