Bridge pattern in C#
Hello, C# enthusiasts! 😍
Today, I want to share with you a very useful design pattern that can help you separate the business logic from the data access layer in your applications. This pattern is called the Bridge pattern, and it is one of the structural design patterns that deal with how classes and objects are composed to form larger structures. 🏗️
The Bridge pattern allows you to decouple an abstraction (such as an interface or an abstract class) from its implementation (such as a concrete class that implements the interface or inherits from the abstract class) so that the two can vary independently. This means that you can change the data access layer without affecting the business logic layer, and vice versa. 🙌
But why would you want to do that? Well, there are many scenarios where you might have more than one version of an abstraction, or more than one way of implementing an abstraction. For example, suppose you are developing a financial application that needs to perform different operations on different types of accounts (such as checking, saving, or credit). You could define an abstraction for the account, such as an interface called IAccount, and then have different implementations for each type of account, such as CheckingAccount, SavingAccount, and CreditAccount. However, this would lead to a large number of classes, and a tight coupling between the abstraction and the implementation. 😱
The Bridge pattern solves this problem by introducing another level of abstraction, called the Implementor, which defines the common operations for all implementations. Then, each concrete implementation inherits from the Implementor and provides its own specific behavior. The original abstraction (IAccount) then holds a reference to an Implementor object, and delegates the calls to it. This way, you can have a hierarchy of abstractions (such as DepositOperation, WithdrawOperation, TransferOperation) that are independent of the hierarchy of implementations (such as CheckingAccount, SavingAccount, CreditAccount). You can also easily add new abstractions or new implementations without breaking the existing code. 😎
Let’s see how this works in C# with a simple example. First, we define the Implementor interface:
public interface IAccountImplementor
{
// A method to get the balance of an account
decimal GetBalance();
// A method to update the balance of an account
void UpdateBalance(decimal amount);
}
public class CheckingAccount : IAccountImplementor
{
private decimal _balance;
public CheckingAccount(decimal initialBalance)
{
_balance = initialBalance;
}
public decimal GetBalance()
{
return _balance;
}
public void UpdateBalance(decimal amount)
{
_balance += amount;
}
}
public class CreditAccount : IAccountImplementor
{
private decimal _balance;
private readonly decimal _creditLimit;
public CreditAccount(decimal initialBalance, decimal creditLimit)
{
_balance = initialBalance;
_creditLimit = creditLimit;
}
public decimal GetBalance()
{
return _balance - _creditLimit;
}
public void UpdateBalance(decimal amount)
{
_balance += amount;
}
}
public class SavingAccount : IAccountImplementor
{
private decimal _balance;
private readonly decimal _interestRate;
public SavingAccount(decimal initialBalance, decimal interestRate)
{
_balance = initialBalance;
_interestRate = interestRate;
}
public decimal GetBalance()
{
return _balance * (1 + _interestRate);
}
public void UpdateBalance(decimal amount)
{
_balance += amount;
}
}
public interface IAccountOperation
{
// A property to get or set the implementor
IAccountImplementor Implementor { get; set; }
// A method to perform an operation on an account
void Perform();
}
public class DepositOperation : IAccountOperation
{
public IAccountImplementor Implementor { get; set; }
private readonly decimal _amount;
public DepositOperation(decimal amount)
{
_amount = amount;
}
public void Perform()
{
Console.WriteLine("Depositing " + _amount + " to the account");
Implementor.UpdateBalance(_amount);
Console.WriteLine("The new balance is " + Implementor.GetBalance());
}
}
public class WithdrawOperation : IAccountOperation
{
public IAccountImplementor Implementor { get; set; }
private readonly decimal _amount;
public WithdrawOperation(decimal amount)
{
_amount = amount;
}
public void Perform()
{
Console.WriteLine("Withdrawing " + _amount + " from the account");
Implementor.UpdateBalance(-_amount);
Console.WriteLine("The new balance is " + Implementor.GetBalance());
}
}
public class TransferOperation : IAccountOperation
{
public IAccountImplementor Implementor { get; set; }
private readonly IAccountImplementor _target;
private readonly decimal _amount;
public TransferOperation(IAccountImplementor target, decimal amount)
{
this._target = target;
this._amount = amount;
}
public void Perform()
{
Console.WriteLine("Transferring " + _amount + " from the source account to the target account");
Implementor.UpdateBalance(-_amount);
_target.UpdateBalance(_amount);
Console.WriteLine("The new balance of the source account is " + Implementor.GetBalance());
Console.WriteLine("The new balance of the target account is " + _target.GetBalance());
}
}
IAccountOperation[] operations = new IAccountOperation[3];
operations[0] = new DepositOperation(100);
operations[1] = new WithdrawOperation(50);
operations[2] = new TransferOperation(new SavingAccount(200, 0.05m), 25);
// Set the implementor for each operation based on the type of account
string accountType = "Checking"; // You can change this to "Saving" or "Credit"
switch (accountType)
{
case "Checking":
foreach (var operation in operations)
{
operation.Implementor = new CheckingAccount(100);
}
break;
case "Saving":
foreach (var operation in operations)
{
operation.Implementor = new SavingAccount(100, 0.05m);
}
break;
case "Credit":
foreach (var operation in operations)
{
operation.Implementor = new CreditAccount(100, 500);
}
break;
default:
Console.WriteLine("Invalid account type");
break;
}
// Perform the operations
foreach (var operation in operations)
{
operation.Perform();
Console.WriteLine();
}
I hope you enjoyed this article and learned something new about the Bridge pattern. If you want to learn more about this pattern, you can check out these resources:
- Bridge Design Pattern - GeeksforGeeks
- Bridge Design Pattern - Dofactory
- Bridge Financing - Overview, How It Works, Example
Happy coding! 😊
You can find the code examples here: source code
Comments
Post a Comment