Agregar responsabilidades a um objeto dinamicamente.
Uma empresa realiza um serviço de monitoramento em equipamentos para detectar falhas antes que elas aconteçam. Sensores são instalados nos equipamentos para monitorar características como temperatura ou pressão, sendo esses dados enviados para o sistema da empresa. Entretanto, dependendo do equipamento e de sua complexidade, a detecção de falhas depende de condições lógicas complexas que envolvem muitas comparações nas leituras de diversos sensores. Por exemplo, dados os sensores X, Y e Z, uma condição para uma falha pode ser composta da seguinte forma:
((X > 5) OR (X < 20)) AND (Y = 100) AND ((Z < 5) OR (Z > 2))
Por isso, uma forma mais simples de compor essas condições deve ser implementada, tornando mais fácil definir um monitoramento que detecta uma falha. A interface Monitoring representa um monitoramento e define a operação evaluateCondition, que avalia a condição proposta pelo monitoramento, verificando se ocorreu uma falha. A classe abstrata SimpleMonitoring representa o monitoramento de um único sensor e armazena o valor usado na comparação, além de possuir uma referência para um objeto do tipo Sensor, que armazena o valor real da leitura de um sensor. As classes concretas LessThanMonitoring, GreaterThanMonitoring e EqualsToMonitoring representam os comparadores que serão aplicados ao executar a operação evaluateCondition para verificar se o valor lido pelos sensores está dentro da condição para uma falha. A classe abstrata MonitoringDecorator referencia um objeto do tipo Monitoring, podendo este ser tanto um SimpleMonitoring quanto um MonitoringDecorator. A classe abstrata BinaryOperatorDecorator referencia um segundo objeto do tipo Monitoring e é quem torna possível compor diversos objetos do tipo Monitoring indefinidamente, encapsulando conjuntos de monitoramentos em operações binárias. Ambas as classes também definem a operação evaluateCondition, que verifica o resultado da condição para o objeto do tipo Monitoring referenciado. As subclasses concretas AndDecorator e OrDecorator verificam o resultado das respectivas operações binárias entre os resultados de evaluateCondition para os objeto referenciados por MonitoringDecorator e por BinaryOperatorDecorator.
package decorator;
public class Main {
public static void main(String[] args) {
// São instanciados os sensores de pressão, temperatura e nível da água
Sensor pressure = new Sensor();
Sensor temperature = new Sensor();
Sensor waterLevel = new Sensor();
// O monitoramento do exemplo será composto da seguinte condição:
// (pressure > 60 && waterLevel < 40) || (temperature > 50 || temperature < 10)
// Para cada operação lógica da condição, um monitoramento simples é criado.
GreaterThanMonitoring pressureOver60 = new GreaterThanMonitoring(pressure, 60);
LessThanMonitoring waterLevelBelow40 = new LessThanMonitoring(waterLevel, 40);
GreaterThanMonitoring temperatureOver50 = new GreaterThanMonitoring(temperature, 50);
LessThanMonitoring temperatureBelow10 = new LessThanMonitoring(temperature, 10);
// Dois decorators são criados para encapsular os monitoramentos simples em operações binárias
AndDecorator pressureAndWaterLevelMonitoring = new AndDecorator(pressureOver60, waterLevelBelow40);
OrDecorator temperatureRangeMonitoring = new OrDecorator(temperatureOver50, temperatureBelow10);
// Um decorator é criado para encapsular os dois decorators criados em uma única operação binária
OrDecorator mainMonitoring = new OrDecorator(pressureAndWaterLevelMonitoring, temperatureRangeMonitoring);
// Sensores identificam os valores:
pressure.setValue(50);
waterLevel.setValue(45);
temperature.setValue(49);
if(mainMonitoring.evaluateCondition()) {
System.out.println("Uma falha foi detectada!");
} else {
System.out.println("Nenhuma falha foi detectada.");
}
}
}
package decorator;
public class AndDecorator extends BinaryOperatorDecorator {
public AndDecorator(Monitoring operand, Monitoring leftOperand) {
super(operand, leftOperand);
}
public boolean evaluateCondition() {
return super.evaluateCondition() && super.evaluateOperandCondition();
}
}
package decorator;
public class EqualsToMonitoring extends SimpleMonitoring {
// No construtor, as informações necessárias para o construtor da classe abstrata SimpleMonitoring
// devem ser recebidos
public EqualsToMonitoring(Sensor sensor, float value) {
super(sensor, value);
}
public boolean evaluateCondition() {
return super.getSensorValue() == super.getValue();
}
}
package decorator;
public class GreaterThanMonitoring extends SimpleMonitoring {
// No construtor, as informações necessárias para o construtor da classe abstrata SimpleMonitoring
// devem ser recebidos
public GreaterThanMonitoring(Sensor sensor, float value) {
super(sensor, value);
}
public boolean evaluateCondition() {
return super.getSensorValue() > super.getValue();
}
}
package decorator;
public abstract class MonitoringDecorator implements Monitoring{
// O componente encapsulado pelo Decorator, do tipo Monitoring
private final Monitoring component;
public MonitoringDecorator(Monitoring component) {
this.component = component;
}
public boolean evaluateCondition() {
return this.component.evaluateCondition();
}
}
package decorator;
public class Sensor {
private float value; // Valor lido pelo sensor
public float getValue() {
return this.value;
}
public void setValue(float value){
this.value = value;
}
}
package decorator;
public abstract class BinaryOperatorDecorator extends MonitoringDecorator {
// O segundo operando definido para a operação binária
private final Monitoring leftOperand;
// O construtor recebe os dois operandos, sendo um deles passado para a classe MonitoringDecorator
public BinaryOperatorDecorator(Monitoring component, Monitoring leftOperand) {
super(component);
this.leftOperand = leftOperand;
}
public boolean evaluateCondition() {
return this.leftOperand.evaluateCondition();
}
public boolean evaluateOperandCondition() {
return super.evaluateCondition();
}
}
package decorator;
public interface Monitoring {
public boolean evaluateCondition();
}
package decorator;
public class LessThanMonitoring extends SimpleMonitoring {
// No construtor, as informações necessárias para o construtor da classe abstrata SimpleMonitoring
// devem ser recebidos
public LessThanMonitoring(Sensor sensor, float value) {
super(sensor, value);
}
public boolean evaluateCondition() {
return super.getSensorValue() < super.getValue();
}
}
package decorator;
public class OrDecorator extends BinaryOperatorDecorator {
public OrDecorator(Monitoring operand, Monitoring leftOperand) {
super(operand, leftOperand);
}
public boolean evaluateCondition() {
return super.evaluateCondition() || super.evaluateOperandCondition();
}
}
package decorator;
public abstract class SimpleMonitoring implements Monitoring {
private final Sensor sensor; // Sensor monitorado
private final float value; // Valor usado na comparação
public SimpleMonitoring(Sensor sensor, float value) {
this.sensor = sensor;
this.value = value;
}
public float getValue() {
return this.value;
}
public float getSensorValue() {
return this.sensor.getValue();
}
}
Em um jogo lutadores de MMA (Mixed Martial Arts) brasileiros e japoneses utilizam diversos estilos de arte marcial (karatê, judô e jui-jitsu). Durante a luta eles podem mudar a arte utilizada nos golpes para ataque e defesa. O jogador atuando como técnico decide como configurar cada lutador de acordo com esses diferentes estilos de arte marcial.
A implementação da abstração Fighter é organizada em duas hierarquias de classes separadas, uma hierarquia básica para variação das nacionalidades dos lutadores (BrazilianFighter, JapaneseFighter) e outra hierarquia de decoradores para variação das artes marciais dos lutadores (Karate, Judo, Jiu-Jitsu). MartialArt (Decorator) é um Fighter e mantém uma referência para outro objeto Fighter (Component) e delega a execução dos golpes para esse objeto. Suas subclasses (Concrete Decorators) redefinem os métodos de golpes herdados acrescentando comportamentos extras antes e/ou depois da delegação, permitindo combinar as duas hierarquias sem ter que criar uma classe para cada possível combinação evitando, assim, uma explosão de classes.
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.padroesdr.decorator.exemplos.mma;
public class JiuJitsu extends MartialArtDecorator {
public JiuJitsu(Fighter fighter) {
super(fighter);
}
@Override
public void toAttack() {
component.toAttack();
System.out.println("Attacking like a Jujitsuka!");
}
@Override
public void defend() {
component.defend();
System.out.println("Defeding like a Jujitsuka!");
}
}
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.padroesdr.decorator.exemplos.mma;
/**
*
* @author Adriano Gonçalves
*/
public class BrazillianFighter extends FighterImpl {
@Override
public void toAttack() {
System.out.println("Beginning a Brazillian attack!");
}
@Override
public void defend() {
System.out.println("Defending a Brazillian defence!");
}
}
package com.padroesdr.decorator.exemplos.mma;
public class Principal {
public static void main(String args[]) {
System.out.println("Fighter 1:");
Fighter fighter1 = new JapaneseFighter();
fighter1 = new Karate(fighter1);
fighter1.toAttack();
fighter1.defend();
// Changing the martial art in runtime
System.out.println("Changing the Japanese martial art in runtime...");
/* The following lines aim to "undecorate" the last martial art, due to the only one martial art at time request
* from this problem.
* This approach is only for making this example simpler, for learning purposes. A desirable approach would be
* creating all the instances through the Factory pattern, and controlling the decorations and undecorations
* inside a manager class.
*/
if (fighter1 instanceof MartialArtDecorator) {
fighter1 = ((MartialArtDecorator) fighter1).getComponent();
}
fighter1 = new Judo(fighter1);
fighter1.toAttack();
fighter1.defend();
System.out.println("Fighter 2:");
Fighter fighter2 = new BrazillianFighter();
fighter2 = new JiuJitsu(fighter2);
fighter2.toAttack();
fighter2.defend();
}
}
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.padroesdr.decorator.exemplos.mma;
public abstract class FighterImpl implements Fighter {
@Override
public abstract void toAttack();
@Override
public abstract void defend();
}
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Interface.java to edit this template
*/
package com.padroesdr.decorator.exemplos.mma;
public interface Fighter {
public void toAttack();
public void defend();
}
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.padroesdr.decorator.exemplos.mma;
public abstract class MartialArtDecorator implements Fighter {
protected Fighter component;
public MartialArtDecorator(Fighter component) {
this.component = component;
}
@Override
public abstract void toAttack();
@Override
public abstract void defend();
// Houston, we've got a problem!
public Fighter getComponent() {
return this.component;
}
}
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.padroesdr.decorator.exemplos.mma;
public class MartialArtManager {
private static MartialArtManager instance;
public MartialArtManager getSoleInstance() {
if (instance == null) {
instance = new MartialArtManager();
}
return instance;
}
public static void setMartialArt(Fighter fighter) {
}
}
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.padroesdr.decorator.exemplos.mma;
public class Karate extends MartialArtDecorator {
public Karate(Fighter fighter) {
super(fighter);
}
@Override
public void toAttack() {
component.toAttack();
System.out.println("Attacking like a Karateka!");
}
@Override
public void defend() {
component.defend();
System.out.println("Defeding like a Karateka!");
}
}
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.padroesdr.decorator.exemplos.mma;
/**
*
* @author Adriano Gonçalves
*/
public class JapaneseFighter extends FighterImpl {
@Override
public void toAttack() {
System.out.println("Beginning a Japanese attack!");
}
@Override
public void defend() {
System.out.println("Defending a Japanese defence!");
}
}
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.padroesdr.decorator.exemplos.mma;
public class Judo extends MartialArtDecorator {
public Judo(Fighter fighter) {
super(fighter);
}
@Override
public void toAttack() {
component.toAttack();
System.out.println("Attacking like a Judoka!");
}
@Override
public void defend() {
component.defend();
System.out.println("Defeding like a Judoka!");
}
}