In the previous chapter, we have seen that creational patterns can help us create objects with a simplified and extensible approach. Moving forward, as we develop various classes, our next challenge is to integrate these classes. In some scenarios, where we need to establish a relationship between two classes and they do not know each other's behavior or are incompatible with each other or are frequently changed, it is very important to establish a robust relationship between these classes. Structural design patterns help us design a robust relationship between multiple classes.
Structural patterns can be used in the following scenarios:
In this chapter, we will discuss various ways to create an optimum relationship between multiple classes.
We will go through the following structural design patterns:
By the time an application is being built, the chances are high that the existing classes become more complex and require complex client code for interaction. Often, this complex code for interaction is duplicated wherever a relationship is to be established. Also, a developer needs to be aware of all the aspects of this complex interaction code, such as a sequence in which it should be executed. The façade pattern helps us hide such complex interaction code and makes it reusable.
Most prominent use cases of the façade design pattern include scenarios where a client needs to have multiple interactions to achieve a unit of work or the interaction code is duplicated at multiple locations.
Consider the example of an online shopping store. Whenever any order is placed in such an application, a series of complicated steps need to be executed before a product is shipped to the client address. It needs to go through steps, such as updating the inventory, verifying the address, calculating a discount, verifying payment, and shipping the product. All these steps are complex and can be considered modules. Also, the sequence of these steps is equally important.
Let's take a look at the following code for all the previously mentioned modules of an online store application. Below each Apex class, you will see the steps performed before a product is shipped to the customer:
/* Class to contain methods related to operations on Inventory */ public class Inventory { //This method updates Inventory public boolean updateInventory(String prodId, Integer count) { System.debug(count+' Product with Id '+prodId+' is subtracted from Inventory'); return true; } } /* Class to contain methods related to operations on Address Verification */ public class AddressVerification { //This method is used to verify address public boolean verify(String zipAdd){ System.debug('Product can be shipped at zip - '+zipAdd); return true; } } /* Class to contain methods related to operations for applying discount */ public class ApplyDiscount { //return discounted price public decimal calculate(Decimal actualPrice,Decimal discount){ Decimal finalPrice = actualPrice - (discount/100 *actualPrice) ; System.debug('Final price, After '+ discount/100 +'% discount applied is '+finalPrice); return finalPrice; } } public class PaymentVerification { public boolean verify(String cardNumber){ System.debug('Card with number '+cardNumber+' is used for payment'); return true; } } /* Class to contain methods related to operations for shipping */ public class ShipToAddress { //Ship product to address public boolean ship(String add, String prodName){ System.debug('"'+prodName+'" is shipped to '+add); return true; } }
In a real-world application, preceding classes can be huge and can involve numerous transactions. For the sake of simplicity, only a skeleton of classes has been provided in this example.
We need one more class to hold all the information about the product ordered by a customer:
public class OrderDetail { //public properties public String productId {get;set;} public String productName {get;set;} public Integer productCount {get;set;} public String zipCode {get;set;} public Decimal price {get;set;} public Decimal discount {get;set;} public String paymentCardNumber {get;set;} public String address {get;set;} //Constructor public OrderDetail(String productId, String productName, Integer productCount, String zipCode, Decimal price, Decimal discount, String paymentCardNumber, String address ){ this.productId = productId; this.productName = productName; this.productCount = productCount; this.zipCode = zipCode; this.price = price; this.discount = discount; this.paymentCardNumber = paymentCardNumber; this.address = address; } }
Now, to place an order, the following code needs to be used at all the required locations:
OrderDetail order = new OrderDetail('IBN-abcd123','Apex Design Pattern', 1, '06042',40,15,'123456789098754','Manchester Buckland hills'); Inventory inv = new Inventory(); inv.updateInventory(order.productId , order.productCount ); AddressVerification add = new AddressVerification(); add.verify(order.zipCode); ApplyDiscount disc = new ApplyDiscount(); disc.calculate(order.price , order.discount); PaymentVerification pym = new PaymentVerification(); pym.verify(order.paymentCardNumber); ShipToAddress shipProduct = new ShipToAddress(); shipProduct.ship(order.address , order.productName);
The output of the preceding code is as follows:
1 Product with Id IBN-abcd123 is subtracted from Inventory Product can be shipped at zip - 06042 Final price, After 0.15% discount applied is 34.00 Card with number 123456789098754 is used for payment "Apex Design Pattern" is shipped to Manchester Buckland hills
The following figure shows a high-level diagram of the current state of an application:
In the preceding example, a problem is very evident. Every piece of client code has to ensure that all the required steps are executed and as per the specified sequence. Any changes made to the steps involved or to the sequence of these steps will result in a ripple effect on all code locations wherever they have been used.
Also, in the preceding figure, the client code is tightly coupled with multiple subsystems, such as card verification, inventory update, applying discount, and so on.
As all the subsystems are well tested and dependent on many other modules, changing the code would not be a wise decision. As there are many large classes, and the client uses the repetitive code for interaction, it strongly hints at the usage of the façade pattern.
While implementing façade pattern, we do not make any changes to the existing code, but we create a new class or layer, which takes care of the interaction with subsystems.
The following Apex class represents a newly written façade class:
public class OnlineStoreFacade {
//This method takes care of processing all steps in sub systems
public void processSteps(OrderDetail order){
Inventory inv = new Inventory();
AddressVerification add = new AddressVerification();
ApplyDiscount disc = new ApplyDiscount();
PaymentVerification pym = new PaymentVerification();
ShipToAddress shipProduct = new ShipToAddress();
inv.updateInventory(order.productId , order.productCount );
add.verify(order.zipCode);
disc.calculate(order.price , order.discount);
pym.verify(order.paymentCardNumber);
shipProduct.ship(order.address , order.productName);
}
}
The client needs to use only the following three lines of code for the interaction:
OrderDetail order = new OrderDetail('IBN-abcd123','Apex Design Pattern', 1, '06042',40,15,'123456789098754','Manchester Buckland hills');
OnlineStoreFacade facade = new OnlineStoreFacade();
facade.processSteps(order);
The following class diagram shows the façade pattern implemented in this example:
Let's say that we have generated Apex classes from a web service's WSDL file using the WSDLtoApex tool. Each time before invoking a web service, we may need to set up the endpoint URL or timeout or any other parameter. This leads to code redundancy. In these scenarios as well, we can have a common Apex class, which can be used to invoke web services, and all these repetitive settings can be part of this Apex class.
WSDLtoApex is an open source tool and part of the Force.com IDE plugin for Eclipse. You can find this tool, or if you want to contribute to this tool, you can visit its GitHub repository page at https://github.com/forcedotcom/WSDL2Apex.
There is one more tool available at FuseIT, which goes one level higher by allowing you to generate the Apex class only for the required method from WSDL. This tool is very helpful in scenarios where WSDL generates hundreds of classes and consumes the allowed Apex limit for the Salesforce instance. You can find more information about this tool at http://www.fuseit.com/Solutions/SFDC-Explorer.aspx.