Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
October 16, 2020 03:25 pm GMT

Design Patterns: Facade Pattern

Cover photo by Silvio Kundt on Unsplash

The facade pattern is a structural design pattern, commonly used when there is some interaction with a complex external library or service.

In this pattern, we create a class to encapsulate the interactions with the 3rd party library. This class is called the Facade. This allows us to expose a simpler interface to the rest of our application. Developers working on other parts of our system need not learn the 3rd party library; rather, they need to only interface with the facade class we create.

This serves a few crucial purposes:

  1. In case the 3rd party system is super complex and requires multiple steps to perform a single action, we can encapsulate all of that in a single function in our facade.
  2. We can easily switch out one 3rd party library for another and still maintain the same interface for our facade. This flexibility is one of the main selling points for the facade.
  3. We can also filter out any extra functionality from the external service that we don't really require in our application. This is very common while using the facade pattern because external libraries can be very comprehensive and we may not require all the features offered.

Let's take an example of using the facade pattern

Amazon order system

Let's picture that you're building the module that deals with the placement of orders at Amazon. While building this, you have realized that there are several steps to placing an order; each step involving interactions with several other systems/modules.

Note: This probably is not how Amazon has implemented it's order system. This is just a simplistic representation of what it could be

Let's list out the steps:

  1. Authenticate the user - Users service
  2. Verify product's availability - Products service
  3. Assign product to user - Products service
  4. Make payment - Payments service
  5. Send notification to dealer - Notification service

All of that on the click of a button

Normally, if you were to directly integrate all the systems and services on the button's click, it would hamper the code's readability and would also make it a nightmare to switch one service for another (Imagine a case where you would revamp the products service)

To solve for all this, we can create a facade class that deals with all this and exposes a function called placeOrder that takes a productId and userId to complete all the above steps

Let's implement this solution in go

type orderFacade struct {    userService UserService    productService ProductService    paymentService PaymentService    notificationService NotificationService}func (o *orderFacade) placeOrder(userId string, productId string) {    fmt.Println("[Facade] Starting order placement")    userValid := o.userService.isUserValid(userId)    productAvailable := o.productService.productAvailable(productId)    if userValid && productAvailable {        o.productService.assignProductToUser(productId, userId)        o.paymentService.makePayment(userId, productId)        o.notificationService.notifyDealer(productId)    }}
Enter fullscreen mode Exit fullscreen mode

Let's also take a look at the other services' code

type UserService struct {}func (u *UserService) isUserValid(userId string) bool {    fmt.Println("[UserService] validating the user: ", userId);    // Complex logic for checking validity    return true}type ProductService struct {}func (p *ProductService) productAvailable(productId string) bool {    fmt.Println("[ProductService] checking availability of product: ", productId)    // Complex logic for checking availability    return true;}func (p *ProductService) assignProductToUser(productId string, userId string) {    fmt.Printf("[ProductService] assigning product %s to user %s
", productId, userId) // complex logic for product assignment}type PaymentService struct {}func (p *PaymentService) makePayment(userId string, productId string) { fmt.Printf("[PaymentService] charging user %s for product %s
", userId, productId) // complex logic for making payment}type NotificationService struct {}func (n *NotificationService) notifyDealer(productId string) { fmt.Printf("[NotificationService] notifying dealer about sale of product %s
", productId) // complex notification logic}
Enter fullscreen mode Exit fullscreen mode

Oooh, so now we have our dummy services and completely legit facade in place - It's time to test it out

func main() {    orderModule := &orderFacade{        userService:  UserService{},        productService:  ProductService{},        paymentService:  PaymentService{},        notificationService:  NotificationService{},    }    userId := "test-user-id"    productId := "test-product-id"    orderModule.placeOrder(userId, productId)}
Enter fullscreen mode Exit fullscreen mode

When you run your program, you should see something like this in the output:

[Facade] Starting order placement[UserService] validating the user:  test-user-id[ProductService] checking availability of product:  test-product-id[ProductService] assigning product test-product-id to user test-user-id[PaymentService] charging user test-user-id for product test-product-id[NotificationService] notifying dealer about sale of product test-product-id
Enter fullscreen mode Exit fullscreen mode

You can notice from the code and the output that the facade only is the surface layer of the functionality. The actual action may happen in the external services and it is still abstracted away from the user of the facade by exposing a super simple interface.

That's the facade pattern in a gist

You can find all the code for this tutorial on this this github repo

Cheers


Original Link: https://dev.to/shubhamzanwar/design-patterns-facade-pattern-9pb

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To