© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2023
V. SarcarSimple and Efficient Programming with C# https://doi.org/10.1007/978-1-4842-8737-8_9

9. Simplify Complex Systems Using Facades

Vaskaran Sarcar1  
(1)
Kolkata, West Bengal, India
 

In Chapter 8, you saw how you can redefine some steps in a multistep algorithm to achieve a task. In this chapter, you will look at an application that also does a series of tasks. But instead of redefining these tasks, you will learn how to make a simplified interface to perform this task. Facades are useful in this context.

Note

You may be interested to understand the difference between a facade and a template method. In general, a template method belongs to a base class and allows the subclasses to redefine some steps. You create an object of a class and invoke this template method to complete your job. But facades often involve multiple objects from many different classes. This time you perform a series of steps to accomplish the task involving all these objects. You do not redefine the methods in these classes; instead, you manage to call them easily. So, a subsystem class often does not know about the presence of a facade.

Facades provide you with an entry point to access various methods across different classes in a structured manner. If you enforce a rule that does not allow you access to the individual methods directly and instead you access them through your facade only, then the facade is called an opaque facade; otherwise, it is a transparent facade. You can make your facade static too.

Author’s Note Depending on a programming language, you can make a top-level static class. For example, at the time of this writing, I was using the Java SE 17 edition. Up until this edition, you cannot tag the keyword static with a top-level class in Java. Java Language Specification (Java SE 17 Edition) says that the modifier static pertains only to member classes and local classes. Though C# allows this, there are restrictions. For example, you cannot instantiate the static class. Also, a static class can contain static members only, and there are no instance constructors. Finally, it is sealed by nature. So, depending on the programming language, you can make a top-level static class with static methods to avoid direct instantiation of a facade class. And when you use a similar concept, you say that you are using a static facade.

The Problem Statement

A person can apply for a loan from a bank. The bank must do some background verification before it grants the loan to a customer. This background verification is a complex process that consists of various subprocesses. If needed, the bank officials can visit the customer’s property too before they consider the loan application. If an applicant fulfills all these criteria, they can get a loan. But here is the key: an applicant does not know the details of the background verification and is interested in the final outcome only, whether the loan is approved. How the bank officials reach the decision does not matter to the customer. In the upcoming examples, you will see such a process. For simplicity, I make the following assumptions:
  • A loan applicant or customer must have some assets. If the asset value is less than the loan application claimed, the customer cannot get the loan.

  • If a customer has an existing loan, the customer cannot get any new loan.

Our job is to make an application that is based on these assumptions. Here is some sample output for better clarity.

Case 1:
Bob's current asset value: $5,000.
He claims a loan amount: $20,000.
He has an existing loan.
Expected Outcome: Bob cannot get the loan for the following reasons:
  • Insufficient balance.

  • An old loan exists.

Case 2:
Jack's current asset value: $100,000.
He claims a loan amount: $30,000.
He has no existing loan.

Expected Outcome: Jack can get the loan.

Case 3:
Tony's current asset value: $125,000.
He claims a loan amount: $50,000.
He has an existing loan.
Expected Outcome: Tony cannot get the loan for the following reason:
  • An old loan exists.

Let’s build the application.

Initial Program

In this example, you see three classes: Person, Asset, and LoanStatus. An instance of the Person class can apply for a loan. This class has a constructor that takes three different parameters called name, assetValue, and previousLoanExist. To avoid more typing and passing all these three parameters during the instance creation, I made the last two parameters optional. Here is the Person class:
class Person
    {
        public string name;
        public double assetValue;
        public bool previousLoanExist;
        public Person(string name,
            double assetValue=100000,
            bool previousLoanExist = false)
        {
          this.name = name;
          this.assetValue = assetValue;
          this.previousLoanExist = previousLoanExist;
        }
    }
Notice the constructor of this class. Since you have two optional parameters, you can create instances using any of the following lines:
Person jack = new Person("Jack");
Person kate = new Person("Kate", 70000);
Person tony = new Person("Tony", 125000,true);
See the Asset class now. This class has a method HasSufficientAssetValue to verify whether the current asset value is greater than or equal to the claim amount. Here is the Asset class:
class Asset
{
    public bool HasSufficientAssetValue(
                          Person person,
                          double claimAmount)
    {
        Console.WriteLine($"Validating {person.name}'s
         asset value.");
        return person.assetValue >= claimAmount;
    }
}
Now see the LoanStatus class. This class has a method called HasPreviousLoans to verify whether a person has an existing loan.
class LoanStatus
{
    public bool HasPreviousLoans(Person person)
    {
        Console.WriteLine($"Verifying {person.name}'s
         existing loan(s).");
        return !person.previousLoanExist;
    }
}
POINT TO NOTE
Notice that inside the HasPreviousLoans method of the LoanStatus class, I have used a simplified statement when I use the following:
return !person.previousLoanExist;
instead of using the following line:
return !person.previousLoanExist ? true : false;

I did the same inside the HasSufficientAssetValue method that belongs to the Asset class. Some readers are often confused when they see the “simplified” code. This is why I wanted to mention this.

Demonstration 1

The Person, Asset, and LoanStatus classes were already shown. So, I will not repeat these classes in the following code segment.

Note

For simplicity, I put all those three classes and the following client code into a single file. When you download the source code from the Apress website, refer to the folder Demo1_WithoutFacade inside Chapter9 to see the complete program.

Now assume that a novice programmer writes the following client code. The programmer creates a Person instance, called bob, and shows whether he is eligible for a loan. It works, but is it a good solution? We’ll analyze this.
Console.WriteLine("Directly interacting with the
 classes(subsystems).");
Asset asset = new();
LoanStatus loanStatus = new();
string status = "approved";
string reason = string.Empty;
bool assetValue, previousLoanExist;
// Person-1
Person bob = new("Bob", 5000, true);
// Starts background verification
assetValue = asset.HasSufficientAssetValue(bob,20000);
previousLoanExist = loanStatus.HasPreviousLoans(bob);
if (!assetValue)
{
    status = "Not approved.";
    reason += " Insufficient balance.";
}
if (!previousLoanExist)
{
    status = "Not approved.";
    reason += " an old loan exists.";
}
Console.WriteLine($"{bob.name}'s application status: {status}");
Console.WriteLine($"Remarks if any: {reason}");

Output

Here is the output:
Directly interacting with the classes(subsystems).
Validating Bob's asset value.
Verifying Bob's existing loan(s).
Bob's application status: Not approved.
Remarks if any:
Insufficient balance.
An old loan exists.

Analysis

Let me ask you a few questions about this program.
  • There is only one customer at this moment. What will the developer do if you have two or more loan applicants? Will the developer repeat the background verification logic multiple times inside the client code?

  • Have you noticed that inside the client code the developer exposes the background verification logic? Is this a good idea?

  • How would you feel if you do not need to create any subsystem instances (for example an Asset or LoanStatus instance) to know the outcome? Instead, assume that there is a loan approver instance that is the only point of contact to know the status of an application. It enables you to write something like the following:

Person bob = new("Bob", 5000, true);
string approvalStatus = loanApprover.   CheckLoanEligibility(bob, 20000);
Console.WriteLine($"{bob.name}'s application
   status: {approvalStatus}");
  • In the future, if there are new criteria to get a loan, the loan approver can take the responsibility to handle the situation. Will you value this point?

Better Program

When you analyze the questions, you’ll search for a better solution. The last two questions give you the idea that making a single point of contact (like the loan approver I discussed) can make the code cleaner and easily maintainable. Let’s see such an implementation in the upcoming demonstration. The LoanApprover class plays the role of the facade layer in this example. You’ll see that the client code talks to this facade to know the final decision.

Class Diagram

Figure 9-1 shows the class diagram for demonstration 2.
Figure 9-1

The client code talks to the LoanApprover to know whether an applicant can get a new loan

Demonstration 2

Here is the complete program:
Console.WriteLine("***Simplifying the usage of a
 complex system using a facade.***");
// Using a facade
LoanApprover loanApprover = new();
// Person-1
Person bob = new("Bob", 5000, true);
string approvalStatus = loanApprover.CheckLoanEligibility(bob, 20000);
Console.WriteLine($"{bob.name}'s application status: {approvalStatus}");
// Person-2
Person jack = new("Jack");
approvalStatus = loanApprover.CheckLoanEligibility(jack, 30000);
Console.WriteLine($"{jack.name}'s application status: {approvalStatus}");
// Person-3
Person tony = new("Tony", 125000, true);
approvalStatus = loanApprover.CheckLoanEligibility(tony, 50000);
Console.WriteLine($"{tony.name}'s application status: {approvalStatus}");
class Person
{
    public string name;
    public double assetValue;
    public bool previousLoanExist;
    public Person(string name,
        double assetValue = 100000,
        bool previousLoanExist = false)
    {
        this.name = name;
        this.assetValue = assetValue;
        this.previousLoanExist = previousLoanExist;
    }
}
class Asset
{
    public bool HasSufficientAssetValue(Person person,
     double claimAmount)
    {
        Console.WriteLine($"Validating {person.name}'s
          asset value.");
        return person.assetValue >= claimAmount;
    }
}
class LoanStatus
{
    public bool HasPreviousLoans(Person person)
    {
        Console.WriteLine($"Verifying {person.name}'s
          existing loan(s).");
        return !person.previousLoanExist;
    }
}
class LoanApprover
{
    readonly Asset asset;
    readonly LoanStatus loanStatus;
    public LoanApprover()
    {
        asset = new Asset();
        loanStatus = new LoanStatus();
    }
    public string CheckLoanEligibility(Person person,
     double claimAmount)
    {
        string status = "Approved";
        string reason = String.Empty;
        Console.WriteLine($" Checking loan
         approval status of {person.name}.");
        // Using raw string literals in C# 11 (Preview)
        Console.WriteLine($"""
         [Current Status of the applicant:
          Asset value:${person.assetValue}.
          Claim amount:${claimAmount}.
          Has existing loan(s)?:
                      {person.previousLoanExist}.
         ]
         """);
        if (!asset.HasSufficientAssetValue(person,
         claimAmount))
        {
            status = "Not approved.";
            reason += " Insufficient balance.";
        }
        if (!loanStatus.HasPreviousLoans(person))
        {
            status = "Not approved.";
            reason += " Old loan exists.";
        }
        return string.Concat(status, " Remarks if
         any:", reason);
    }
}

Output

Here is the output:
***Simplifying the usage of a complex system using a
 facade.***
Checking loan approval status of Bob.
[Current Status of the applicant:
 Asset value:$5000.
 Claim amount:$20000.
 Has existing loan(s)?:True.
]
Validating Bob's asset value.
Verifying Bob's existing loan(s).
Bob's application status: Not approved.
Remarks if any:
Insufficient balance.
An old loan exists.
Checking loan approval status of Jack.
[Current Status of the applicant:
 Asset value:$100000.
 Claim amount:$30000.
 Has existing loan(s)?:False.
]
Validating Jack's asset value.
Verifying Jack's existing loan(s).
Jack's application status: Approved
Remarks if any:
Checking loan approval status of Tony.
[Current Status of the applicant:
 Asset value:$125000.
 Claim amount:$50000.
 Has existing loan(s)?:True.
]
Validating Tony's asset value.
Verifying Tony's existing loan(s).
Tony's application status: Not approved.
Remarks if any:
An old loan exists.

Analysis

Using facades, you get the following benefits:
  • You can create a simplified interface for your clients.

  • You reduce the number of objects that a client needs to deal with.

  • If there are a large number of subsystems, managing those subsystems with a facade can make communication easier.

Summary

This chapter shows you how to use a facade in an application. You learned that a facade can help you develop a simplified interface for your clients when you need to deal with many subsystems. I also discussed the difference between a facade and a template method. It’s worth remembering the following points before you consider using a facade in an application:
  • You should not assume that you can have only one facade in an application. You can use two or more of them if you find them useful.

  • One facade can show a different behavior from another facade. For example, you may allow or disallow direct access to subsystems. When you force the client to create instances through a facade, it is called an opaque facade. When you also allow direct access to the subsystems, it is called a transparent facade.

  • But what happens if you allow a client to talk to the subsystem classes directly? You make your code “dirty” and often expose the program logic.

  • On the contrary, when you restrict this direct communication, you add the costs for maintaining a separate layer. If a subsystem changes, you need to incorporate the corresponding behavior in the facade layer.

  • In addition, you need to test this layer before you deliver the product to a customer. If the facade is too complex, it can produce some additional maintenance costs.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.150.149