Mastering C# Fundamentals: Abstract Classes vs Interfaces
mohamed Tayel
Posted on October 2, 2024
Meta Description: Discover the differences between abstract classes and interfaces in C# through a practical financial example. Learn when to use each and how to create flexible, maintainable software with assignments to reinforce your understanding
In C# programming, developers often find themselves deciding between using abstract classes and interfaces. Both serve the purpose of defining a common set of behaviors that multiple derived classes can implement, but they are not interchangeable. In this article, we'll explore what abstract classes and interfaces are, their differences, when to use each, and we'll use a financial example to illustrate their use in a real-world scenario.
What Is an Abstract Class?
An abstract class is a class that cannot be instantiated directly. It serves as a blueprint for other classes. Abstract classes allow you to define:
- Abstract Methods: Methods that have no implementation and must be implemented by derived classes.
- Concrete Methods: Methods with implementation that can be inherited and used by derived classes.
- Fields and Properties: State information that derived classes can use.
Abstract Class Example: Financial Perspective
Let's create an abstract class BankAccount
that represents a common blueprint for different types of bank accounts.
public abstract class BankAccount
{
public string AccountNumber { get; set; }
public decimal Balance { get; protected set; }
public BankAccount(string accountNumber, decimal initialBalance)
{
AccountNumber = accountNumber;
Balance = initialBalance;
}
// Abstract method that must be implemented by derived classes
public abstract void CalculateInterest();
// Concrete method shared across all accounts
public void Deposit(decimal amount)
{
Balance += amount;
Console.WriteLine($"{amount:C} deposited. Current balance: {Balance:C}");
}
public void Withdraw(decimal amount)
{
if (amount <= Balance)
{
Balance -= amount;
Console.WriteLine($"{amount:C} withdrawn. Current balance: {Balance:C}");
}
else
{
Console.WriteLine("Insufficient balance.");
}
}
}
In the BankAccount
class:
- The
CalculateInterest
method is abstract—it must be implemented by derived classes. - The
Deposit
andWithdraw
methods are concrete—they are implemented and shared among all accounts.
What Is an Interface?
An interface defines a contract that implementing classes must follow. Interfaces only contain method signatures—there is no implementation. Interfaces are useful when you want multiple classes to share a common set of behaviors without sharing an implementation.
Interface Example: Financial Perspective
Let’s define interfaces that represent certain capabilities:
public interface ILoanAccount
{
void ApplyForLoan(decimal amount);
void PayLoan(decimal amount);
}
public interface IOnlineBanking
{
void TransferFunds(string toAccount, decimal amount);
void CheckOnlineBalance();
}
These interfaces define behaviors (ApplyForLoan
, TransferFunds
, etc.) that can be shared by different types of accounts.
Combining Abstract Classes and Interfaces: Financial Example
Let’s create two classes: SavingsAccount
and CreditAccount
. Each inherits from BankAccount
and implements relevant interfaces.
SavingsAccount: Inherits BankAccount
and Implements IOnlineBanking
public class SavingsAccount : BankAccount, IOnlineBanking
{
public SavingsAccount(string accountNumber, decimal initialBalance)
: base(accountNumber, initialBalance)
{
}
public override void CalculateInterest()
{
decimal interest = Balance * 0.03m;
Balance += interest;
Console.WriteLine($"Interest added: {interest:C}. New balance: {Balance:C}");
}
public void TransferFunds(string toAccount, decimal amount)
{
if (amount <= Balance)
{
Balance -= amount;
Console.WriteLine($"{amount:C} transferred to account {toAccount}. Current balance: {Balance:C}");
}
else
{
Console.WriteLine("Insufficient balance for transfer.");
}
}
public void CheckOnlineBalance()
{
Console.WriteLine($"The current balance for account {AccountNumber} is {Balance:C}");
}
}
CreditAccount: Inherits BankAccount
and Implements ILoanAccount
public class CreditAccount : BankAccount, ILoanAccount
{
public decimal LoanBalance { get; private set; }
public CreditAccount(string accountNumber, decimal initialBalance)
: base(accountNumber, initialBalance)
{
LoanBalance = 0;
}
public override void CalculateInterest()
{
decimal interest = LoanBalance * 0.05m;
LoanBalance += interest;
Console.WriteLine($"Interest on loan added: {interest:C}. Current loan balance: {LoanBalance:C}");
}
public void ApplyForLoan(decimal amount)
{
LoanBalance += amount;
Console.WriteLine($"{amount:C} loan approved. Current loan balance: {LoanBalance:C}");
}
public void PayLoan(decimal amount)
{
if (amount <= LoanBalance)
{
LoanBalance -= amount;
Console.WriteLine($"{amount:C} paid towards loan. Current loan balance: {LoanBalance:C}");
}
else
{
Console.WriteLine("Overpayment not allowed.");
}
}
}
Demonstration
Let’s see how we can use these classes:
public class Program
{
public static void Main(string[] args)
{
// Create a SavingsAccount
SavingsAccount savings = new SavingsAccount("SA12345", 1000m);
savings.Deposit(500m);
savings.CalculateInterest();
savings.TransferFunds("SA54321", 200m);
savings.CheckOnlineBalance();
Console.WriteLine();
// Create a CreditAccount
CreditAccount credit = new CreditAccount("CA98765", 2000m);
credit.ApplyForLoan(1000m);
credit.CalculateInterest();
credit.PayLoan(500m);
credit.Withdraw(1000m);
}
}
Output
$500.00 deposited. Current balance: $1,500.00
Interest added: $45.00. New balance: $1,545.00
$200.00 transferred to account SA54321. Current balance: $1,345.00
The current balance for account SA12345 is $1,345.00
$1,000.00 loan approved. Current loan balance: $1,000.00
Interest on loan added: $50.00. Current loan balance: $1,050.00
$500.00 paid towards loan. Current loan balance: $550.00
$1,000.00 withdrawn. Current balance: $1,000.00
Abstract Class vs Interface: Key Differences
Feature | Abstract Class | Interface |
---|---|---|
Inheritance | Can inherit only one abstract class. | Can implement multiple interfaces. |
Methods | Can have both abstract and concrete methods. | Can only have abstract methods (except for default implementations). |
Fields | Can contain fields. | Cannot have fields. |
Access Modifiers | Can use different access modifiers. | All members are implicitly public. |
Constructor | Can have constructors. | Cannot have constructors. |
Use Case | Used when classes share a common base and behavior. | Used for defining capabilities that multiple classes can share. |
When to Use Abstract Class vs Interface
Use Abstract Class When:
- You have shared code or default behavior that should be inherited by all derived classes.
- You need to define fields or properties.
- You want to provide a common base class that multiple classes can share.
Use Interface When:
- You need to define a set of capabilities or behaviors that can be shared by multiple unrelated classes.
- You want to use multiple inheritance (a class can implement several interfaces, while it can only inherit from one abstract class).
- You want to ensure that a class provides specific functionality but don't care how that functionality is implemented.
Assignments
To reinforce your understanding of abstract classes and interfaces in a financial context, try the following exercises:
Easy Level
- Create an abstract class called
FinancialAccount
with propertiesAccountHolderName
andBalance
. - Add an abstract method called
CalculateFee()
and a concrete method calledDeposit()
to theFinancialAccount
class. - Implement a
FixedDepositAccount
class that inherits fromFinancialAccount
and provides an implementation forCalculateFee()
.
Medium Level
- Create two interfaces:
IOverdraftAccount
with methodsRequestOverdraft()
andRepayOverdraft()
, andISavingsBonus
with a methodAddBonus()
. - Create a class
SavingsPlusAccount
that inherits from theFinancialAccount
class and implements bothIOverdraftAccount
andISavingsBonus
interfaces. - Write a short program to create an instance of
SavingsPlusAccount
and call all the implemented methods.
Difficult Level
- Create an abstract class
InvestmentAccount
with methodsInvest()
and an abstract methodCalculateReturns()
. - Create two interfaces:
IRiskAssessment
with a methodAssessRisk()
andIDividend
with a methodPayDividend()
. - Implement a class
StockInvestmentAccount
that inherits fromInvestmentAccount
and implements both interfaces, providing concrete implementations for all methods. - Write a program that creates multiple types of investment accounts (
StockInvestmentAccount
,BondInvestmentAccount
, etc.), demonstrates shared functionality, and shows how different investment
products handle risk assessment and dividend payments.
Summary
- Abstract Classes provide a shared template for different entities, allowing common code and abstract methods to be inherited.
- Interfaces define specific capabilities that various classes can have, allowing for multiple inheritance of behaviors.
- By combining abstract classes and interfaces, you can create financial software that is robust, reusable, and easy to maintain.
In this example, we used abstract classes and interfaces to develop a flexible financial system involving different account types with distinct capabilities. Understanding when to use each will help you design software that is scalable and easy to understand.
Posted on October 2, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.