In this article, we are about to cover another design pattern in C#, this time a structural one. That pattern is the Facade Pattern.
The source code is available at the Facade Design Pattern GitHub Repository.
For the complete list of articles from this series check out C# Design Patterns.
What is a Facade Pattern?
As the name suggests, it represents a “facade” for the end-user to simplify the usage of the subsystems that are poorly designed and/or too complicated by hiding their implementation details. It also comes in handy when working with complex libraries and APIs.
A Facade Pattern is represented by a single-class interface that is simple, easy to read and use, without the trouble of changing the subsystems themselves. However, we must be careful since limiting the usage of the subsystem’s functionalities may not be enough for the power-users.
Facade Pattern Example
As an example for explaining the Facade Pattern better, we are going to describe the workflow of ordering food online.
Let’s say we have a list of restaurants. We open the restaurant’s page, find the dish that we like and add it to the cart. We do it as many times as we want and complete the order. When we submit the order, we get an order confirmation along with the price of the order.
First, let’s create a class named Order
that will represent the order coming from the User:
public class Order { public string DishName { get; set; } public double DishPrice { get; set; } public string User { get; set; } public string ShippingAddress { get; set; } public double ShippingPrice { get; set; } public override string ToString() { return string.Format("User {0} ordered {1}. The full price is {2} dollars.", User, DishName, DishPrice + ShippingPrice); } }
Furthermore, we have to create two more classes – an online restaurant and a shipping service. The OnlineRestaurant
class provides methods for adding orders to the cart:
public class OnlineRestaurant { private readonly List<Order> _cart; public OnlineRestaurant() { _cart = new List<Order>(); } public void AddOrderToCart(Order order) { _cart.Add(order); } public void CompleteOrders() { Console.WriteLine("Orders completed. Dispatch in progress..."); } }
On the other hand, the ShippingService
class accepts the order and ships them to the address stored in the ShippingAddress
property inside the Order
class. The ShippingService
also calculates the shipping expenses and displays them to the user:
public class ShippingService { private Order _order; public void AcceptOrder(Order order) { _order = order; } public void CalculateShippingExpenses() { _order.ShippingPrice = 15.5; } public void ShipOrder() { Console.WriteLine(_order.ToString()); Console.WriteLine("Order is being shipped to {0}...", _order.ShippingAddress); } }
In the end, we are incorporating the entire logic into the Main
class to represent the workflow of ordering food online:
class Program { static void Main(string[] args) { var restaurant = new OnlineRestaurant(); var shippingService = new ShippingService(); var chickenOrder = new Order() { DishName = "Chicken with rice", DishPrice = 20.0, User = "User1", ShippingAddress = "Random street 123" }; var sushiOrder = new Order() { DishName = "Sushi", DishPrice = 52.0, User = "User2", ShippingAddress = "More random street 321" }; restaurant.AddOrderToCart(chickenOrder); restaurant.AddOrderToCart(sushiOrder); restaurant.CompleteOrders(); shippingService.AcceptOrder(chickenOrder); shippingService.CalculateShippingExpenses(); shippingService.ShipOrder(); shippingService.AcceptOrder(sushiOrder); shippingService.CalculateShippingExpenses(); shippingService.ShipOrder(); Console.ReadLine(); } }
The result of the current implementation of the Main
class is:
Note: We assume that the orders are coming from the outside and that’s why we created them inside the Main
class. This is optional, of course, for more complicated systems this would also be a part of some service.
Now, for those of you who are wondering:
“Why is this wrong?”
Continue reading.
Looking at the Main
class and all the steps we’ve implemented, we can see a lot of code in one single class. Since we tend to make things easier to read and less complicated, we have to make some adjustments.
And that’s where the Facade Pattern steps in.
Facade Pattern Implementation
One of the goals of the Facade Pattern is to hide the implementation details which indicates that having everything in the Main
class doesn’t do the work. It is too much unnecessary information, therefore, we would like it better somewhere else.
That said, we are going to create another class called Facade
. The Facade class will act as a “middleware” between the User and the complexity of the system without changing the business logic:
public class Facade { private readonly OnlineRestaurant _restaurant; private readonly ShippingService _shippingService; public Facade(OnlineRestaurant restaurant, ShippingService shippingService) { _restaurant = restaurant; _shippingService = shippingService; } public void OrderFood(List<Order> orders) { foreach (var order in orders) { _restaurant.AddOrderToCart(order); } _restaurant.CompleteOrders(); foreach (var order in orders) { _shippingService.AcceptOrder(order); _shippingService.CalculateShippingExpenses(); _shippingService.ShipOrder(); } } }
Since we’ve moved the implementation logic to the Facade
class, we can simplify the Main
class:
class Program { static void Main(string[] args) { var restaurant = new OnlineRestaurant(); var shippingService = new ShippingService(); var facade = new Facade(restaurant, shippingService); var chickenOrder = new Order() { DishName = "Chicken with rice", DishPrice = 20.0, User = "User1", ShippingAddress = "Random street 123" }; var sushiOrder = new Order() { DishName = "Sushi", DishPrice = 52.0, User = "User2", ShippingAddress = "More random street 321" }; facade.OrderFood(new List<Order>() { chickenOrder, sushiOrder }); Console.ReadLine(); } }
When we run the code, we can see the exact output:
This means we have successfully freed the user of the unnecessary pressure of knowing all the required steps for the food to arrive.
Note: in this example, we passed the OnlineRestaurant
and the ShippingService
to the Facade
, assuming that they are already created. However, they can also be instantiated inside the Facade
itself.
Conclusion
So, we have seen how the Facade Pattern can assist us in making the client’s lives easier. Now, we are ready to embrace the complex implementations as is. And last but not least, in certain situations, using this pattern requires carefulness. It can limit the user’s abilities to make use of the full potential of the application or library that we’re trying to simplify.