Programação orientada a objetos

Todos os conceitos da programação orientada à objetos
Voltar
Conteúdo disponível

Programação orientada a objetos (POO ou OOP - object-oriented programming) é paradigma (padrão) de programação, com objetivo de aproximar mundo digital com real. Criado pelo matemático e biólogo Alan Kay. Os exemplos de códigos nesse resumo serão implementados em linguagem Java.

    Vantagens:
    comern_ada
  1. Confiável: isolamento entre partes gera software seguro. Ao alterar uma parte, nenhuma outra é afetada;
  2. Oportuno: ao dividir tudo em partes, várias delas podem ser desenvolvidas em paralelo;
  3. Manutenível: maior facilidade de atualização. Pequena modificação pode beneficiar todas partes que usarem o objeto;
  4. Extensível: software não estático, que deve crescer para permanecer útil;
  5. Reutilizável: utilização de determinadas estruturas, como atributos e métodos, através de herança;
  6. Natural: linguagem natural, de fácil interpretação, onde usuário mantém foco na funcionalidade, ao invés dos detalhes de implementação.

Abstração

Classificar -> Abstrair -> Instanciar

Diferente de métodos e classes abstratas, significa "enxugar" conteúdo, mantendo ao mesmo só o essencial. Observar elemento(s), avaliando características e propriedades à parte.


Classe

Também chamada de 'entidade' (entity). Define conjunto de objetos que compartilham mesmas características (atributos), ações (métodos ou operações), relacionamentos e semântica. Objeto (object) é algo material ou não material (abstrato), que pode ser percebido pelos sentidos e descrito por meio de características (estado atual), possuindo identidade única. Classe instancia (cria) objetos, e objetos são instâncias (criações) de classe. Toda classe é identificada unicamente pelo seu nome. Relacionamentos entre classes determinam conexões entre objetos, fornecendo caminho para comunicação entre os mesmos.



import java.util.ArrayList;
import java.util.List;

public class Pessoa {
    public String nome = "UB Social";
    private List<String> endereco = new ArrayList<>(2);
    private final int cpf;

    public String getNome() {
        return nome;
    }

    public void setEndereco(String endereco, char tipo) {
        if (this.endereco.size() < 2) {
            this.endereco.add(endereco + " (" + tipo + ")");
        } else {
            System.out.println("Máximo de 2 endereços já atingido.");
        }
    }
}

Nome da classe:

Nome único, padrão CamelCase. Pode ser simples (ex: Livro) ou com caminho via padrão 'NomePacote :: NomeClasse', referenciando o pacote onde a classe encontra-se (ex: SitemaLivraria :: Livro).



Atributos:

Cada objeto possui estado, representado pelos valores associados a cada atributo definido pela respectiva classe.
Sintaxe atributo (UML): visibilidade nome : tipo multiplicidade = valor inicial {propriedades e restrições}


  • + titulo : String = "Sapiens"
  • - autor : String [0..2]
  • - paginas : int {frozen}

Visibilidade:

Indica nível de acesso aos componentes internos de classe e/ou dela mesma. Termo visibilidade é sinônimo de acessibilidade (visível = acessível).

  • (+) public: público, visível em todo projeto;
  • (#) protected: protegido, visível na própria classe, nas classes do próprio pacote e classes filhas;
  • (-) private: privado, visível somente na própria classe;
  • (~) package: pacote, visível nas classes do próprio pacote.

Exemplo: atributo privado é visível (acessível) somente na própria classe, ou seja, pode ser acessado/referenciado somente pelos métodos da própria classe. Geralmente atributos privados são acessados por métodos especiais assessores get e set (encapsulamento).

Classes: Em Java, quando não é explicitamente especificada visibilidade de classe, o modificador de acesso padrão é "package-private" ou "default". Isso significa que a classe é acessível apenas dentro do mesmo pacote. A classe não será acessível fora do pacote, mas todas as outras classes no mesmo pacote poderão acessá-la.


Tipo:

Tipo do atributo. Pode ser outra classe, interface ou tipo primitivo da linguagem. Principais tipos no Java:

  • String: classe String, valores formato texto (ex: "fulano 123");
  • Date: classe Date, valores formato data (ex: "01.01.2024");
  • char: caractere, valores formato caractere (ex: 'a');
  • int: integer, valores numéricos inteiros (ex: 13);
  • float ou double: valores numéricos decimais (ex: 23.20);
  • boolean: booleano, valores verdadeiro (true ou 1) ou falso (false ou 0);
  • array: lista de valores (ex: int[] nomeLista: {1,2,3});
  • ArrayList: lista dinâmica de valores/objetos (ex: ArrayList<String> nomeLista = new ArrayList<>());
  • object: objeto, instância de classe, com respectivos atributos e métodos (ex: Livro).

Multiplicidade:

Representada por números ou '*'. Número de instâncias de uma classe que podem ser associadas a instância de outra classe, em mesma relação (quantas vezes uma classe pode se relacionar com outra).

  • 0..1: atributo opcional (pode ser considerado um-para-um);
  • 1: atributo obrigatório (pode ser considerado um-para-um);
  • 0..*: nenhum ou vários objetos (pode ser considerado muitos-para-muitos);
  • 1..*: pelo menos 1 ou vários objetos (pode ser considerado muitos-para-muitos);
  • 4..10: pelo menos 4 e no máximo 10 objetos (pode ser considerado muitos-para-muitos).

Valor inicial:

Valor padrão inicial atribuído ao atributo para cada instância da classe. Há possibilidade de ser modificado futuramente, com exceção segundo propriedades (ex: atributo read-only não pode ser modificado).


Propriedades e restrições:
  • Propriedades: compreendem conjunto de tags pré-definidas, que descrevem determinadas características ao atributo, como read-only e add-only;
  • Restrições: permitem indicar 1 ou mais restrições sobre o atributo. Podem ser escritas em linguagem natural ou com uso de gramática formal, como OCL (ex: '- idade: int {idade>18}').

Estado do objeto:

Conjunto de valores dos atributos que o objeto possui. Ex: estado atual de objeto Livro meuLivro = lido.


// Definição de classe
public class Livro {
    // Atributos
    private String titulo;
    private String autor;
    private int paginas;

    // Construtor
    public Livro(String titulo, String autor, int paginas) {
        this.titulo = titulo;
        this.autor = autor;
        this.paginas = paginas;
    }

    // Getters e Setters
    public String getTitulo() {
        return titulo;
    }
    public void setTitulo(String titulo) {
        this.titulo = titulo;
    }
    public String getAutor() {
        return autor;
    }
    public void setAutor(String autor) {
        this.autor = autor;
    }
    public int getPaginas() {
        return paginas;
    }
    public void setPaginas(int paginas) {
        this.paginas = paginas;
    }

    public void lerLivro() {
        System.out.println("Lendo o livro: " + titulo + " de " + autor);
    }

    public String virarPagina(int pagina) {
        this.paginas = pagina;
        String ler = "Virando para página " + pagina + " do livro: " + titulo;
        return ler;
    }

    // Método main para testar a classe
    public static void main(String[] args) {
        Livro meuLivro = new Livro("O Alquimista", "Paulo Coelho", 208);
        meuLivro.lerLivro();
        System.out.println(meuLivro.virarPagina(23));
        System.out.println("Título: " + meuLivro.getTitulo());
        System.out.println("Autor: " + meuLivro.getAutor());
        System.out.println("Páginas: " + meuLivro.getPaginas());
    }
}

Static

Elemento que pertence à classe e não a instâncias individuais da classe, sendo compartilhado com entre todas instâncias da classe, ou seja, todas instâncias acessam e modificam o mesmo valor (valor salvo no mesmo local de memória - valor estático mutável), único entre todos seus semelhantes que o encapsulam. Não possui dependência de terceiros. Se utilizado em atributos, o atributo vira 'atributo de classe'. Método static pertence somente à classe em si, em vez de aos objetos da mesma. Método static pode ser invocado (chamado) sem criar instância da mesma. Método static não pode acessar ou modificar diretamente atributos do objeto (não tem acesso via self ou this). Métodos static podem acessar e modificar variáveis estáticas da classe. No exemplo, 'contador' é compartilhado entre todas instâncias da classe 'Exemplo'. Em diagrama de classes UML, elementos static são representados por sublinhado. Pode-se usar static e final para definir constante de classe, onde cria-se valor imutável pertencente a classe. No exemplo 2, atributo 'pi' é constante pertencente a classe 'Exemplo2', e seu valor não pode ser alterado. static define que membro (atributo ou método) pertence à classe, e não a instâncias. final define que valor (para atributos) ou comportamento (para métodos ou classes) não pode ser alterado. static final define constante de classe, com valor imutável compartilhado entre todas instâncias da mesma.


class Exemplo {
    static int contador = 0;

    static void incrementar() {
        contador++;
    }
}

class Exemplo2 {
    static final double PI = 3.14;
}

public class Estatico {

    static int contador = 0;
    static final double PI = 3.14;

    static void incrementarContador() {
        contador++;
        System.out.println("Contador: " + contador);
    }

    static final void mostrarPI() {
        System.out.println("Valor de PI: " + PI);
    }

    public static void main(String[] args) {
        Exemplo.contador = 5;
        Exemplo.incrementarContador();
        System.out.println("PI: " + Exemplo.PI);
        Exemplo.mostrarPI();
    }
}

Atributo de objeto

Atributo não estático, no qual cada objeto encapsula seu próprio valor (quando um atributo é do tipo de outra classe criada). Não há distinção visual em UML para tal (ex: '- salario : double'). Exemplo, em classe Funcionario, atributo de objeto 'salario', cada objeto Funcionario encapsula seu próprio valor de 'salario'. O tipo objeto é quando atributo é do tipo de outra classe criada.


Atributo de classe

Também chamado de atributo estático (static). Possui mesmo valor para todos objetos que o encapsularem (valor salvo no mesmo local de memória). Utilizado para padronização de valor no sistema. Alteração no valor do atributo de classe implicará, consequentemente, na alteração de todos valores dos mesmos, presentes nos outros objetos semelhantes. Em UML, atributo de classe é sublinhado (ex: '- pisoSalarial : double'). Exemplo, em classe Funcionario, atributo de classe pisoSalarial possuirá mesmo valor para todos objetos Funcionarios.


public class Livro {
    // Atributo de classe
    public static int contadorLivros = 0;

    // Atributo de objeto (não-static)
    public String nome;

    public Livro(String nome) {
        this.nome = nome;
        contadorLivros++;
    }

    public void mostrarInformacoes() {
        System.out.println("Nome: " + this.nome);
        System.out.println("Contador de livros: " + Livro.contadorLivros);
    }

    public static void main(String[] args) {
        Livro livro1 = new Livro("Livro 1");
        Livro livro2 = new Livro("Livro 2");
        livro1.mostrarInformacoes();
        livro2.mostrarInformacoes();

        // Acessar atributo de classe diretamente
        System.out.println("Contador de livros (diretamente): " + Livro.contadorLivros);
    }
}

Atributo derivado

Atributo implícito na classe (não definido, armazenado e acessado explicitamente), mas háverá métodos para acessá-lo. Pode ser calculado através de outros atributos de classe. Em UML, atributo derivado possui caractere barra antes do nome (ex: '- / media : int'). Exemplo, em classe Aluno, atributo derivado media não é explícito, mas pode ser utilizado em métodos como calcularMedia.


public class Retangulo {
    private double largura;
    private double altura;

    public Retangulo(double largura, double altura) {
        this.largura = largura;
        this.altura = altura;
    }

    public double getLargura() {return largura;}
    public void setLargura(double largura) {this.largura = largura;}
    public double getAltura() {return altura;}
    public void setAltura(double altura) {this.altura = altura;}

    // Método para calcular área (atributo derivado - area)
    public double getArea() {
        return largura * altura;
    }

    public static void main(String[] args) {
        Retangulo retangulo = new Retangulo(5, 10);
        System.out.println("Largura: " + retangulo.getLargura());
        System.out.println("Altura: " + retangulo.getAltura());
        System.out.println("Área: " + retangulo.getArea());

        retangulo.setLargura(7);
        retangulo.setAltura(3);
        System.out.println("Nova Largura: " + retangulo.getLargura());
        System.out.println("Nova Altura: " + retangulo.getAltura());
        System.out.println("Nova Área: " + retangulo.getArea());
    }
}

Método

Também chamado de operação ou ação. Serviço que pode ser requisitado a qualquer objeto da classe, afetando seu comportamento. A execução de método por um objeto pode resultar na alteração do valor de seus atributos. Geralmente, seu nome é verbo, indicando ação (ex: lerLivro, fazerAniversario, calcularSoma). Exemplo, em classe Aluno, método calcularMedia utilizará atributo listaNotas para calcular média de notas presentes em listaNotas desse objeto Aluno, e acoplará valor final de média no atributo media do mesmo. Geralmente, toda classe comum possui método especial construtor (construct, criador, instancia objetos) e, para cada atributo comum, tem-se métodos especiais get (getNomeAtributo, assessor, retorna valor do atributo) e método set (setNomeAtributo, modificador, modifica valor do atributo).
Sintaxe: visibilidade nome (parametro: tipo, parametro: tipo) : tipo-retorno {propriedades}


  • + getTitulo () : String
  • + setTitulo (titulo: String) : void
  • Método de objeto: método comum, atua sobre objeto (instância). Não há distinção visual em UML para tal (ex: '+ getSalario() : double');
  • Método de classe: método static, atua sobre classe (conjunto de objetos), onde não é necessário ter instância de classe para acessá-la. Em UML, operação de classe é sublinhado (ex: '+ setPisoSalarial(p: double) : void').

public class Calculadora {
    // Método de classe (static)
    public static int somar(int a, int b) {
        return a + b;
    }

    public static int subtrair(int a, int b) {
        return a - b;
    }

    public static int multiplicar(int a, int b) {
        return a * b;
    }

    public static double dividir(int a, int b) {
        if (b == 0) {
            throw new IllegalArgumentException("Divisor não pode ser zero");
        }
        return (double) a / b;
    }

    public static void main(String[] args) {
        // Invocar métodos de classe sem criar instância de Calculadora
        int soma = Calculadora.somar(10, 5);
        int subtracao = Calculadora.subtrair(10, 5);
        int multiplicacao = Calculadora.multiplicar(10, 5);
        double divisao = Calculadora.dividir(10, 5);

        System.out.println("Soma: " + soma);
        System.out.println("Subtração: " + subtracao);
        System.out.println("Multiplicação: " + multiplicacao);
        System.out.println("Divisão: " + divisao);
    }
}

Assinatura do método:

Constitui nome, tipo do método, quantidade e ordem de seus parâmetros (nome dos parâmetros não faz parte da assinatura, nem tipo do retorno do método). No método Java "public int lerLivro(pagina: int, lido: boolean) {pagina = this.pagina++;}", a assinatura é 'lerLivro', método tipo 'int', possui 2 parâmetros ordenados int e boolean.


Visibilidade:
  • (+) public: público, visível em todo projeto. Qualquer objeto pode invocar (chamar, referenciar, executar) método;
  • (#) protected: protegido, visível na própria classe, nas classes do próprio pacote e classes filhas. Somente objetos da própria classe, subclasses e classes do mesmo pacote podem invocar método;
  • (-) private: privado, visível somente na própria classe. Somente objetos da própria mesma classe podem invocar método;
  • (~) package: pacote, visível nas classes do próprio pacote. Objetos de classes do mesmo pacote podem invocar método.

Lista de parâmetros:

Lista de valores (e tipos) de entrada para o método. Um método não precisa, obrigatoriamente, ter 1 ou mais parâmetros de entrada, ou seja, lista de parâmetros do método é opcional.


public class Pessoa {
    private String nome;
    private int idade;

    public Pessoa(String nome, int idade) {
        this.nome = nome;
        this.idade = idade;
    }

    // Método sem parâmetros
    public void mostrarInformacoes() {
        System.out.println("Nome: " + nome);
        System.out.println("Idade: " + idade);
    }

    // Método com parâmetros
    public void atualizarInformacoes(String novoNome, int novaIdade) {
        this.nome = novoNome;
        this.idade = novaIdade;
    }

    public static void main(String[] args) {
        Pessoa pessoa = new Pessoa("João", 25);

        // Invocar método sem parâmetros
        pessoa.mostrarInformacoes();

        // Atualizando as informações da pessoa
        pessoa.atualizarInformacoes("Maria", 30);

        // Invocar método sem parâmetros novamente para ver alterações
        pessoa.mostrarInformacoes();
    }
}

Tipo de retorno:

Valor de retorno (return) indica tipo de dado que será informado como resultado da operação.


public class ContaBancaria {
    private String titular;
    private double saldo;

    public ContaBancaria(String titular, double saldoInicial) {
        this.titular = titular;
        this.saldo = saldoInicial;
    }

    // Método sem retorno (void)
    public void depositar(double valor) {
        if (valor > 0) {
            saldo += valor;
            System.out.println("Depósito de R$ " + valor + " realizado com sucesso.");
        }
    }

    // Método com retorno (double)
    public double obterSaldo() {
        return saldo;
    }

    public static void main(String[] args) {
        ContaBancaria conta = new ContaBancaria("Alice", 1000.0);

        // Invocar método sem retorno (void)
        conta.depositar(500.0);

        // Invocar método com retorno (double) e armazenar valor retornado
        double saldoAtual = conta.obterSaldo();
        System.out.println("Saldo atual: R$ " + saldoAtual);
    }
}

Propriedades:

Conjunto de tags pré-definindas que descrevem características da operação, como isQuery, guarded e leaf.


Métodos especiais

Métodos com operações individuais, diferentes dos demais (comuns). get (assessor), set (modificador), construct (construtor, criador). Cada atributo da classe possui um método especial get para acessar seu valor ('getNomeAtributo()'), e um método especial set para modificar seu valor ('setNomeAtributo(novoValor)'). Em UML, getAll e setAll são utilizados para substituir, implicitamente, os métodos get e set de todos atributos da classe.


public class Livro {
    private String titulo;
    private String autor;
    private double preco;

    // Método construtor
    public Livro(String titulo, String autor, double preco) {
        this.titulo = titulo;
        this.autor = autor;
        this.preco = preco;
    }

    // Método getter para o título
    public String getTitulo() {
        return titulo;
    }

    // Método setter para o título
    public void setTitulo(String titulo) {
        this.titulo = titulo;
    }

    // Método getter para o autor
    public String getAutor() {
        return autor;
    }

    // Método setter para o autor
    public void setAutor(String autor) {
        this.autor = autor;
    }

    // Método getter para o preço
    public double getPreco() {
        return preco;
    }

    // Método setter para o preço
    public void setPreco(double preco) {
        this.preco = preco;
    }

    public void mostrarInformacoes() {
        System.out.println("Título: " + titulo);
        System.out.println("Autor: " + autor);
        System.out.println("Preço: R$ " + preco);
    }

    public static void main(String[] args) {
        // Criar objeto Livro
        Livro livro = new Livro("1984", "George Orwell", 39.90);

        livro.mostrarInformacoes();

        // Invocar método getTitulo
        System.out.println(livro.getTitulo());

        // Invocar métodos set
        livro.setTitulo("Admirável Mundo Novo");
        livro.setAutor("Aldous Huxley");
        livro.setPreco(29.90);

        livro.mostrarInformacoes();
    }
}

Método construtor:

Método construct, com mesmo nome da classe e visibilidade public, cuja função é criar instâncias (objetos) da classe. Em UML, método construtor não possui tipo de retorno visível (ex método construtor de classe Livro: '+ Livro(args : ArrayList)'). Em UML, o atributo 'args' representa, implicitamente, todos atributos da classe para criação de suas instâncias. Em Java, utiliza-se o super() para importar, implicitamente, todos atributos das superclasses (herança).


// classe Livro é subclasse de Publicacao, onde, no construtor da classe Livro, importa-se (via super) atributos da superclasse Publicacao. Construtor Livro instanciará objetos Livros, com dados de Livro e também Publicacao
public class Publicacao {
    private String titulo;
    private String autor;

    public Publicacao(String titulo, String autor) {
        this.titulo = titulo;
        this.autor = autor;
    }
}

public class Livro extends Publicacao {
    private int paginas;

    public Livro(String titulo, String autor, int paginas) {
        super(titulo, autor);
        this.paginas = paginas;
    }

}

public static void main(String[] args) {
    Livro meuLivro = new Livro("1984", "George Orwell", 328);
}

Encapsulamento

Técnica para proteger código, via criação de métodos públicos para realizar acesso protegido a atributos e métodos não públicos. Ocultar partes independentes da implementação, permitindo construir partes invisíveis ao mundo (código) exterior. Entre as vantagens, tem-se mudanças no código interno imperceptíveis ao código externo, reutilização de código e redução de efeitos colaterais.


public class ContaBancaria {
    // Atributos privados (encapsulados)
    private String titular;
    private double saldo;

    public ContaBancaria(String titular, double saldoInicial) {
        this.titular = titular;
        this.saldo = saldoInicial;
    }

    // Método público getter para acessar atributo privado titular
    public String getTitular() {
        return titular;
    }

    // Método público setter para acessar atributo privado titular
    public void setTitular(String titular) {
        this.titular = titular;
    }

    // Método público getter para acessar atributo privado saldo
    public double getSaldo() {
        return saldo;
    }

    public static void main(String[] args) {
        // Criar objeto ContaBancaria (criar, via método público construct, objeto de atributos privados encapsulados)
        ContaBancaria conta = new ContaBancaria("Alice", 1000.0);

        // Exibir saldo inicial (acessar, via método público get, valor de atributo privado encapsulado)
        System.out.println("Saldo inicial: R$ " + conta.getSaldo());

        // Exibir titular
        System.out.println("Titular: " + conta.getTitular());

        // Modificar titular (modificar, via método público set, valor de atributo privado encapsulado)
        String novoTitular = "Fulana";
        setTitular(novoTitular);

        // Exibir novo titular
        System.out.println("Novo titular: " + conta.getTitular());
    }
}

Método com valor de retorno

Método com return, onde o tipo do método é o mesmo que o tipo de seu valor de retorno (return).


Método void

Método que não possui valor de retorno (return), mas pode ter parâmetros.


public class Cafe {
    private String tipo;
    private double precoPorXicara;

    public Cafe(String tipo, double precoPorXicara) {
        this.tipo = tipo;
        this.precoPorXicara = precoPorXicara;
    }

    // Método com return (return precisa ser String, já que método também é String)
    public String getTipo() {
        return tipo;
    }

    // Método void (não possui return, mas tem parâmetros)
    public void setTipo(String tipo) {
        this.tipo = tipo;
    }

    public double getPrecoPorXicara() {return precoPorXicara;}
    public void setPrecoPorXicara(double precoPorXicara) {this.precoPorXicara = precoPorXicara;}

    // Método com valor de retorno que calcula o preço total
    public double calcularPrecoTotal(int quantidade) {
        return quantidade * precoPorXicara;
    }

    public static void main(String[] args) {
        Cafe cafe = new Cafe("Expresso", 5.0);

        System.out.println("Tipo de café: " + cafe.getTipo());
        System.out.println("Preço por xícara: R$ " + cafe.getPrecoPorXicara());

        int quantidade = 3;
        double precoTotal = cafe.calcularPrecoTotal(quantidade);
        System.out.println("Preço total para " + quantidade + " xícaras: R$ " + precoTotal);

        cafe.setTipo("Latte");
        cafe.setPrecoPorXicara(6.0);
        quantidade = 2;
        precoTotal = cafe.calcularPrecoTotal(quantidade);
        System.out.println("Preço total para " + quantidade + " xícaras: R$ " + precoTotal);
    }
}

Responsabilidades de classe

Responsabilidade de classe são as obrigações dentro do contexto do sistema, onde são traduzidas em conjunto de atributos e métodos que melhor atendam tais obrigações. Em UML, uma anotação (Note) pode ser relacionada na classe via relação NoteAnchor (linha tracejada, sem setas).



Relacionamentos

  • Relacionamentos fracos: não armazenados em banco de dados. Em UML, representados por linha tracejada. Realização e dependência;
  • Relacionamentos fortes: armazenado em banco de dados. Em UML, representados por linha contínua. Associação (agregação e composição) e generalização.

Associação

Relacionamento estrutural, forte, que descreve ligação entre objetos das classes. Uma associação pode ter nome, que pode ser utilizado para descrever a natureza do relacionamento. Em UML, associação é representada por linha contínua entre classes, opcionalmente com nome e seta indicando implicação à classe alvo. Associação possui, opcionalmente, multiplicidade:

  • (1-1): somente 1 autor escreve somente 1 livro;
  • (1-0..1): somente 1 autor escreve nenhum ou somente 1 livro;
  • (1-1..*): somente 1 autor escreve 1 ou mais livros;
  • (1-0..*): somente 1 autor escreve nenhum ou mais livros (exemplo na imagem).

Associação possui, opcionalmente, papéis de cada classe na relação (name of the association end). Pode ocorrer associação comum, entre diferentes classes, ou auto associação, na mesma classe. Conforme exemplo abaixo, 1 autor, denominado autor da obra, escreve nenhum ou mais livros em uma lista de livros. Na auto associação, opcionalmente 1 pessoa cônjugeA realiza casamento com, opcionalmente, 1 pessoa cônjugeB.



Associação possui, opcionalmente, navegabilidade (representada por seta), que determina visualização (acesso) por parte de classes relacionadas. Tipos de navegabilidades de associação:

  • (A - B): associação com navegabilidade não especificada, onde há ausênca de setas abertas em ambas extremidades da linha contínua. Classes A e B podem, ou não, acessar uma a outra;
  • (A <-> B): associação com navegabilidade especificada em ambas extremidades, onde há presença de setas abertas em ambas extremidades da linha contínua. Classes A e B acessam uma a outra. Exemplo abaixo;
  • (A -> B): associação com navegabilidade especificada em somente 1 extremidade e não especificada em outra extremidade. Classe A acessa B, mas classe B pode, ou não, acessar A;
  • (A x-> B): associação sem navegabilidade (x) em 1 extremidade e com navegabilidade na outra extremidade. Classe A acessa B, mas classe B não acessa A;
  • (A x-x B): associação sem navegabilidades em ambas extremidades. Classe A não acessa B, nem classe B acessa A.

Associação pode possuir propriedade que determina fim da associação, apontando o classificador associado. Em UML, o fim da associação é representado por círculo fechado preenchido (similar a um ponto). A propriedade que está apontada pelo círculo, é pertencente à propriedade do outro lado da linha de associação. Conforme exemplo abaixo, o fim da associação "query" pertence a classe QueryBuilder, e o fim da associação "qbuilder" é propriedade da própria associação Builds.



import java.util.ArrayList;
import java.util.List;

// Classe Professor
class Professor {
    private String nome;
    private List<Disciplina> disciplinas;

    public Professor(String nome) {
        this.nome = nome;
        this.disciplinas = new ArrayList<>();
    }

    // Método para adicionar disciplina que professor leciona
    public void adicionarDisciplina(Disciplina disciplina) {
        disciplinas.add(disciplina);
    }

    // Método para exibir disciplinas que professor leciona
    public void mostrarDisciplinas() {
        System.out.println("Professor: " + nome);
        System.out.println("Disciplinas lecionadas:");
        for (Disciplina disciplina : disciplinas) {
            System.out.println("- " + disciplina.getNome());
        }
    }
}

// Classe Disciplina
class Disciplina {
    private String nome;
    private List<Professor> professores;

    public Disciplina(String nome) {
        this.nome = nome;
        this.professores = new ArrayList<>();
    }

    public String getNome() {return nome;}

    // Método para adicionar professor que leciona a disciplina
    public void adicionarProfessor(Professor professor) {
        professores.add(professor);
    }

    // Método para exibir professores que lecionam a disciplina
    public void mostrarProfessores() {
        System.out.println("Disciplina: " + nome);
        System.out.println("Professores que lecionam:");
        for (Professor professor : professores) {
            System.out.println("- " + professor.nome);
        }
    }
}

// Exemplo de uso das classes
public class ExemploAssociacao {
    public static void main(String[] args) {
        // Criar objetos professores
        Professor p1 = new Professor("João");
        Professor p2 = new Professor("Maria");

        // Criar objetos disciplinas
        Disciplina d1 = new Disciplina("Matemática");
        Disciplina d2 = new Disciplina("História");

        // Associar professores às disciplinas
        d1.adicionarProfessor(p1);
        d1.adicionarProfessor(p2);
        d2.adicionarProfessor(p2);

        // Associar disciplinas aos professores
        p1.adicionarDisciplina(d1);
        p2.adicionarDisciplina(d1);
        p2.adicionarDisciplina(d2);

        // Exibir disciplinas lecionadas por cada professor
        p1.mostrarDisciplinas();
        System.out.println();
        p2.mostrarDisciplinas();

        // Exibir professores que lecionam cada disciplina
        System.out.println();
        d1.mostrarProfessores();
        System.out.println();
        d2.mostrarProfessores();
    }
}

Agregação

Tipo especial de associação. Agregação ("tem um" ou "é um agregado de"), relação entre classes, onde classe B tem um atributo da classe A, na qual a definição do atributo em B diferencia-se da mesma em A. Dessa forma, uma das classes é parte ou está contida em outra classe.
Ex1: 1 Canil é agregado de 0 ou mais Cachorros;
Ex2: Mercado addProduto(p1); onde p1 é instância da classe Produto, e só adicionado, estando preenchido, na classe Mercado;

Agregação possui navegabilidade representada, em UML, através de losango não preenchido, apontado ao agregador ('classe X tem' o 'classe x é um agregado de'). Na outra extremidade da linha contínua (associação), tem opcionalmente navegabilidade à classe agregada.
No exemplo 1 abaixo, nenhuma ou 1 biblioteca é um agregado de 1 ou mais livros, onde classe Biblioteca acessa Livro, mas Livro pode, ou não, acessar Biblioteca (agregação para associação sem navegabilidade especificada). No exemplo 2 abaixo, nenhuma ou 1 biblioteca é um agregado de 1 ou mais livros, onde classe Biblioteca2 acessa Livro2, e Livro2 acessa Biblioteca2 (agregação para associação navegável).



import java.util.ArrayList;
import java.util.List;

// Classe Funcionário
class Funcionário {
    private String nome;
    private int idade;

    public Funcionário(String nome, int idade) {
        this.nome = nome;
        this.idade = idade;
    }

    public String getNome() {return nome;}
    public int getIdade() {return idade;}
}

// Classe Departamento
class Departamento {
    private String nome;
    private List<Funcionário> funcionários;

    public Departamento(String nome) {
        this.nome = nome;
        this.funcionários = new ArrayList<>();
    }

    // Método para adicionar funcionários ao departamento
    public void adicionarFuncionário(Funcionário funcionário) {
        funcionários.add(funcionário);
    }

    // Método para exibir funcionários do departamento
    public void mostrarFuncionários() {
        System.out.println("Departamento: " + nome);
        System.out.println("Funcionários:");
        for (Funcionário funcionário : funcionários) {
            System.out.println("- " + funcionário.getNome() + ", " + funcionário.getIdade() + " anos");
        }
    }
}

// Exemplo de uso das classes
public class ExemploAgregacao {
    public static void main(String[] args) {
        // Criar objetos funcionários
        Funcionário f1 = new Funcionário("João", 30);
        Funcionário f2 = new Funcionário("Maria", 25);
        Funcionário f3 = new Funcionário("José", 28);

        // Criar objeto departamento
        Departamento d1 = new Departamento("Recursos Humanos");

        // Adicionar funcionários ao departamento
        d1.adicionarFuncionário(f1);
        d1.adicionarFuncionário(f2);
        d1.adicionarFuncionário(f3);

        // Exibir funcionários do departamento
        d1.mostrarFuncionários();
    }
}

Nesting

Nesting (owning) consiste em aninhar/conter (nest) elemento dentro de outro, para mostrar relações de composição ou agrupamento entre partes do modelo. Usado quando classe contém outra classe(s) como atributo(s), indicando que essa está intimamente relacionada ou é parte essencial da classe principal/proprietária owner (contém partes (classe) do código referenciadas no classe todo). Em UML, nesting é relacionado através de linha contínua com um círculo fechado em uma das extremidades da linha, preenchido com um "+", apontando para o todo (elemento que vai conter o outro dentro). No exemplo 1 abaixo, a classe Lista está aninhando (todo, está contendo, contém) a interface Elemento, ou seja, Elemento está no escopo de Lista (está contido). No exemplo 2 abaixo, classe ProximoValor está aninhada pela classe EstruturaDados, possuindo classe ElementoDado no código de ProximoValor. A classe ElementoDado está agregada em EstruturaDados.




// Classe externa
class ClasseExterna {
    private int valorExterno;

    public ClasseExterna(int valorExterno) {
        this.valorExterno = valorExterno;
    }

    // Método da classe externa
    public void mostrarValor() {
        System.out.println("Valor externo: " + valorExterno);
    }

    // Classe interna (nested class)
    class ClasseInterna {
        private int valorInterno;

        public ClasseInterna(int valorInterno) {
            this.valorInterno = valorInterno;
        }

        // Método da classe interna
        public void mostrarValorInterno() {
            System.out.println("Valor interno: " + valorInterno);
        }
    }
}

// Classes aninhadas
public class ExemploClassesAninhadas {
    public static void main(String[] args) {
        // Criar instância da classe externa
        ClasseExterna externa = new ClasseExterna(10);

        // Criar instância da classe interna usando a instância da classe externa
        ClasseExterna.ClasseInterna interna = externa.new ClasseInterna(5);

        externa.mostrarValor(); // Saída: Valor externo: 10
        interna.mostrarValorInterno(); // Saída: Valor interno: 5
    }
}

Composição

Tipo especial de associação. Relacionamento forte, dependente ("relaciona-se exclusivamente"). Quando uma parte é criada (parte do todo), sua existência coincide com o todo, ou seja, se o objeto da classe que o contém for destruído, as classes da composição também serão destruídas, já que as mesmas fazem parte da outra (quando o todo é apagado, apaga-se também os dependentes - partes do todo). Exemplo, Canil é composto por Cachorros, então se não existir Canil, também não existirão Cachorros. Então, Canil é um composto de Cachorros, e Cachorros compõem-se de Canil.
Ex1: Classe Mercado tem addProduto("vassoura",25.9). Objeto produto não é criado na classe Produto, sendo dependente de sua criação na classe Mercado;
Ex2: Na agregação e associação, objetos adicionados na classe Canil podem ter sido criados em outra classe. Na composição, um Canil cria Cachorro.

Associação possui navegabilidade representada, em UML, através de losango preenchido, apontado ao "todo", sendo esse sempre com multiplicidade 1. Na outra extremidade da linha contínua (associação), tem opcionalmente navegabilidade à classe dependente do todo.
No exemplo 1 abaixo, 1 Editora é um composto (todo) de nenhum ou mais Livros, onde Editora acessa Livro, mas Livro pode, ou não, acessar Editora (composição para associação sem navegabilidade especificada). Nesse caso, se Editora for destruída, Livro também será. No exemplo 2 abaixo, ocorre o mesmo procedimento. Entretanto, Editora acessa Livro, e Livro acessa Editora (composição para associação navegável).


Exemplo 1:

Computador (todo) possui diretamente objetos (partes do todo) CPU, PlacaMae e MemoriaRAM. Se Computador for destruído, os componentes CPU, PlacaMae e MemoriaRAM também serão destruídos, pois eles não têm existência própria independente do Computador.


// Classe CPU (parte do composto)
class CPU {
    private String tipo;
    private int velocidade;

    public CPU(String tipo, int velocidade) {
        this.tipo = tipo;
        this.velocidade = velocidade;
    }

    public String getTipo() {return tipo;}
    public int getVelocidade() {return velocidade;}
}

// Classe PlacaMae (parte do composto)
class PlacaMae {
    private String modelo;
    private int slotsMemoria;

    public PlacaMae(String modelo, int slotsMemoria) {
        this.modelo = modelo;
        this.slotsMemoria = slotsMemoria;
    }

    public String getModelo() {return modelo;}
    public int getSlotsMemoria() {return slotsMemoria;}
}

// Classe MemoriaRAM (parte do composto)
class MemoriaRAM {
    private int capacidade;
    private String tipo;

    public MemoriaRAM(int capacidade, String tipo) {
        this.capacidade = capacidade;
        this.tipo = tipo;
    }

    public int getCapacidade() {return capacidade;}
    public String getTipo() {return tipo;}
}

// Classe Computador (composto/todo: composição com CPU, PlacaMae e MemoriaRAM)
class Computador {
    private CPU cpu;
    private PlacaMae placaMae;
    private MemoriaRAM memoriaRam;

    public Computador(CPU cpu, PlacaMae placaMae, MemoriaRAM memoriaRam) {
        this.cpu = cpu;
        this.placaMae = placaMae;
        this.memoriaRam = memoriaRam;
    }

    // Método para exibir informações do computador
    public void exibirInfo() {
        System.out.println("Computador:");
        System.out.println("- CPU: " + cpu.getTipo() + ", " + cpu.getVelocidade() + " GHz");
        System.out.println("- Placa Mãe: " + placaMae.getModelo() + ", " + placaMae.getSlotsMemoria() + " slots de memória");
        System.out.println("- Memória RAM: " + memoriaRam.getCapacidade() + " GB, " + memoriaRam.getTipo());
    }
}

// Exemplo de uso das classes
public class ExemploComposicaoJava {
    public static void main(String[] args) {
        // Criar componentes individuais do computador
        CPU cpu = new CPU("Intel Core i7", 3);
        PlacaMae placaMae = new PlacaMae("ASUS Prime Z590", 4);
        MemoriaRAM memoriaRAM = new MemoriaRAM(16, "DDR4");

        // Criar objeto computador com os componentes criados
        Computador meuComputador = new Computador(cpu, placaMae, memoriaRAM);

        // Exibir informações do computador
        meuComputador.exibirInfo();
    }
}


Exemplo 2:


public class Canil {
    private Cachorro[];
    ArrayList<Cachorro> listac;
    public void addCachorro(Cachorro c){}
    public void addCachorro(String nome, Date) {
        Cachorro c = new Cachorro(nome, dtNasc);
        this.listac.add(c); -> Associação: entre as classes gera o listac, pois possibilita instanciar o objeto que representa a parte fora de seu todo
    } -> Composição: Canil depende de NotaFiscal, pois para criar um objeto NotaFiscal é necessário ter a classe da mesma
    public NotaFiscal geraNota(Date data, double valor) {
        NotaFiscal f = new NotaFiscal();
        return f;
    }
}

Herança

Relacionamento hierárquico forte, chamado generalização. Quando classe(s) herda(m) atributos, métodos e relacionamentos de outra(s) ("é um tipo de", exemplo: Cachorro (classe filha, subclasse) é um tipo de Animal (classe pai, superclasse)). Subclasse herda atributos e métodos da superclasse, permitindo modificações nos mesmos (sobrescrita - @Override). Consequentemente, herança incorpora todo conteúdo da(s) superclasse(s) na(s) subclasse(s). Quando há herança para atribuir os valores aos atributos herdados, utilizar através do Construtor o super (representa, implicitamente, atributos e construtor da superclasse).

  • Generalização: árvore acima, de subclasse para superclasse;
  • Especialização: árvore abaixo, de superclasse para subclasse;
  • Classe ancestral (mãe, base): superclasse;
  • Classe raíz (root, base): superclasse progenitora (classe de origem, mãe de todas, 1ª classe). Em UML, possui, opcionalmente, propriedade {root} ao lado do nome;
  • Subclasse (derivada, filha): classe gerada a partir de herança de superclasse. Incorpora, implicitamente, todos atributos, métodos e relacionamentos da(s) superclasse(s), salve exceções;
  • Classe folha (filha, derivada, leaf): última filha (não possui filhas, linha final da árvore, não pode ser herdada). Também chamada de classe final. Em UML, possui, opcionalmente, propriedade {leaf} ao lado do nome.
    Tipos de herança:
  • Herança de implementação: quando subclasse não possui conteúdo extra e próprio, somente conteúdo da superclasse;
  • Herança para diferença: quando subclasse possui conteúdo extra e próprio, além do conteúdo da superclasse;
  • Herança múltipla: conceito OO, não encontrado em linguagens, caracteriza herança de atributos e métodos de 2 ou mais superclasses. Nelas, não há divisão de atributos, e sim acréscimos.

Generalização é representada, em UML, através de linha contínua e, em uma das extremidades, uma seta fechada, não preenchida, apontando para superclasse. Não especifica-se navegabilidade.
No exemplo abaixo, subclasse Livro herdará os atributos e método da superclasse Exemplar (pois todo livro é um exemplar, e todo exemplar gera um livro). Nesse caso, Livro possuirá, além de seus próprios atributos, métodos e relacionamentos, as mesmas propriedades de Exemplar.



Exemplo 1:


// Classe Rascunho (superclasse)
class Rascunho {
    protected String titulo;
    protected String autor;

    public Rascunho(String titulo, String autor) {
        this.titulo = titulo;
        this.autor = autor;
    }

    public String getTitulo() {return titulo;}
    public String getAutor() {return autor;}

    public void exibirInfo() {
        System.out.println("Título: " + titulo);
        System.out.println("Autor: " + autor);
    }
}

// Classe Livro (subclasse, herda de Rascunho)
class Livro extends Rascunho {
    private String isbn;
    private int numeroPaginas;

    public Livro(String titulo, String autor, String isbn, int numeroPaginas) {
        super(titulo, autor); // Chama construtor da Rascunho
        this.isbn = isbn;
        this.numeroPaginas = numeroPaginas;
    }

    public String getIsbn() {return isbn;}
    public int getNumeroPaginas() {return numeroPaginas;}

    // Método para exibir informações do livro (sobrescreve exibirInfo da classe Rascunho)
    @Override
    public void exibirInfo() {
        super.exibirInfo(); // Invoca método da classe Rascunho
        System.out.println("ISBN: " + isbn);
        System.out.println("Número de Páginas: " + numeroPaginas);
    }
}

// Exemplo de uso das classes
public class ExemploHeranca {
    public static void main(String[] args) {
        // Criar objeto rascunho
        Rascunho r1 = new Rascunho("Rascunho de Java", "João Silva");

        // Criar objeto livro
        Livro l1 = new Livro("Java Completo", "Maria Oliveira", "978-3-16-148410-0", 450);

        // Exibir informações do rascunho
        System.out.println("Informações do Rascunho:");
        r1.exibirInfo();

        // Exibir informações do livro
        System.out.println("\nInformações do Livro:");
        l1.exibirInfo();
    }
}


Exemplo 2:

Classe Animal (superclasse), Cachorro (subclasse de Animal) e Poodle (subclasse de Cachorro, e também é classe final - leaf)


// Classe base Animal
class Animal {
    protected String nome;
    protected int idade;

    public Animal(String nome, int idade) {
        this.nome = nome;
        this.idade = idade;
    }

    public String getNome() {return nome;}
    public int getIdade() {return idade;}

    public void emitirSom() {
        System.out.println("O animal emite um som.");
    }
}

// Classe derivada Cachorro
class Cachorro extends Animal {

    public Cachorro(String nome, int idade) {
        super(nome, idade); // Invoca construtor da classe base Animal
    }

    public void latir() {
        System.out.println("O cachorro late: Au Au!");
    }

    // Sobrescrer método emitirSom da classe base Animal
    @Override
    public void emitirSom() {
        System.out.println("O cachorro faz: Au Au!");
    }
}

// Classe final (folha,leaf) Poodle que herda de Cachorro
public final class Poodle extends Cachorro {

    public Poodle(String nome, int idade) {
        super(nome, idade); // Invoca construtor da classe base Cachorro
    }

    // Método específico para o poodle
    public void mostrarEstilo() {
        System.out.println("O poodle está exibindo seu estilo.");
    }

    // Sobrescrever método emitirSom da classe Cachorro
    @Override
    public void emitirSom() {
        System.out.println("O poodle faz: Au Au, com um toque de elegância!");
    }
}

public class ExemploHerancaFinal {
    public static void main(String[] args) {
        // Criar objeto Animal
        Animal a1 = new Animal("Animal Genérico", 5);
        a1.emitirSom();

        // Criar objeto Cachorro
        Cachorro c1 = new Cachorro("Rex", 3);
        c1.emitirSom();
        c1.latir();

        // Criar objeto Poodle
        Poodle p1 = new Poodle("Bella", 2);
        p1.emitirSom();
        p1.latir();
        p1.mostrarEstilo();

        // Exibir informações do Poodle
        System.out.println("Nome: " + p1.getNome());
        System.out.println("Idade: " + p1.getIdade());
    }
}

Polimorfismo

Técnica para criar métodos iguais, porém com outro comportamento de execução, permitindo que um mesmo nome represente vários comportamentos diferentes numa mesma classe e projeto.

    Tipos:
  • Sobrecarga: métodos com mesmo nome, lista de parâmetros diferentes (geralmente na mesma classe);
  • Sobrescrita: métodos com mesmo nome e lista de parâmetros, conteúdo interno diferente na subclasse.

Exemplo 1:


class Animal {
    protected String nome;

    public Animal(String nome) {
        this.nome = nome;
    }

    public String getNome() {return nome;}

    // Método emitir som (será sobrescrito nas subclasses)
    public void emitirSom() {
        System.out.println("O animal emite um som.");
    }

    // Sobrecarga de métodos - diferentes parâmetros
    public void emitirSom(String som) {
        System.out.println("O animal faz: " + som);
    }
}

class Cachorro extends Animal {

    public Cachorro(String nome) {
        super(nome);
    }

    // Sobrescrever método emitirSom da classe base Animal
    @Override
    public void emitirSom() {
        System.out.println("O cachorro faz: Au Au!");
    }
}

class Gato extends Animal {

    public Gato(String nome) {
        super(nome);
    }

    // Sobrescrever método emitirSom da classe base Animal
    @Override
    public void emitirSom() {
        System.out.println("O gato faz: Miau!");
    }
}

public class ExemploPolimorfismo {
    public static void main(String[] args) {
        Animal animal = new Animal("Animal Genérico");
        Cachorro cachorro = new Cachorro("Rex");
        Gato gato = new Gato("Mimi");

        // Sobrescrita de métodos
        animal.emitirSom(); // Invocar método da classe base Animal
        cachorro.emitirSom(); // Invocar método sobrescrito da classe Cachorro
        gato.emitirSom(); // Invocar método sobrescrito da classe Gato

        // Sobrecarga de métodos
        animal.emitirSom("Roar"); // Invocar método sobrecarregado da classe base

        // Usando polimorfismo com um array de animais
        Animal[] animais = {animal, cachorro, gato};
        for (Animal a : animais) {
            System.out.println("Nome: " + a.getNome());
            a.emitirSom(); // Invocar método apropriado baseado no tipo do objeto
        }
    }
}

Exemplo 2:

O código Java abaixo, informa mecanismo de ligação tardia entende, onde p1 é instância (criação) de Brasileiro, mesmo sendo "armazenado" dentro de Pessoa.


public class Main {
    public static void main (String[] args) {
        Pessoa p = new Pessoa("Ana");
        Brasileiro b = new Brasileiro("Bob");
        Cachorro c = new Cachorro();
        Pessoa p1 = new Brasileiro("Rick");
        b.setPet(c);
    }
}   
public class Cachorro {
    private String nome;
    public void setNome(String n) {this.nome = n;}
}   
public class Pessoa {
    private String nome;
    public Pessoa(String nome) {this.nome = nome;}
    public void comer() {QUADRADO}
    public boolean comer(float qtde) {return false; BOLA}
}
public class Brasileiro extends Pessoa {
    private Cachorro pet;
    public Brasileiro(String  nome) {super(nome);}
    public void comer() {TRIÂNGULO}
    public void setPet(Cachorro c) {this.pet = c;}
}

p1.comer(); //resultado TRIÂNGULO
p.comer(); //resultado QUADRADO
p1.comer(0.5); //resultado BOLA
b.comer(); //resultado TRIÂNGULO

Sobreposição:

Conceito geral, significa clonar itens de mesmo valor e atribuir incremento extra ao item clonado.


Dependência

Relacionamento fraco entre 2 itens, um independente e um dependente, onde mudança no independente poderá afetar o dependente. Onde objeto manipula, em seus métodos, outro objeto (de outra classe), mas não tem qualquer outro relacionamento com a mesma. Relacionamento utilizado quando deseja-se representar utilização de uma classe em outra, como nos casos de parâmetros de métodos e uso de código nos métodos.
Ex1: classe Cliente depende de classe Fornecedor, onde Cliente não tem conhecimento semântico de Fornecedor;
Ex2: addNf() onde somente é possível criar objeto nf na classe Canil, porque Canil depende da classe NotaFiscal para instanciar as mesmas, mas não há qualquer outro relacionamento entre as classes NotaFiscal e Canil.

Dependência é representada, em UML, através de linha tracejada e, em uma das extremidades, uma seta aberta apontando para classe independente. Dependência pode, opcionalmente, possuir título de relacionamento, denominado estereótipo (stereotype), representado por '<<nomeStereotype>>'. Dependência pode ser de uso (usage, use, '<<use>>'), onde elemento todo é dependente de outro, para que realize sua implementação ou operação de forma completa.
No exemplo abaixo, Autor é dependente de Caneta, onde Autor usa Caneta e depende da mesma.



import java.util.ArrayList;
import java.util.List;

// Classe Produto (independente)
class Produto {
    private String nome;
    private double preco;

    public Produto(String nome, double preco) {
        this.nome = nome;
        this.preco = preco;
    }

    public String getNome() {return nome;}
    public double getPreco() {return preco;}
}

// Classe Pedido (dependente de Produto)
class Pedido {
    private List<Produto> produtos;

    public Pedido() {
        this.produtos = new ArrayList<>();
    }

    // Método para adicionar produto ao pedido
    public void adicionarProduto(Produto produto) {
        produtos.add(produto);
    }

    // Método para calcular total do pedido
    public double calcularTotal() {
        double total = 0.0;
        for (Produto produto : produtos) {
            total += produto.getPreco();
        }
        return total;
    }

    // Método para exibir produtos do pedido
    public void exibirPedido() {
        System.out.println("Produtos no pedido:");
        for (Produto produto : produtos) {
            System.out.println("- " + produto.getNome() + ": R$ " + produto.getPreco());
        }
    }
}

public class ExemploDependencia {
    public static void main(String[] args) {
        Produto prod1 = new Produto("Camiseta", 49.90);
        Produto prod2 = new Produto("Calça", 89.90);
        Produto prod3 = new Produto("Tênis", 129.90);
        Pedido ped = new Pedido();

        // Adicionar produtos ao pedido
        ped.adicionarProduto(prod1);
        ped.adicionarProduto(prod2);
        ped.adicionarProduto(prod3);

        // Exibir produtos do pedido
        ped.exibirPedido();

        // Calcular e exibir total do pedido
        double total = ped.calcularTotal();
        System.out.println("Total do pedido: R$ " + total);
    }
}

Classe associativa

Relação (fraca em fase de análise (classe associativa), forte em fase de projeto (associação)) geralmente utilizada para representar intermédio em associação com multiplicidade muitos-para-muitos (0..* ou 1..* ou n..n) entre 2 outras classes. Classe derivada de associação para qual seja necessário expressar propriedades em intermédio à relação. O nome da relação em classe associativa precisa ser igual ao nome da classe associativa. Classes associativas geralmente são utilizadas somente na fase de análise do projeto, onde são substituídas por outra classe em intermédio, relacionada com as demais via associação. Classe associativa é representada, em UML, por classe em intermédio entre associação de classes, vinculada à linha contínua de associação, por meio de linha tracejada, possuindo nome da associação igual ao nome da classe associativa de intermédio. Para a fase de projeto, a multiplicidade que está na classe esquerda (em fase de análise) vai para multiplicidade no lado direito da classe gerada por associação, e multiplicidade que está na classe direita (em fase de análise) vai para multiplicidade no lado esquerdo da classe gerada por associação. As multiplicidades das classes esquerda e direita tornam-se 1.
No exemplo 1 abaixo, associação entre classes Autor e Livro gera classe associativa Publicacao. Pois, para autor publicar um livro, é necessário ocorrer por meio de publicação, apresentando dados como editora e data da publicação. No exemplo 2 abaixo, tem-se o mesmo exemplo 1, porém aplicado à fase de projeto, onde a classe associativa torna-se classe de intermédio entre associação das envolvidas. Nesse exemplo, Publicacao possui 1 ou mais versões, publicadas pelo Autor, e Livro possui 1 publicação da obra.




import java.util.ArrayList;
import java.util.List;

// Classe Livro
class Livro {
    private String titulo;
    private String isbn;

    public Livro(String titulo, String isbn) {
        this.titulo = titulo;
        this.isbn = isbn;
    }

    public String getTitulo() {return titulo;}
    public String getIsbn() {return isbn;}
}

// Classe Autor
class Autor {
    private String nome;
    private String biografia;

    public Autor(String nome, String biografia) {
        this.nome = nome;
        this.biografia = biografia;
    }

    public String getNome() {return nome;}
    public String getBiografia() {return biografia;}
}

// Classe associativa LivroAutor (Livro pode ter vários Autores, e Autor pode ter vários Livros)
class LivroAutor {
    private Livro livro;
    private Autor autor;
    private String papel; // Papel do autor no livro (ex: "Escritor", "Editor")

    public LivroAutor(Livro livro, Autor autor, String papel) {
        this.livro = livro;
        this.autor = autor;
        this.papel = papel;
    }

    public Livro getLivro() {return livro;}
    public Autor getAutor() {return autor;}
    public String getPapel() {return papel;}
}

public class ExemploClasseAssociativa {
    public static void main(String[] args) {
        Livro livro1 = new Livro("Java Programming", "123-456-789");
        Livro livro2 = new Livro("Advanced Java", "987-654-321");
        Autor autor1 = new Autor("John Doe", "Experienced Java developer.");
        Autor autor2 = new Autor("Jane Smith", "Java enthusiast and writer.");

        LivroAutor associacao1 = new LivroAutor(livro1, autor1, "Escritor");
        LivroAutor associacao2 = new LivroAutor(livro1, autor2, "Co-escritor");
        LivroAutor associacao3 = new LivroAutor(livro2, autor1, "Editor");

        // Exibir associações
        System.out.println("Livro: " + associacao1.getLivro().getTitulo() + ", Autor: " + associacao1.getAutor().getNome() + ", Papel: " + associacao1.getPapel());
        System.out.println("Livro: " + associacao2.getLivro().getTitulo() + ", Autor: " + associacao2.getAutor().getNome() + ", Papel: " + associacao2.getPapel());
        System.out.println("Livro: " + associacao3.getLivro().getTitulo() + ", Autor: " + associacao3.getAutor().getNome() + ", Papel: " + associacao3.getPapel());
    }
}

Propriedades dos atributos

  • readOnly: também chamado de atributo final, indica que valor do atributo não pode ser modificado após valor inicial atribuído. Pode ser considerado atributo "constante";
    
    Exemplo UML:
    classe Biblioteca
    - livro : String = "UB Social" {readOnly}
    
    Exemplo Java:
    public class Biblioteca {
        private final String livro = "UB Social";
    }
    
  • changeable: atributo modificável, sem restrições para modificações. Por padrão, um atributo é sempre changeable;
  • addOnly: válido somente para atributos com multiplicidade maior que 1, onde valor do atributo não pode ser alterado ou removido;
  • union: frequentemente utilizado para indicar que atributo é união derivada de outro conjunto de atributos;
  • redefines <attribute-name>: atributo atua como 'alias' (apelido, atalho) de outro atributo. Pode ser utilizado para indicar que subclasse possui atributo atalho para atributo na superclasse.

Propriedades dos métodos

  • leaf: folha, indica que método não possuirá redefinição. Também chamado de método final, que pode ser herdado, mas não sobrescrito (modificado) nas subclasses;
  • isQuery: método 'puro', ou seja, não altera estado do sistema;
  • sequential: invocadores do método devem coordenar externamente o objeto, garantindo que exista fluxo único no objeto por vez;
  • guarded: método garante que várias chamadas serão tratadas como chamadas sequenciais;
  • concurrent: método considerado atômico e permite que seja executado concorrentemente com outros métodos.

class Animal {
    protected String nome;

    public Animal(String nome) {
        this.nome = nome;
    }

    public String getNome() {return nome;}

    // Método final que não pode ser sobrescrito
    public final void dormir() {
        System.out.println(nome + " está dormindo.");
    }

    // Método que pode ser sobrescrito
    public void emitirSom() {
        System.out.println(nome + " está fazendo um som.");
    }
}

class Cachorro extends Animal {
    public Cachorro(String nome) {
        super(nome);
    }

    // Sobrescrevendo método emitirSom da classe base Animal
    @Override
    public void emitirSom() {
        System.out.println(nome + " está fazendo: Au Au!");
    }
}

public class ExemploMetodoFinal {
    public static void main(String[] args) {
        Cachorro cachorro = new Cachorro("Rex");

        // Invocar métodos dormir (final) e emitirSom (sobrescrito)
        cachorro.dormir();  // Invocar método final da classe base Animal
        cachorro.emitirSom(); // Invocar método sobrescrito da classe Cachorro
    }
}

Restrições

Define limites para objetos, classes, atributos, relacionamentos e associações. Restrição é especificada entre '{}' próximo ao elemento restrito, ou pode ser especificada como comentário do elemento. Restrições servem para limitar e realizar consistência dos elementos podendo tornar-se base para asserções (pré e/ou pós condições) em programação. Em UML, comentário é uma Note vinculada a classe via relação NoteAnchor. Restrições na generalização:

  • complete: não há mais nenhuma subclasse a especificar;
  • incomplete: existem outras subclasses a especificar;
  • disjoint: objeto da superclasse só pode ser objeto de subclasse;
  • overlapping: objeto da superclasse pode ser objeto de mais de 1 subclasse ao mesmo tempo.

No exemplo abaixo, complete indica que não há mais nenhum tipo de usuário de livro, e overlapping indica que um usuário de livro pode ser escritor e leitor ao mesmo tempo.



interface Pessoa {
    String getNome();
}

interface Estudante extends Pessoa {
    String getCurso();
}

interface Funcionario extends Pessoa {
    String getCargo();
}

// Classe que implementa todas interfaces
class PessoaEstudanteFuncionario implements Estudante, Funcionario {
    private String nome;
    private String curso;
    private String cargo;

    public PessoaEstudanteFuncionario(String nome, String curso, String cargo) {
        this.nome = nome;
        this.curso = curso;
        this.cargo = cargo;
    }

    // Implementação do método getNome da interface Pessoa
    @Override
    public String getNome() {return nome;}

    // Implementação do método getCurso da interface Estudante
    @Override
    public String getCurso() {return curso;}

    // Implementação do método getCargo da interface Funcionario
    @Override
    public String getCargo() {return cargo;}
}

public class ExemploOverlapping {
    public static void main(String[] args) {
        PessoaEstudanteFuncionario pessoa = new PessoaEstudanteFuncionario("Ana", "Engenharia", "Estagiária");

        // Usar instância como Pessoa
        System.out.println("Nome: " + pessoa.getNome());

        // Usar instância como Estudante
        System.out.println("Curso: " + pessoa.getCurso());

        // Usar instância como Funcionario
        System.out.println("Cargo: " + pessoa.getCargo());
    }
}

Classe abstrata

Classe concreta possui instâncias (objetos). Classe abstrata não possui instâncias. É utilizada na construção de hierarquia de relacionamentos de generalização. Pode, ou não, ter métodos concretos (padrão) ou abstratos. Se tiver métodos abstratos, obrigatoriamente terá de ser abstrata. Os métodos abstratos devem ser implementados nas subclasses concretas da classe abstrata (criado na classe abstrata, mas implementado nas subclasses abstratas concretas). Métodos abstratos, em UML, são identificados em itálico. Método abstrato possui, na superclasse, somente assinatura, e nas subclasses (@override) será informado conteúdo interno. Métodos construtores de classes abstratas não podem ser abstratos. Classe abstrata é representada, em UML, com o nome em itálico, ou com nome normal seguido de estereótipo '<<abstract>>' acima do nome.

No exemplo abaixo, Figura e Retangulo são abstratas, e classe Botao é folha. Método 'mostrar()' na classe Figura é método abstrato, implementado na subclasse concreta e folha Botao.


colocar código método abstrato JAVA


// Classe abstrata Animal
abstract class Animal {
    protected String nome;

    public Animal(String nome) {
        this.nome = nome;
    }

    // Método abstrato que deve ser implementado pelas subclasses concretas
    public abstract void emitirSom();

    // Método concreto
    public void dormir() {
        System.out.println(nome + " está dormindo.");
    }

    public String getNome() {return nome;}
}

class Cachorro extends Animal {
    public Cachorro(String nome) {
        super(nome);
    }

    // Implementação do método abstrato emitirSom
    @Override
    public void emitirSom() {
        System.out.println(nome + " está fazendo: Au Au!");
    }
}

class Gato extends Animal {
    public Gato(String nome) {
        super(nome);
    }

    // Implementação do método abstrato emitirSom
    @Override
    public void emitirSom() {
        System.out.println(nome + " está fazendo: Miau!");
    }
}

public class ExemploClasseAbstrata {
    public static void main(String[] args) {
        Animal cachorro = new Cachorro("Rex");
        Animal gato = new Gato("Mimi");

        cachorro.emitirSom();
        cachorro.dormir();

        gato.emitirSom();
        gato.dormir();
    }
}

Interface

Estereótipo interface é classe que define conjunto de métodos sem implementação (apenas assinatura). É o contato com o mundo exterior (via abstração), que define o que pode ser feito com um objeto dessa classe. Na interface, todo métodos são public e abstract, que não possuem conteúdo interno (somente assinatura), pelas quais todas as classes que implementarão (implements) 1 ou mais interfaces, terão de carregar (@override) todos métodos abstratos informados (implements). Não é obrigatório informar "abstract" nos métodos na interface, porque já entende-se que os mesmos nela são abstratos por padrão. Os métodos abstratos podem ter valor de retorno e parâmetros. A partir do Java 8, interfaces também podem conter métodos concretos default (com implementação) e métodos estáticos. A parir do Java 9, interfaces podem conter métodos privados. Também na interface, todos os atributos são atributos de classe (constante) e readOnly, sendo static e final. Interface não possui método construtor. Classe herdeira pode herdar (extends), simultaneamente, somente uma classe, abstrata ou não (Java não suporta herança múltipla de classes). Uma classe herdeira pode implementar (implements) várias interfaces simultaneamente.

Em UML, interface é representada pelo estereótipo '<<interface>>' (normal interface) acima do nome da classe (boas práticas sugerem nome da interface com 'I' na 1ª letra, como 'IPainel'). Da mesma forma, todos seus métodos são representados em itálico (métodos abstract). Alternativamente, em UML, interface pode ser representada por círculo fechado não preenchido, ao invés de quadrado padrão de classe.
Ex 1: Painel é interface, Botao (classe concreta) é implementação de Painel, então Botao é um tipo de Painel.

Realização (realization - realize) é relacionamento fraco onde classe concretiza ou implementa o comportamento de outro. É relacionamento entre interface e classe. Classe realiza comportamento de interface quando ocorre a implementação da mesma na primeira, permitindo assim, relacionamento "é um tipo de".

Em UML, quando a interface encontra-se como classe estereotipada, relacionamento realização é representado por linha tracejada, onde uma das extremidades possui seta fechada não preenchida, apontando para a interface. Ou, alternativamente, quando interface encontra-se como círculo fechado não preenchido, realização é representado como linha contínua, sem identificação nas extremidades (associação).

Provided interface (interface fornecida, provida) é a interface implementada por uma classe Provider, que será conectada, via associação, à required interface (interface requerida, necessária, obrigatória) de uma classe Consumer, para que possa executar um método com interface provida pela Provider. Em UML, provider interface é a ligação, via associação, entre classe Consumer (em uma extremidade da linha) e gancho/garfo para conexão com interface fornecida (círculo fechado não preenchido), conectada, via associação, à classe Provider (provided interface). Em síntese, Consumer requer (required) interface provida (provided) pela Provider (conforme exemplo abaixo).
No exemplo abaixo, classe Consumer requer acesso à interface, e Provider providencia acesso à interface. Nesse caso, Consumer possui método com interface como parâmetro, onde será necessário (required, à esquerda) acesso à interface, provida (provided, à direita) pela Provided.



Exemplo 1:


public interface OperacoesMatematicas {
    // Constante (atributo final estático)
    double PI = 3.141592653589793;

    // Método abstrato (deve ser implementado pelas classes concretas)
    double calcular(double a, double b);

    // Método default (com implementação)
    default double calcularCircunferencia(double raio) {
        return 2 * PI * raio;
    }

    // Método estático (com implementação)
    static double calcularAreaCirculo(double raio) {
        return PI * raio * raio;
    }
}

public class Soma implements OperacoesMatematicas {
    // Implementação do método abstrato calcular
    @Override
    public double calcular(double a, double b) {
        return a + b;
    }
}

public class Multiplicacao implements OperacoesMatematicas {
    // Implementação do método abstrato calcular
    @Override
    public double calcular(double a, double b) {
        return a * b;
    }
}

public class TestaOperacoesMatematicas {
    public static void main(String[] args) {
        OperacoesMatematicas soma = new Soma();
        OperacoesMatematicas multiplicacao = new Multiplicacao();

        // Utilizar métodos abstratos
        System.out.println("Soma: " + soma.calcular(5, 3)); // Output: Soma: 8.0
        System.out.println("Multiplicação: " + multiplicacao.calcular(5, 3)); // Output: Multiplicação: 15.0

        // Utilizar método default
        System.out.println("Circunferência: " + soma.calcularCircunferencia(3)); // Output: Circunferência: 18.84955592153876

        // Utilizar método estático
        System.out.println("Área do círculo: " + OperacoesMatematicas.calcularAreaCirculo(3)); // Output: Área do círculo: 28.274333882308138
    }
}

Exemplo 2:


public interface OperacoesMatematicas {
    double calcular(double a, double b);
}

public interface OperacoesGeometricas {
    double calcularArea(double raio);
    double calcularPerimetro(double lado);
}

public class CalculadoraAvancada implements OperacoesMatematicas, OperacoesGeometricas {
    // Implementação do método calcular da interface OperacoesMatematicas
    @Override
    public double calcular(double a, double b) {
        return a + b;
    }

    // Implementação do método calcularArea da interface OperacoesGeometricas
    @Override
    public double calcularArea(double raio) {
        return Math.PI * raio * raio;
    }

    // Implementação do método calcularPerimetro da interface OperacoesGeometricas
    @Override
    public double calcularPerimetro(double lado) {
        return 4 * lado;
    }

    // Método adicional para exibir os resultados
    public void exibirResultados(double a, double b, double raio, double lado) {
        System.out.println("Soma: " + calcular(a, b));
        System.out.println("Área do círculo: " + calcularArea(raio));
        System.out.println("Perímetro do quadrado: " + calcularPerimetro(lado));
    }
}

public class TestaCalculadoraAvancada {
    public static void main(String[] args) {
        CalculadoraAvancada calculadora = new CalculadoraAvancada();

        double a = 5.0;
        double b = 3.0;
        double raio = 2.0;
        double lado = 4.0;

        calculadora.exibirResultados(a, b, raio, lado);
    }
}

Exemplo 3:


// Interface fornecida (provided interface)
public interface Service {
    void execute();
}

// Interface requerida (required interface)
public interface Logger {
    void log(String message);
}

// Implementação da interface fornecida (provided interface)
public class ServiceImpl implements Service {
    private Logger logger; // Interface requerida

    // Injeção da interface requerida via construtor
    public ServiceImpl(Logger logger) {
        this.logger = logger;
    }

    @Override
    public void execute() {
        logger.log("Service is being executed.");
        System.out.println("Service execution logic here.");
    }
}

// Implementação da interface requerida (required interface)
public class ConsoleLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("Log: " + message);
    }
}

public class Main {
    public static void main(String[] args) {
        // Criar instância da implementação da interface requerida
        Logger logger = new ConsoleLogger();

        // Passar instância da interface requerida para a implementação da interface fornecida
        Service service = new ServiceImpl(logger);

        // Executar serviço
        service.execute();
    }
}

Fronteira

Fronteira/limite (boundary) representa objeto que realiza comunicação entre sistema e mundo externo (outros sistemas, atores, etc). Modela interfaces do sistema com o ambiente externo, sendo dependente do ambiente do sistema (exemplos: AdministradoraCartao, SistemaPonto, Catraca, SensorPresenca). Geralmente vinculado a outra classe via associação ou composição. Atua como interface de entrada/saída do sistema. Em UML, boundary pode ser representado por círculo fechado, não preenchido, com linhas congruentes anexadas à esquerda. Também pode ser representado por classe com estereótipo <<boundary>>. Control é classe que coordena comportamento no sistema (contêm lógica de controle para coordenar operações entre outras classes (boundary e entity)), na execução de processos, fluxos e orquestram outras classes. Cria, ativa, anula e controla a concorrência aos objetos controlados. Classes de controle geralmente são criadas quando o sistema executa um caso de uso e seu ciclo de vida tem duração até que tal caso seja concluído. Em UML, control é representado por cículo fechado, não preenchido, com sinal de menor (<) sob a linha na parte superior do círculo. Também pode ser representado por classe com estereótipo <<control>>. Boundary e control trabalham juntas com classes entity para formar sistema coeso, separando claramente responsabilidades de interação com usuário, controle de fluxo e manipulação de dados.



// Entity (classe entidade)
// Representa conta bancária com métodos para depositar, sacar e consultar saldo
public class Account {
    private String accountNumber;
    private double balance;

    public Account(String accountNumber) {
        this.accountNumber = accountNumber;
        this.balance = 0.0;
    }

    public void deposit(double amount) {
        balance += amount;
    }

    public void withdraw(double amount) {
        if (amount <= balance) {
            balance -= amount;
        }
    }

    public double getBalance() {return balance;}
    public String getAccountNumber() {return accountNumber;}
}

// Control (classe controle)
// Gerencia lógica de controle, coordenando criação de contas e consulta de saldos
public class AccountController {
    private Account account;

    public void createAccount(String accountNumber) {
        account = new Account(accountNumber);
    }

    public double getBalance() {
        if (account != null) {
            return account.getBalance();
        }
        return 0.0;
    }

    // Métodos adicionais para depositar e sacar podem ser adicionados aqui
}

// Boundary (classe fronteira/limite)
// Interage com usuário, obtendo entradas e exibindo saídas
import java.util.Scanner;
public class AccountUI {
    private AccountController controller;
    private Scanner scanner;

    public AccountUI() {
        controller = new AccountController();
        scanner = new Scanner(System.in);
    }

    public void createAccount() {
        System.out.print("Enter account number: ");
        String accountNumber = scanner.nextLine();
        controller.createAccount(accountNumber);
        System.out.println("Account created successfully!");
    }

    public void displayBalance() {
        double balance = controller.getBalance();
        System.out.println("Current balance: " + balance);
    }

    public static void main(String[] args) {
        AccountUI ui = new AccountUI();
        ui.createAccount();
        ui.displayBalance();
    }
}

Classe parametrizada

Também conhecida como template (modelo) ou classe template. Classe template é usada como conjunto modelo para criar elementos mais específicos a partir dessa.
Ex 1: classe template EstanteLivro serve de classe modelo para criação de classe MinhaEstanteLivro, sendo essa mais específica e personalizada;
Ex 2: código Java, lista meusLivros é usada como modelo para criar objetos (Livros) mais específicos:


public ArrayList<Livro> meusLivros = new ArrayList<Livro>();

Em UML, classe template é representada por pequeno quadrado tracejado no canto superior direito da classe, indicando template ou lista dos parâmetros. O relacionamento fraco entre classe template e classe concreta (nesse caso chamada 'bound element' (elemento ligado)) é representado explicitamente por linha tracejada com seta aberta em uma das extremidades, apontando para a classe template. Tal relação é nomeada pelo estereótipo '<<bind>>', juntamente com indicação do parâmetro para a classe template (template biding), em que o biding aponta seta para o objeto que lhe substituirá. Na classe concreta relacionada, geralmente não são apresentados atributos e métodos, pois não pode haver qualquer alteração no bound element (apenas indica-se o(s) parâmetro(s)). Na forma explícita, o quadrado tracejado representando template, pode possuir nome, tipo, valor padrão e tipo modificador (ex: 'T:boolean=true'). Na forma implícita, o bound element precisa ter mesmo nome da classe template, e a indicação do(s) parâmetro(s) - classe - é feita ao lado do nome do bound element, entre sinais de menor e maior.
O exemplo 1 abaixo (à esquerda) mostra notação com vinculação explícita.
O exemplo 2 abaixo (à direita) mostra notação com vinculação implícita.
Os exemplos 3 abaixo representam outras formas práticas de classe parametrizada.

Em Java, classes parametrizadas são denominadas Generics. Generics permitem criação de classes, interfaces e métodos que funcionam com qualquer tipo de dado, proporcionando maior flexibilidade e reusabilidade ao código.




public class Caixa<T> {
    // Variável de instância do tipo genérico T
    private T item;

    public Caixa(T item) {
        this.item = item;
    }

    public T getItem() {return item;}
    public void setItem(T item) {this.item = item;}

    public void exibirItem() {
        System.out.println("Item: " + item);
    }
}

public class TestaCaixa {
    public static void main(String[] args) {
        // Criar instância de Caixa para armazenar Integer
        Caixa<Integer> caixaInteger = new Caixa<>(123);
        caixaInteger.exibirItem();

        // Criar instância de Caixa para armazenar String
        Caixa<String> caixaString = new Caixa<>("Olá Mundo");
        caixaString.exibirItem();

        // Criar instância de Caixa para armazenar Double
        Caixa<Double> caixaDouble = new Caixa<>(3.14);
        caixaDouble.exibirItem();

        caixaString.setItem("Nova String");
        System.out.println("Item atualizado: " + caixaString.getItem());
    }
}

Package

Pacote ou sub-pacote (sub-package) do projeto, contendo entidades, pacotes e outros componentes do projeto. Pacotes são relacionados via relacionamento fraco de dependência. Um componente pode relacionar-se diretamente com pacote, através de nesting, cujo círculo do relacionamento apontará para o pacote (todo). Pacote pode herdar de outro (generalização).
O exemplo abaixo indica que pacote Financas é dependete do pacote Pedidos, pois é necessário ter conhecimento dos pedidos, para gestão das finanças.



package com.empresa.produto;
import com.empresa.util.Utilitario; // Importar classe do outro pacote

public class Produto {
    private String nome;
    private double preco;

    public Produto(String nome, double preco) {
        this.nome = nome;
        this.preco = preco;
    }

    // Método para calcular preço com desconto usando uma classe do outro pacote
    public double calcularPrecoComDesconto(double descontoPercentual) {
        // Utilizar método da classe do pacote com.empresa.util
        double desconto = Utilitario.calcularDesconto(preco, descontoPercentual);
        return preco - desconto;
    }
}
------------------------------
package com.empresa.util;

public class Utilitario {
    // Método estático para calcular desconto
    public static double calcularDesconto(double valor, double percentual) {
        return valor * (percentual / 100);
    }
}

Subsystem

Subsistema dentro do sistema macro, contendo entidades, pacotes, subsistemas e outros componentes do projeto. Subsistemas são relacionados via relacionamento fraco de dependência. Um componente pode relacionar-se diretamente com subsistema, através de nesting, cujo círculo do relacionamento apontará para o subsistema (todo). Subsistema pode herdar de outro (generalização).
No exemplo abaixo, subsistema SistemaGestao é dependente do subsistema SistemaCliente, pois é necessária existência do sistema com showroom e escolhas dos clientes, para alimentar informações às pesquisas de produtos e tendências de mercado. Subsystem



Subsystem de Produtos:
// Arquivo: Produto.java
package com.empresa.produtos.cadastro;

public class Produto {
    private String nome;
    private double preco;
    // Getters e setters omitidos para simplicidade
}

// Arquivo: Estoque.java
package com.empresa.produtos.estoque;

public class Estoque {
    private int quantidadeDisponivel;

    public void atualizarEstoque(int quantidade) {
        // Lógica para atualizar o estoque
    }
}

// Arquivo: Venda.java
package com.empresa.produtos.vendas;
import com.empresa.produtos.cadastro.Produto;
import com.empresa.produtos.estoque.Estoque;

public class Venda {
    public void realizarVenda(Produto produto, int quantidade) {
        // Lógica para processar venda e atualizar estoque
        Estoque estoque = new Estoque();
        estoque.atualizarEstoque(-quantidade); // Reduz quantidade do estoque após venda
    }
}

Subsistema de Clientes e Pedidos:
// Arquivo: Cliente.java
package com.empresa.clientes;

public class Cliente {
    private String nome;
    private String endereco;
    // Getters e setters omitidos para simplicidade
}

// Arquivo: Pedido.java
package com.empresa.clientes.pedidos;
import com.empresa.produtos.cadastro.Produto;

public class Pedido {
    private Cliente cliente;
    private Produto produto;
    private int quantidade;
    // Construtor, getters e setters omitidos para simplicidade
}

Classe Main:
// Arquivo: Main.java
package com.empresa;
import com.empresa.clientes.Cliente;
import com.empresa.clientes.pedidos.Pedido;
import com.empresa.produtos.cadastro.Produto;
import com.empresa.produtos.vendas.Venda;

public class Main {
    public static void main(String[] args) {
        // Exemplo de uso do subsistema de produtos
        Produto produto = new Produto();
        produto.setNome("Camiseta");
        produto.setPreco(29.99);

        Venda venda = new Venda();
        venda.realizarVenda(produto, 1);

        // Exemplo de uso do subsistema de clientes e pedidos
        Cliente cliente = new Cliente();
        cliente.setNome("João");
        cliente.setEndereco("Rua A, 123");

        Pedido pedido = new Pedido();
        pedido.setCliente(cliente);
        pedido.setProduto(produto);
        pedido.setQuantidade(2);

        // Lógica para processar pedido
        processarPedido(pedido);
    }

    private static void processarPedido(Pedido pedido) {
        // Lógica para processar pedido
    }
}

Business

BusinessEntity representa classe com dados manuseáveis persistentes no sistema, essencialmente objetos de negócio que contém informações sobre domínio do mesmo. Esses objetos podem ser comparados às entidades em banco de dados, ou objetos de domínio na aplicação. Em UML, BusinessEntity é representada por círculo entity, com linha diagonal, para direira, dentro do círculo. Possui mesmas possibilidades de relacionamentos que entity. BusinessWorker representa papéis ou atividades desempenhadas em processos de negócios, encapsulando lógica de negócios, interagindo com as BusinessEntities para realizar tarefas. BusinessWorker pode ser considerado como classe ou conjunto de operações que manipulam BusinessEntities. Em UML, BusinessWorker é representado por círculo worker, com linha diagonal, para a direita, dentro do círculo. Possui mesmas possibilidades de relacionamentos que worker.
No exemplo abaixo, Customer, Order e Product (BusinessEntities) representam dados do sistema, como informações do cliente, detalhes do pedido e informações dos produtos. OrderProcessor (BusinessWorker) contém lógica de negócios para criar e processar pedidos, interagindo com as BusinessEntities.



// BusinessEntity
public class Customer {
    private String id;
    private String name;

    public Customer(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {return id;}
    public String getName() {return name;}
}

// BusinessEntity
import java.util.List;
public class Order {
    private String orderId;
    private String customerId;
    private List<Product> productList;

    public Order(String orderId, String customerId, List<Product> productList) {
        this.orderId = orderId;
        this.customerId = customerId;
        this.productList = productList;
    }

    public String getOrderId() {return orderId;}
    public String getCustomerId() {return customerId;}
    public List<Product> getProductList() {return productList;}
    public double getTotalAmount() {return productList.stream().mapToDouble(Product::getPrice).sum();}
}

// BusinessEntity
public class Product {
    private String productId;
    private String productName;
    private double price;

    public Product(String productId, String productName, double price) {
        this.productId = productId;
        this.productName = productName;
        this.price = price;
    }

    public String getProductId() {return productId;}
    public String getProductName() {return productName;}
    public double getPrice() {return price;}
}

// BusinessWorker
import java.util.List;
public class OrderProcessor {
    public Order createOrder(String orderId, String customerId, List<Product> productList) {
        return new Order(orderId, customerId, productList);
    }

    public void processOrder(Order order) {
        // Implementar lógica para processar order
        System.out.println("Processing order for customer ID: " + order.getCustomerId());
        System.out.println("Order total amount: " + order.getTotalAmount());
    }
}

// Integração
import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
        Customer customer = new Customer("C123", "John Doe");
        Product product1 = new Product("P101", "Laptop", 1200.00);
        Product product2 = new Product("P102", "Mouse", 25.00);

        OrderProcessor orderProcessor = new OrderProcessor();
        Order order = orderProcessor.createOrder("O1001", customer.getId(), Arrays.asList(product1, product2));       
        orderProcessor.processOrder(order);
    }
}

Specification

Pode-se especificar (InstanceSpecification) instâncias (objetos) concretas de entity (classe), BusinessEntity, Boundary, Control e BusinessWorker. Em UML, Specification é representada pelo elemento visual círculo (conforme seu componente - entity pode ser representada pelo quadrado tradicional, conforme abaixo), cujo nome está sublinhado, antecedido de ':'. Possui mesmas possibilidades de relacionamentos que nos componentes nativos envolvidos na Specification.
No exemplo abaixo, ': Book' e ': LibraryMember' são InstanceSpecification (entity) das classes Book e LibraryMember, respectivamente.



// Classes
public class Book {
    private String title;
    private String author;
    private String isbn;

    public Book(String title, String author, String isbn) {
        this.title = title;
        this.author = author;
        this.isbn = isbn;
    }

    public String getTitle() {return title;}
    public String getAuthor() {return author;}
    public String getIsbn() {return isbn;}
}

public class LibraryMember {
    private String name;
    private String memberId;

    public LibraryMember(String name, String memberId) {
        this.name = name;
        this.memberId = memberId;
    }

    public String getName() {return name;}
    public String getMemberId() {return memberId;}
}

// InstanceSpecifications
public class Main {
    public static void main(String[] args) {
        Book book = new Book("1984", "George Orwell", "123456789");
        LibraryMember member = new LibraryMember("Alice", "M001");

        System.out.println("Book Title: " + book.getTitle());
        System.out.println("Book Author: " + book.getAuthor());
        System.out.println("Book ISBN: " + book.getIsbn());
        
        System.out.println("Member Name: " + member.getName());
        System.out.println("Member ID: " + member.getMemberId());
    }
}

Java


public static void main(String[] args) {}
  • public: modificador de acesso, visibilidade pública do método (método pode ser acessado de qualquer outra classe);
  • static: modificador da classe, método estático (de classe), não método de instância. Permite invocar método, sem necessidade de criar instâncias (objetos);
  • void: tipo de retorno, método não retorna nenhum valor;
  • main: nome do método, 'main' é nome padrão do método de entrada em programa Java, para início da JVM;
  • String[] args: args é parâmetro do método, sendo array de objetos tipo classe String. Utilizado para receber argumentos de linha de comando quando programa Java é executado, onde cada elemento do arrays 'args' é um argumento passado na linha de comando;
  • {}: corpo do método, delimitando bloco de código que compõe o método main. O código entre {} será executado quando método main for invocado, ou seja, quando o programa Java for iniciado pela JVM.

Elaborado por Mateus Schwede
ubsocial.github.io