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 :

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 :

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 :

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:

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 :

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 :

Example :
Problem : After close an invoice you need do:
- Persist in database;
- 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 :
Great post! It’s very useful, Thanks
LikeLike