Loading repository data…
Loading repository data…
JoseRaimundo / repository
Tutorial completo sobre programação orientada a objetos e padrões de projetos.
A transparent discovery signal based on current public GitHub metadata.
This score does not audit code, security, maintainers, documentation quality, or suitability. Verify the repository and its current documentation before adoption.
Este tutorial apresenta uma serie de conceitos e práticas sobre os padrões de projetos, para isso será contextualizado alguns conceitos básicos de Programação Orientada Objeto (POO), e em seguida serão abordados os principais padrões de criação, estrutural e comportamentais. A seção de Padrões de Projetos apresenta exemplos e explicações adaptadas de várias fontes, principalmente do sites do Marcos Brizeno e do Source Making.
Nesta secção, serão apresentados os conceitos básicos de POO, estes conceitos são fundamentais para o entendimento do restante do tutorial, porém se você já é familiarizado com POO, então pode ir direto para a secção de Padrões de Projetos.
Atenção: Para este tutorial, foi utilizado a linguagem de programação Java, com o ambiente de desenvolvimento InteliJ.
Para definirmos bem as funcionalidades de nossos programas, é necessário realizar a abstração do mundo que nossa aplicação irá abranger, por exemplo: Imagine que você irá fazer um sistema simples que calcula a massa corporal de uma pessoa, para essa aplicação serão necessárias abstrair uma especie de objeto no qual você irá trabalhar e que conterá informações como idade, altura e peso atual. Neste objeto, informações como cor do cabelo, gosto musical, CPF e etc, não serão necessários, pois não terão utilidade para sua aplicação. Este processo de mapeamento de informações uteis em um elemento (objeto) coerente com a aplicação se chama de abstração. Dominar o conceito de abstração é algo importante, pois permite ao desenvolvedor formular um cenário mais enxuto e sem distrações.
As classes são projetos de um objetos, na qual encontramos as características e comportamentos que os objetos terão. Analogia: Imagine uma classe como uma receita de bolo em que teremos a massa, cobertura e recheio, todas as propriedades necessárias para fazer nosso bolo estão presentes, ou seja, independente se o bolo será de cobertura de chattily ou morango, massa comum ou de chocolate, sabemos que ele terá uma massa, uma cobertura e um recheio. No contexto da orientação objetos, o bolo feito a partir da nossa receita é um objeto concreto (que já existe), ou seja, quanto nossa receita contem as informações de como nosso bolo deve ser feito, nosso bolo contem as informações do bolo propriamente dito.
| Ex. de Classe | Ex. de Objeto |
|---|---|
| Receita (cobertura, massa) | Bolo (Chocolate, Massa comum) |
| Carro (marca, ano) | Gol(Volkswagen, 2015) |
| Funcionário (nome, salário) | Gerente(Paulo, R$ 7000,00) |
Em Java, uma classe é formada pelo seguinte comando.
class MeuBolo{
//corpo da classe
}
E um objeto (boloDeChocolate) gerado a partir da classe (MeuBolo) em Java é:
MeuBolo boloDeChocolate = new MeuBolo();
Boa Pratica: Para melhorar a legibilidade do seu código, é recomendado utilizar nomes intuitivos para suas classes, e também utilizar as letras maiúsculas para destacar o inicio de palavras no nome da classes, por exemplo: ExemploDeClasse.
Agora que sabemos o conceito básico de nossa classe, falta adicionar as características dela, também chamadas de atributos no contexto da orientação objetos. No código do bolo apresentado anteriormente, foi mostrado apenas a estrutura básica da classe (em Java), porém não foi apresentado nenhuma das suas características, tais como o massa, cobertura e recheio. Como virmos na seção sobre abstração, estes componentes são muito importantes, pois descrevem virtualmente o que queremos mapear do problema real.
O código para representar um atributo em Java, é formado pelo tipo do atributo e o nome, conforme o código a seguir:
class MeuBolo{
//tipo nome
String massa;
String recheio;
String cobertura;
}
Os atributos, como foi dito anteriormente, correspondem as características da nossa classe, na nossa abstração para o exemplo, o bolo contem apenas três atributos, mas em uma situação real (ou dependendo da sua abstração), poderia haver vários e mais complexos (incluindo outras classes).
Em Java e muitas outras linguagens de programação chamadas de "tipadas", os atributos possuem tipos específicos que devem ser associados ao tipo do dado ao qual o atributo está representando.
| Tipo | Descrição | Exemplos |
|---|---|---|
| byte | Valor pequeno (com tamanho de 8 bits) | de -127 a 127 |
| int | Valor inteiro (com tamanho de 32 bits) | 1 , -2 , 2018 |
| long | Valor inteiro (com tamanho de 64 bits) | -1 , 2 , -132113 |
| float | Valor real (com tamanho de 32 bits) | 1.38, 3.14 , 0.0001 |
| double | Valor real (com tamanho de 64 bits) | 1 , 2, 2018 |
| char | Caractere (números,letras ou especias) | 'a' , '%' , '1' |
| String | Palavras, frases e demais conjuntos de caracteres | "Massa comum" , "Java" , "Chocolate" |
Há outros tipos, porém ficaremos apenas com estes para não confundir. Outra coisa importante sobre os tipos, é que eles podem ser primitivos ou não-primitivos (ou também chamados de compostos), em Java os tipos primitivos iniciam com a primeira letra minuscula, enquanto os tipos não-primitivos iniciam com letra maiúscula. Os tipos primitivos são os componentes básicos (atômicos) das classes, eles correspondem a um espaço de memória com tamanho fixo destinado para armazenar o valor que é atribuído a ele. Enquanto os tipos não-primitivos são compostos por outros atributos, um exemplo de tipo não-primitivo é o String, que contem próprios atributos (conjunto de caracteres do tipo char (letras)) e também carrega seus próprios comportamentos (métodos) que veremos mais a frente. :house:
Os métodos correspondem ao comportamento da nossa classe. A estrutura básica de um métodos possui um modificador de acesso (veremos isso mais tarde), um tipo de retorno, o nome do método e os parâmetros.
//modificador retorno nome(parametros){bloco de código}
public void meuMetodo(){
//realiza algum processamento
}
Um método pode ou não receber um parâmetro, um parâmetro é um atributo de escopo
e pode ter ou não um retorno, caso não tenha o local do tipo deve conter o termo void.
Boas práticas: Como um método remete a um comportamente, é recomendado usar verbos no infinitivo para nomear-los, por exemplo: calcularValor, descreverBolo, copiarTexto e etc.
Por motivos de segurança, as vezes é preciso restringir a "visualização" de um método ou atributo de uma classe em relação à outras classes.
Exemplo: Imagine que você criou um método que valida a senha de um usuário, e este método é utilizado apenas na classe ContaUsuario, logo ao utilizar um modificador de acesso, você pode, por exemplo, manter o acesso desse método apenas interno da classe em que ele está ou pode liberar para ser usada em qualquer outro lugar da aplicação.
Para definir a visibilidade de atributos e métodos, na maioria das linguagens, exitem palavras-chaves que definem o escopo de acesso. São elas:
| Modificador | Descrição |
|---|---|
| public | acesso liberado para todos que tem acesso à classe (o modificador mais liberado) |
| private | acesso exclusivo apenas para a classe (o modificador mais rigoroso) |
| protected | acesso para as classes do mesmo pacote ou por meio de herança |
| default | acesso para as classes do mesmo pacote |
Para ser usado em atributos ou métodos, deve ser usado da seguinte forma:
//Exemplo do modificador private em um atributo
private String nome = "José Raimundo";
// Exemplo do modificador public em um método
public String getNome(){
return nome;
}
Modificador abstract: usado em métodos e classes, a classe que o usa não pode ser instanciada e a presença de um método abstrato implica que a classe deve ser abstrata também. Este modificador é usado quando o método ainda não está implementado, normalmente quando você tem apenas uma ideia ou esboço de como será a classe/método, bastante comum em classes pai que possuem métodos que ainda não foram implementadas, sendo necessário a implementação nas classes filhas.
Exemplo: Note que na classe Bolo eu não sei ainda o tipo da cobertura que o bolo irá ter, então eu crio um método abstrato que irá conter apenas a ideia de como o método irá se comportar, em seguida eu passo a tarefa da implementação para os filhos.
public abstract class Bolo{
public abstract String cobertura();
}
A classe filha implementa o métodos abstratos do pai.
public class BoloChocolate exends Bolo{
// .... construtor
@Override
public String cobertura(){
return "Cobertura de chocolate";
}
}
Com essa abordagem, eu consigo padronizar minhas várias classes filhas carregando um método com a mesma nomenclature da classe pai.
Quando um variável é criada utilizando o modificador static, significa que o valor daquela variável será o mesmo em todas as instâncias do objeto que a carrega, e se em alguma delas for modificada o valor será modificado em todas.
Por exemplo, observe o valor do atributo nome.
public class Bolo {
public static String nome = "teste";
public Bolo(){ }
}
No código a seguir, note que ao modificar o valor de nome na instância b2, o valor de nome em b1 também é modificado.
public class main {
public static void main(String[] args) {
Bolo b1 = new Bolo();
System.out.println(b1.nome);
//saída: "teste"
Bolo b2 = new Bolo();
b2.nome = "Chocolate";
System.out.println(b1.nome);
// saída: "Chocolate"
System.out.println(b2.nome);
// saída: "Chocolate"
}
}
Este comportamento é constantemente utilizado em aplicações que utilizam o padrão de projeto singleton (veremos mais a frente), pois garante que um valor seja compartilhado por diversos módulos do mesmo projeto, evitando conflitos ou a necessidade de atualizar diversas instâncias da mesma classe.
Atenção: Os exemplos utilizados neste tutorial são exemplos apresentados como exercícios para o curso de Sistemas Para Internet, no Instituto Federal da Paraíba, há possibilidades de conter alguns erros, caso encontre, basta entrar em contato ou você mesmo consertar via Git.
O objetivo do Factory Method é "Definir uma interface para criar um objeto, mas deixar as subclasses decidirem que classe instanciar. O Factory Method permite adiar a instanciação para subclasses."[5].
Imagine que temos que construir um sistema para nossa loja de bolos para gerenciar os nosso produtos e fornecedores, e que atualmente temos três fornecedores especializados em tipos de bolos diferentes. Exemplo:
| Fornecedor | Bolo |
|---|---|
| João Bolos | Bolo de Ovo |
| Recanto da vovô | Bolo de Nata |
| Sabor nordestino | Bolo Bae |