Problem
You want to add behavior or state to individual objects at run-time. Inheritance is not feasible because it is static and applies to an entire class.
What is the Decorator Pattern?
The Decorator Pattern attaches additional responsibilities to an object statically or dynamically. A Decorator provides an enhanced interface to the original object. In the implementation of this pattern, we prefer composition over an inheritance – so that we can reduce the overhead of subclassing again and again for each decorating element. The recursion involved with this design can be used to decorate our object as many times as we require.
The ornaments that are added to pine or fir trees are examples of Decorators. Lights, garland, candy canes, glass ornaments, etc., can be added to a tree to give it a festive look. The ornaments do not change the tree itself which is recognizable as a Christmas tree regardless of particular ornaments used. As an example of additional functionality, the addition of lights allows one to “light up” a Christmas tree.
Another example: an assault gun is a deadly weapon on its own. But you can apply certain “decorations” to make it more accurate, silent and devastating.
Purpose
How to implement
Example
public interface Weapon { boolean aimAndFire(); } public class Gun implements Weapon { @Override public boolean aimAndFire() { System.out.println("fire gun!!!"); return true; } } public class AK47 extends WeaponDecorator { public AK47(Weapon weapon) { super(weapon); } @Override public boolean aimAndFire() { // TODO Auto-generated method stub super.aimAndFire(); // add additional functionalities. System.out.println("It's an AK47, fire 5 rounds a pull!"); return true; } } public class Automatic extends WeaponDecorator { public Automatic(Weapon weapon) { super(weapon); } @Override public boolean aimAndFire() { super.aimAndFire(); // add additional functionalities. System.out.println("It's an automatic, put on silencer and fire 10 rounds a pull!"); return true; } } public class SniperRiffle extends WeaponDecorator { public SniperRiffle(Weapon weapon) { super(weapon); } @Override public boolean aimAndFire() { // TODO Auto-generated method stub super.aimAndFire(); // add additional functionalities. System.out.println("It's a SniperRiffle, put on scope and fire 1 round a pull!"); return true; } }
public class DecoratorDemo { public static void main(String[] args) { // TODO Auto-generated method stub Weapon ak47 = new AK47(new Gun()); System.out.println("fired ak47: " + ak47.aimAndFire()); System.out.println(); Weapon automatic = new Automatic(new Gun()); System.out.println("fired automatic: " + automatic.aimAndFire()); System.out.println(); // here a riffle can fire like a riffle and also as a automatic(or other guns) Weapon riffle = new SniperRiffle(new Automatic(new Gun())); System.out.println("fired riffle: " + riffle.aimAndFire()); System.out.println(); List<Weapon> weapons = Arrays.asList(new AK47(new Gun()), new Automatic(new Gun()), new SniperRiffle(new Gun())); weapons.stream().forEach(weapon -> { System.out.println("fire weapon: " + weapon.aimAndFire()); System.out.println(); }); } }
Results
fire gun!!! It's an AK47, fire 5 rounds a pull! fired ak47: true fire gun!!! It's an automatic, put on silencer and fire 10 rounds a pull! fired automatic: true fire gun!!! It's an automatic, put on silencer and fire 10 rounds a pull! It's a SniperRiffle, put on scope and fire 1 round a pull! fired riffle: true fire gun!!! It's an AK47, fire 5 rounds a pull! fire weapon: true fire gun!!! It's an automatic, put on silencer and fire 10 rounds a pull! fire weapon: true fire gun!!! It's a SniperRiffle, put on scope and fire 1 round a pull! fire weapon: true
Uses
Advantages
Disadvantages
Problem: sometimes some objects consume a lot of resources and can be a problem at startup. What you do is not create those objects until there are needed or a request has come for them. An example is a database connection.
Solution
Proxy is a placeholder for another object. A proxy controls access to the original object and allows actions to take place either before or after a request gets through to the original object.
Applicability
Lazy initialization (virtual proxy). This is when you have a heavyweight service object that wastes system resources by being up always, even though you only need it from time to time.
Instead of creating the object when the app launches, you can delay the object’s initialization to a time when it’s really needed.
Access control (protection proxy). This is when you want only specific clients to be able to use the service object; for instance, when your objects are crucial parts of an operating system and clients are various launched applications (including malicious ones).
The proxy can pass the request to the service object only if the client’s credentials match some criteria.
Local execution of a remote service (remote proxy). This is when the service object is located on a remote server.
In this case, the proxy passes the client request over the network, handling all of the nasty details of working with the network.
Logging requests (logging proxy). This is when you want to keep a history of requests to the service object.
The proxy can log each request before passing it to the service.
Caching request results (caching proxy). This is when you need to cache results of client requests and manage the life cycle of this cache, especially if results are quite large.
The proxy can implement caching for recurring requests that always yield the same results. The proxy may use the parameters of requests as the cache keys.
Smart reference. This is when you need to be able to dismiss a heavyweight object once there are no clients that use it.
The proxy can keep track of clients that obtained a reference to the service object or its results. From time to time, the proxy may go over the clients and check whether they are still active. If the client list gets empty, the proxy might dismiss the service object and free the underlying system resources.
The proxy can also track whether the client had modified the service object. Then the unchanged objects may be reused by other clients.
A real-world example of proxy can be a cheque or credit card for what is in your bank account. A credit card can be used as a placeholder for cash and provides a means of accessing that cash when required. And that’s exactly what the Proxy pattern does – “Controls and manages access to the actual object”.
public class BankAccount { private long id; private String routingNumber; private String accountNumber; private String name; private double balance; private double transferFee; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getRoutingNumber() { return routingNumber; } public void setRoutingNumber(String routingNumber) { this.routingNumber = routingNumber; } public String getAccountNumber() { return accountNumber; } public void setAccountNumber(String accountNumber) { this.accountNumber = accountNumber; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } public double getTransferFee() { return transferFee; } public void setTransferFee(double transferFee) { this.transferFee = transferFee; } public boolean withdraw(double amount) { if(balance>0 && balance>=amount) { balance-=amount; return true; } throw new RuntimeException("Nothing enough funds in your account."); } }
public class CreditCard { // credit card number private String number; private String cvc; private String expMonth; private String expYear; private String zipcode; private String last4; BankAccount bankAccount; public CreditCard() { this(null); } public CreditCard(BankAccount bankAccount) { this(null,null,null,null,null,null,bankAccount); } public CreditCard(String number, String cvc, String expMonth, String expYear, String zipcode, String last4, BankAccount bankAccount) { super(); this.number = number; this.cvc = cvc; this.expMonth = expMonth; this.expYear = expYear; this.zipcode = zipcode; this.last4 = last4; this.bankAccount = bankAccount; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public String getCvc() { return cvc; } public void setCvc(String cvc) { this.cvc = cvc; } public String getExpMonth() { return expMonth; } public void setExpMonth(String expMonth) { this.expMonth = expMonth; } public String getExpYear() { return expYear; } public void setExpYear(String expYear) { this.expYear = expYear; } public String getZipcode() { return zipcode; } public void setZipcode(String zipcode) { this.zipcode = zipcode; } public String getLast4() { return last4; } public void setLast4(String last4) { this.last4 = last4; } public BankAccount getBankAccount() { return bankAccount; } public void setBankAccount(BankAccount bankAccount) { this.bankAccount = bankAccount; } }
public class CreditCardService { public boolean authorizeCharge(CreditCard creditCard, double amount) { return creditCard.getBankAccount().withdraw(amount); } public double getAvailableBalance(CreditCard creditCard) { // TODO Auto-generated method stub return creditCard.getBankAccount().getBalance(); } }
public class ProxyDemo { static CreditCardService creditCardService = new CreditCardService(); public static void main(String[] args) { BankAccount bankAccount = new BankAccount(); bankAccount.setId(1); bankAccount.setAccountNumber("000123456789"); bankAccount.setRoutingNumber("110000000"); bankAccount.setBalance(1000); bankAccount.setName("Laulau"); CreditCard masterCard = new CreditCard(bankAccount); // charge $100; double chargeAmount = 100; double availableBalance = creditCardService.getAvailableBalance(masterCard); System.out.println("availableBalance: $"+availableBalance); boolean ok = creditCardService.authorizeCharge(masterCard, chargeAmount); System.out.println("charge ok? "+ok); } }
Pros
Cons