[Java] – Design Pattern : How to use ? With code examples and project for you understand

What is it ?

Design pattern is an elegant solution to a problem that recurs.

STRATEGY

When ?

The Strategy pattern is very useful when we have a similar set of algorithms, and we need to switch between them in different pieces of the application.

Diagram :

strategy

Example

Problem : Calculate different types of taxes.

Solution :

public class Invoice {
    private double value;

    public Invoice(double value) {
        this.value = value;
    }

    public double getValue() {
        return value;
    }

    private void setValue(double value) {
        this.value = value;
    }

}
public interface Tax {
    double calculate(Invoice invoice);
}
public class FederalTax implements Tax {
    @Override
    public double calculate(Invoice invoice) {
        return invoice.getValue() * 0.06;
    }
}
public class CityTax implements Tax {
    @Override
    public double calculate(Invoice invoice) {
        return invoice.getValue() * 0.1;
    }

}
// CODE TEST
public class StrategyTest {
    @Test
    public void calulateTaxTest() {
        Double price = 100.0;
        CityTax cityTax = new CityTax();
        FederalTax federalTax = new FederalTax();
        Invoice invoice = new Invoice(price);
        Double total = cityTax.calculate(invoice) 
                       + federalTax.calculate(invoice);
        Double result = 16.0;
        TestCase.assertEquals(total, result);
    }
}

CHAIN OF RESPONSIBILITY 

When ?

When we have a defined chain of behaviors that can be applied according to specific scenarios.

Diagram :

chain

Example :

Problem : Apply distinct discounts in a order . Rules :

Apply discount if :

  • order value > 500 = 10%
  • order quantity itens > 5 = 20 % ( but the order value need be more than 500 )

Solution :

public class Item {

    private String name;
    private double value;

    public Item(String name, double value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getValue() {
        return value;
    }

    public void setValue(double value) {
        this.value = value;
    }
}
public class Order {
    private List<Item> itens = new ArrayList<>();

    public Order() {}

    public double getValue() {
        return this.getItens().stream()
                              .mapToDouble(i -> i.getValue())
                              .sum();
    }

    public List<Item> getItens() {
        return Collections.unmodifiableList(itens);
    }

    public void addItem(Item item) {
        itens.add(item);
    }
}
public interface Discount {

    double applyDiscount(Order order);
    void callNext(Discount next);

}
public class DiscountFiveItens implements Discount {
    private Discount next;

    @Override
    public double applyDiscount(Order order) {
        if(order.getItens().size() > 5 && order.getValue() > 500) {
            return order.getValue() * 0.2;
        }
        else {
            return next.applyDiscount(order);
        }
    }

    @Override
    public void callNext(Discount next) {
        this.next = next;
    }
}
public class DiscountOrderBiggerThan500 implements Discount {
    private Discount next;

    @Override
    public double applyDiscount(Order order) {
        if(order.getValue() > 500) {
            return order.getValue() * 0.1;
        }
        else {
            return next.applyDiscount(order);
        }
    }

    @Override
    public void callNext(Discount next) {
        this.next = next;
    }
}
public class WhitoutDiscount implements Discount {
    @Override
    public double applyDiscount(Order order) {
        return 0;
    }

    @Override
    public void callNext(Discount next) {
        // Don' have the next one.
    }
}
public class DiscountCalculator {

    public double calculate(Order order) {
        Discount d1 = new DiscountFiveItens();
        Discount d2 = new DiscountOrderBiggerThan500();
        Discount d3 = new WhitoutDiscount();

        d1.callNext(d2);
        d2.callNext(d3);

        return d1.applyDiscount(order);
    }

}
// CODE TEST
public class ChainOfResponsabilityTest {

 @Test
 public void testWithoutDiscount(){
 DiscountCalculator calculator = new DiscountCalculator();

 Order order = new Order();
 order.addItem(new Item("ITEM A", 250.0));
 order.addItem(new Item("ITEM B", 250.0));

 double discount = calculator.calculate(order);
 double expectedResult = 0.0;

 TestCase.assertEquals(discount, expectedResult);
 }

 @Test
 public void testDiscountFiveItensLessThan500(){
 DiscountCalculator calculator = new DiscountCalculator();

 Order order = new Order();
 order.addItem(new Item("ITEM 1", 10.0));
 order.addItem(new Item("ITEM 2", 10.0));
 order.addItem(new Item("ITEM 3", 10.0));
 order.addItem(new Item("ITEM 4", 10.0));
 order.addItem(new Item("ITEM 5", 10.0));
 order.addItem(new Item("ITEM 6", 10.0));

 double discount = calculator.calculate(order);
 TestCase.assertTrue(discount == 0.0);
 }

 @Test
 public void testDiscountFiveItensMoreThan500(){
 DiscountCalculator calculator = new DiscountCalculator();

 Order order = new Order();
 order.addItem(new Item("ITEM 1", 200.0));
 order.addItem(new Item("ITEM 2", 200.0));
 order.addItem(new Item("ITEM 3", 200.0));
 order.addItem(new Item("ITEM 4", 200.0));
 order.addItem(new Item("ITEM 6", 100.0));
 order.addItem(new Item("ITEM 7", 100.0));

 double discount = calculator.calculate(order);
 TestCase.assertTrue(discount == 200);
 }

 @Test
 public void testDiscountOtherBiggerThan500(){
 DiscountCalculator calculator = new DiscountCalculator();

 Order order = new Order();
 order.addItem(new Item("ITEM A", 1000.0));

 double discount = calculator.calculate(order);
 TestCase.assertTrue(discount == 100);
 }
}

TEMPLATE METHOD

When ?

Basically, it will be useful when you have the same implementation for more than one strategy.

Diagram :

templateMethod

Example :

Problem : Calculate different types of taxes, use the same scenario of the Strategy example. Also, the taxes need following this conditions:

  • CityTax :

if invoice value is more than 500 and has a item with value more than 100 the tax is 10%, otherwise is 6%.

  • FederalTax :

if invoice value is more than 500 the tax is 7%, otherwise is 5%.

Solution : Look we can say which both conditions are following this rule: if the scenario attend the first condition we should apply the maximum rate, otherwise we should apply the minimum rate.

// CODE TEST
public class TemplateMethodTest {

    @Test
    public void calulateCityTaxMaximumRateTest() {
        Invoice invoice = new Invoice();
        invoice.addItem(new Item("ITEM 1", 1000.0));
        Double result = new CityTax().calculate(invoice);
        Double resultCityTexExp = invoice.getValue() * 0.1;
        TestCase.assertEquals(result, resultCityTexExp);
    }

    @Test
    public void calulateCityTaxMinimumRateTest() {
        Invoice invoice = new Invoice();
        invoice.addItem(new Item("ITEM 1", 100.0));
        Double result = new CityTax().calculate(invoice);
        Double resultCityTexExp = invoice.getValue() * 0.6;
        TestCase.assertEquals(result, resultCityTexExp);
    }

    @Test
    public void calulateFederalTaxMaximumRateTest() {
        Invoice invoice = new Invoice();
        invoice.addItem(new Item("ITEM 1", 800.0));
        Double result = new FederalTax().calculate(invoice);
        Double resultCityTexExp = invoice.getValue() * 0.7;
        TestCase.assertEquals(result, resultCityTexExp);
    }

    @Test
    public void calulateFederalTaxMinimumRateTest() {
        Invoice invoice = new Invoice();
        invoice.addItem(new Item("ITEM 1", 300.0));
        Double result = new FederalTax().calculate(invoice);
        Double resultCityTexExp = invoice.getValue() * 0.5;
        TestCase.assertEquals(result, resultCityTexExp);
    }

}
public class CityTax extends TemplateConditionalInvoice {

    @Override
    public boolean isToApplyMaximunRate(Invoice invoice) {
        return invoice.getValue() > 500 && hasItemValueMoreThan100(invoice);

    }

    private boolean hasItemValueMoreThan100(Invoice invoice) {
        Optional<Item> invoiceOptional = invoice.getItens().stream()
                .filter(i -> i.getValue() > 100)
                .findFirst();
        return invoiceOptional.isPresent();
    }

    @Override
    protected double applyMaximumRate(Invoice invoice) {
        return invoice.getValue() * 0.10;
    }

    @Override
    protected double applyMinimumRate(Invoice invoice) {
        return invoice.getValue() * 0.60;
    }

}
public class FederalTax extends TemplateConditionalInvoice {

    @Override
    public boolean isToApplyMaximunRate(Invoice invoice) {
        return invoice.getValue() > 500;
    }

    @Override
    protected double applyMaximumRate(Invoice invoice) {
        return invoice.getValue() * 0.70;
    }

    @Override
    protected double applyMinimumRate(Invoice invoice) {
        return invoice.getValue() * 0.50;
    }
}
public class Invoice {

    private List<Item> itens = new ArrayList<>();
    public Invoice() {}


    public List<Item> getItens() {
        return Collections.unmodifiableList(itens);
    }

    public void addItem(Item item) {
        itens.add(item);
    }

    public double getValue() {
        return this.getItens().stream().mapToDouble(i -> i.getValue()).sum();
    }

}
public class Item {

    private String name;
    private double value;

    public Item(String name, double value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getValue() {
        return value;
    }

    public void setValue(double value) {
        this.value = value;
    }
}
public interface Tax {
    double calculate(Invoice invoice);
}

— LOOK THIS WITH ATTENTION —

public abstract class TemplateConditionalInvoice implements Tax {

    public double calculate(Invoice invoice) {

        if(isToApplyMaximunRate(invoice)) {
            return applyMaximumRate(invoice);
        } else {
            return applyMinimumRate(invoice);
        }
    }

    protected abstract boolean isToApplyMaximunRate(Invoice invoice);
    protected abstract double applyMaximumRate(Invoice invoice);
    protected abstract double applyMinimumRate(Invoice invoice);
}

DECORATOR

When ?

Whenever we realize we have behaviors that can be composed of behaviors involved other classes in the same hierarchy.”Behaviors comprise other behaviors.”

Diagram:

decorator

Example:

public class DecoratorTest {

    @Test
    public void testDecorator() {
        Tax taxComplexy = new CityTax(new FederalTax());
        Invoice invoice = new Invoice(1000.0);
        double result = taxComplexy.calculate(invoice);
        TestCase.assertEquals(result, 150);
    }
}
public abstract class Tax {

    private final Tax otherTax;

    public Tax(Tax otherTax) {
        this.otherTax = otherTax;
    }

    // construtor default
    public Tax() {
        this.otherTax = null;
    }

    protected double calculateOtherTax(Invoice invoice) {
        // If don't exist exist
        if(otherTax == null) return 0;
        return otherTax.calculate(invoice);
    }

    public abstract double calculate(Invoice invoice);

}
public class FederalTax extends Tax {

    public FederalTax(Tax otherTax) {
        super(otherTax);
    }

    public FederalTax(){}

    @Override
    public double calculate(Invoice invoice) {
        return invoice.getValue() * 0.05 + this.calculateOtherTax(invoice);
    }

}
public class CityTax extends Tax {

    public CityTax(Tax otherTax) {
        super(otherTax);
    }

    public CityTax(){}

    @Override
    public double calculate(Invoice invoice) {
        return invoice.getValue() * 0.1 + this.calculateOtherTax(invoice);
    }
}
public class Invoice {

    private double value;

    public Invoice(double value) {
        this.value = value;
    }

    public double getValue() {
        return value;
    }

    private void setValue(double value) {
        this.value = value;
    }
}

STATE

When ?

When we have actions to be performed according to the states.

Diagram :

state

Example:

Problem :

If the state order is in progress apply 10% of discount;

If the state order is in approved apply 5% of discount;

@Test
public void testDiscountExtra(){
    Order order = new Order(100.0);

    // Apply 10% discount because the state is Progress
    order.applyDiscountExtra();
    TestCase.assertEquals(order.value, 90.0);
    // Aprove Order
    order.approve();
    // Apply 5% discount because the state is Progress
    order.applyDiscountExtra();
    TestCase.assertEquals(order.value, 85.5);
    order.finish();

    // If you call now applyDiscountExtra()
    // you will receive a RunTimeException

    try{
        order.applyDiscountExtra();
    }catch (RuntimeException r){
        TestCase.assertTrue(true);
    }
}
public class Order {

    protected double value;
    protected OrderState currentState; // veja a mudança aqui

    public Order(double value) {
        this.value = value;
        this.currentState = new OrderProgress();
    }

    public void applyDiscountExtra() {
        currentState.applyDiscountExtra(this);
    }

    public void approve() {
        currentState.approve(this);
    }

    public void refuse() {
        currentState.refuse(this);
    }

    public void finish() {
        currentState.finish(this);
    }

    public double getValue() {
        return value;
    }

    public void setValue(double value) {
        this.value = value;
    }

    public OrderState getCurrentState() {
        return currentState;
    }

    public void setCurrentState(OrderState currentState) {
        this.currentState = currentState;
    }
}

public interface OrderState {
    void applyDiscountExtra(Order order);
    void approve(Order order);
    void refuse(Order order);
    void finish(Order order);
}
public class OrderApproved implements OrderState {

    @Override
    public void applyDiscountExtra(Order order) {
        order.value -= order.value * 0.05;
    }

    @Override
    public void approve(Order order) {
        order.currentState = new OrderApproved();
    }

    @Override
    public void refuse(Order order) {
        order.currentState = new OrderRefused();
    }

    @Override
    public void finish(Order order) {
        order.currentState = new OrderFinish();
    }
}
ublic class OrderFinish implements OrderState {
    @Override
    public void applyDiscountExtra(Order order) {
        new RuntimeException("Order finished");
    }

    @Override
    public void approve(Order order) {
        new RuntimeException("Order finished");
    }

    @Override
    public void refuse(Order order) {
        new RuntimeException("Order finished");
    }

    @Override
    public void finish(Order order) {
        order.currentState = new OrderFinish();
    }
}
public class OrderProgress implements OrderState {
    @Override
    public void applyDiscountExtra(Order order) {
        order.value -= order.value * 0.1;
    }

    @Override
    public void approve(Order order) {
        order.currentState = new OrderApproved();
    }

    @Override
    public void refuse(Order order) {
        order.currentState = new OrderRefused();
    }

    @Override
    public void finish(Order order) {
        throw new RuntimeException("Order in progress");

    }
}
public class OrderRefused implements OrderState {
    @Override
    public void applyDiscountExtra(Order order) {
        new RuntimeException("Order refused");
    }

    @Override
    public void approve(Order order) {
        new RuntimeException("Order refused");
    }

    @Override
    public void refuse(Order order) {
        order.currentState = new OrderRefused();
    }

    @Override
    public void finish(Order order) {
        new RuntimeException("Order refused");
    }
}

Builder

When ?

Whenever we have a complex object to be created, which it has several attributes or a complicated creation logic, we can centralize all in a Builder.

 

Example :

Problem : Create a invoice with a complex logic.

Solution:

Result/Cosole

*******************INVOICE**********************
Invoice Date Emission :09/06/2016
Invoice Date :09/06/2016
*******************ITEMS**********************
-ITEM A $20.0
-ITEM B $25.0
-ITEM C $4.0
-ITEM D $8.5
Total Items :57.5
*******************DISCOUNTS**********************
-B2B – 10.0%
-Week Promotion – 5.0%
Total Porcentagem :15.0%
*******************OBSERVATIONS********************
Order Late
*************************************************
Total Discount: $15.0
Total Value: $48.875
Date to Pay: 14/06/2016

Code :

@Test
  public void builderTest(){
      LocalDate yesteday = LocalDate.now().minus(1, ChronoUnit.DAYS);

      BuilderInvoice invoice = new BuilderInvoice();
      invoice.addItem("ITEM A", 20.0)
      .addItem("ITEM B", 25.0)
      .addItem("ITEM C", 4.0)
      .addItem("ITEM D", 8.50)
      .addDiscount("B2B", 10)
      .addDiscount("Week Promotion", 5)
      .addObservations("Order Late")
      .changeDateOrder(yesteday)
      .close()
      .emission();

      TestCase.assertFalse(true);
  }

public class BuilderInvoice {

    private Invoice invoice = new Invoice();

    public BuilderInvoice() {
        invoice.setDate(LocalDate.now());
        invoice.setOrder(new Order());
        invoice.getOrder().setDate(invoice.getDate());
    }

    public BuilderInvoice addItem(String name, double value) {
        invoice.getOrder().getItemList().add(new Item(name,value));
        return this;
    }

    public BuilderInvoice addDiscount(String desc, double por) {
        invoice.getDiscountList().add(new Discount(desc,por));
        return this;
    }

    public BuilderInvoice changeDateOrder(LocalDate date) {
        invoice.getOrder().setDate(date);
        return this;
    }

    public BuilderInvoice addObservations(String obs) {
        invoice.getOrder().setObservations(obs);
        return this;
    }

    public BuilderInvoice close(){
        double valueDiscount = (invoice.getOrder().getTotalValue() * invoice.getTotalDisountPorcentage())/100;
        double totalValue =  invoice.getOrder().getTotalValue() - valueDiscount;
        invoice.setTotalInvoice(totalValue);
        invoice.setFinishDate(LocalDate.now());
        invoice.setDatePayment(invoice.getFinishDate().plus(5, ChronoUnit.DAYS));
        return this;
    }

    public void emission(){
        System.out.println("*******************INVOICE**********************");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
        System.out.println("Invoice Date Emission :" + this.invoice.getFinishDate().format(formatter) );
        System.out.println("Invoice Date :" + this.invoice.getDate().format(formatter) );
        System.out.println("*******************ITEMS**********************");
        this.invoice.getOrder().getItemList().stream().forEach(i->{
            System.out.println("-" + i.getName() + " $" + i.getValue());
        });
        System.out.println("Total Items :" + this.invoice.getOrder().getTotalValue() );
        System.out.println("*******************DISCOUNTS**********************");
        this.invoice.getDiscountList().stream().forEach(i->{
            System.out.println("-" + i.getDescription() + " - " + i.getPorcent() + "%");
        });
        System.out.println("Total Porcentagem :" + this.invoice.getTotalDisountPorcentage()  + "%" );
        System.out.println("*******************OBSERVATIONS********************");
        System.out.println( this.invoice.getOrder().getObservations());
        System.out.println("*************************************************");
        System.out.println("Total Discount: $"+ this.invoice.getTotalDiscount());
        System.out.println("Total Value: $"+ this.invoice.getTotalInvoice());
        System.out.println("Date to Pay: "+ this.invoice.getDatePayment().format(formatter));
    }

}

public class Invoice {

    private Order order;
    private double totalItens;
    private List<Discount> discountList;
    private double totalInvoice;
    private LocalDate date;
    private LocalDate datePayment;
    private LocalDate finishDate;

    public Invoice() {
        this.discountList = new ArrayList<>();
        this.discountList = new ArrayList<>();
    }

    public LocalDate getFinishDate() {
        return finishDate;
    }

    public void setFinishDate(LocalDate finishDate) {
        this.finishDate = finishDate;
    }

    public Double getTotalDisountPorcentage(){
        return this.getDiscountList().stream().mapToDouble(i->i.getPorcent()).sum();
    }

    public List<Discount> getDiscountList() {
        return discountList;
    }

    private void setDiscountList(List<Discount> discountList) {
        this.discountList = discountList;
    }

    public LocalDate getDate() {
        return date;
    }

    public void setDate(LocalDate date) {
        this.date = date;
    }

    public LocalDate getDatePayment() {
        return datePayment;
    }

    public void setDatePayment(LocalDate datePayment) {
        this.datePayment = datePayment;
    }

    public Order getOrder() {
        return order;
    }

    public void setOrder(Order order) {
        this.order = order;
    }

    public double getTotalItens() {
        return totalItens;
    }

    private void setTotalItens(double totalItens) {
        this.totalItens = totalItens;
    }

    public double getTotalDiscount() {
        return this.getDiscountList().stream().mapToDouble( d-> d.getPorcent()).sum();
    }

    public double getTotalInvoice() {
        return totalInvoice;
    }

    public void setTotalInvoice(double totalInvoice) {
        this.totalInvoice = totalInvoice;
    }
}
public class Order {
    private List<Item> itemList;
    private LocalDate date;
    private Double totalValue;
    private String observations;

    public Order(){
        date = LocalDate.now();
        this.itemList = new ArrayList<>();
    }

    public String getObservations() {
        return observations;
    }

    public void setObservations(String observations) {
        this.observations = observations;
    }

    public List<Item> getItemList() {
        return itemList;
    }

    public void setItemList(List<Item> itemList) {
        this.itemList = itemList;
    }

    public LocalDate getDate() {
        return date;
    }

    public void setDate(LocalDate date) {
        this.date = date;
    }

    public Double getTotalValue() {
        return this.getItemList().stream().mapToDouble(i->i.getValue()).sum();
    }

    private void setTotalValue(Double totalValue) {
        this.totalValue = totalValue;
    }
}
public class Item {
    private String name;
    private double value;

    private Item() {
    }

    public Item(String name, double value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getValue() {
        return value;
    }

    public void setValue(double value) {
        this.value = value;
    }
}

public class Discount {
    private String description;
    private double porcent;

    public Discount(String description, double porcent) {
        this.description = description;
        this.porcent = porcent;
    }

    public String getDescription() {
        return description;
    }

    private void setDescription(String description) {
        this.description = description;
    }

    public double getPorcent() {
        return porcent;
    }

    private void setPorcent(double porcent) {
        this.porcent = porcent;
    }
}

Observer

When ?

When we have several different actions to be performed after a certain process.

Diagram :

httpatomoreillycomsourceoreillyimages1326916

Example :

Problem : After close an invoice you need do:

  • Persist in database;
  • Print
  • Send email;
  • Send SMS;

Solution :

Result/Console:

-> Persist in the database
*******************INVOICE**********************
Invoice Date Emission :09/06/2016
Invoice Date :09/06/2016
*******************ITEMS**********************
-ITEM A $20.0
-ITEM B $25.0
-ITEM C $4.0
-ITEM D $8.5
Total Items :57.5
*******************DISCOUNTS**********************
-B2B – 10.0%
-Week Promotion – 5.0%
Total Porcentagem :15.0%
*******************OBSERVATIONS********************
Order Late
*************************************************
Total Discount: $15.0
Total Value: $48.875
Date to Pay: 14/06/2016
-> Send email
-> Send Sms

Code:

public class ObserverTest {

    @Test
    public void builderTest(){

        BuilderInvoice invoice = new BuilderInvoice();
        invoice.addActions( new EmailDAO() )
        .addActions( new Printer() )
        .addActions( new SenderEmail() )
        .addActions( new SendSMS() );

        invoice.addItem("ITEM A", 20.0)
        .addItem("ITEM B", 25.0)
        .addItem("ITEM C", 4.0)
        .addItem("ITEM D", 8.50)
        .addDiscount("B2B", 10)
        .addDiscount("Week Promotion", 5)
        .addObservations("Order Late")
        .close();

        TestCase.assertFalse(true);
    }
}

public interface ActionsAfterCloserInvoice {

    public void execute(Invoice invoice);
}

public class EmailDAO implements ActionsAfterCloserInvoice {

    public void execute(Invoice invoice){
        System.out.println("-> Persist in the database");
        // Here the implementation to persist in the database
    }
}

public class SenderEmail implements ActionsAfterCloserInvoice {

    public void execute(Invoice invoice){
        // Here the implementation to send email
        System.out.println("-> Send email");
    }
}

public class SendSMS implements ActionsAfterCloserInvoice {

    public void execute(Invoice invoice){
        // Here the implementation to send a sms
        System.out.println("-> Send Sms");
    }
}

public class Printer implements ActionsAfterCloserInvoice {

    public void execute(Invoice invoice){
        System.out.println("*******************INVOICE**********************");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
        System.out.println("Invoice Date Emission :" + invoice.getFinishDate().format(formatter) );
        System.out.println("Invoice Date :" + invoice.getDate().format(formatter) );
        System.out.println("*******************ITEMS**********************");
        invoice.getOrder().getItemList().stream().forEach(i->{
            System.out.println("-" + i.getName() + " $" + i.getValue());
        });
        System.out.println("Total Items :" + invoice.getOrder().getTotalValue() );
        System.out.println("*******************DISCOUNTS**********************");
        invoice.getDiscountList().stream().forEach(i->{
            System.out.println("-" + i.getDescription() + " - " + i.getPorcent() + "%");
        });
        System.out.println("Total Porcentagem :" + invoice.getTotalDisountPorcentage()  + "%" );
        System.out.println("*******************OBSERVATIONS********************");
        System.out.println( invoice.getOrder().getObservations());
        System.out.println("*************************************************");
        System.out.println("Total Discount: $"+ invoice.getTotalDiscount());
        System.out.println("Total Value: $"+ invoice.getTotalInvoice());
        System.out.println("Date to Pay: "+ invoice.getDatePayment().format(formatter));
    }
}

public class BuilderInvoice {

    private Invoice invoice ;
    private List<ActionsAfterCloserInvoice> actionsAfterCloserInvoices ;
    public BuilderInvoice() {
        actionsAfterCloserInvoices = new ArrayList<>();
        invoice = new Invoice();
        invoice.setDate(LocalDate.now());
        invoice.setOrder(new Order());
        invoice.getOrder().setDate(invoice.getDate());
    }

    public BuilderInvoice addItem(String name, double value) {
        invoice.getOrder().getItemList().add(new Item(name,value));
        return this;
    }

    public BuilderInvoice addDiscount(String desc, double por) {
        invoice.getDiscountList().add(new Discount(desc,por));
        return this;
    }

    public BuilderInvoice changeDateOrder(LocalDate date) {
        invoice.getOrder().setDate(date);
        return this;
    }

    public BuilderInvoice addObservations(String obs) {
        invoice.getOrder().setObservations(obs);
        return this;
    }

    public BuilderInvoice close(){
        double valueDiscount = (invoice.getOrder().getTotalValue() * invoice.getTotalDisountPorcentage())/100;
        double totalValue =  invoice.getOrder().getTotalValue() - valueDiscount;
        invoice.setTotalInvoice(totalValue);
        invoice.setFinishDate(LocalDate.now());
        invoice.setDatePayment(invoice.getFinishDate().plus(5, ChronoUnit.DAYS));
        actions();
        return this;
    }

    // Here we call the actions --> observers
    private void actions(){
        actionsAfterCloserInvoices.stream().forEach(a-> a.execute(this.invoice));
    }

    public BuilderInvoice addActions(ActionsAfterCloserInvoice actionsAfterCloserInvoice){
        this.actionsAfterCloserInvoices.add(actionsAfterCloserInvoice);
        return this;
    }

}

 

 

You also can check all implementations above in :

https://github.com/camilamacedo86/designPatternExamples

 

1 Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s