๐Ÿ—๏ธ

Abstraction

Object-Oriented Programming Intermediate 2 min read 200 words

Hide complexity and expose only essential interfaces to simplify how we interact with objects

Abstraction

Abstraction is the process of hiding implementation complexity and exposing only the essential interface. It allows you to focus on what an object does rather than how it does it.

Why Abstraction Matters

Consider a car: you donโ€™t need to understand the internal combustion engine to drive. You just use the steering wheel, pedals, and gear shift. The complexity is abstracted away.

In software, abstraction:

  • Reduces complexity for consumers of your code
  • Allows implementation changes without affecting callers
  • Creates cleaner, more maintainable APIs
  • Enables better separation of concerns

Abstraction Through Interfaces

Interfaces define a contract - they specify what methods a class must implement without dictating how.

// The interface defines WHAT operations are available
public interface IPaymentProcessor
{
    Task<PaymentResult> ProcessAsync(PaymentDetails details);
}

// Implementation details are hidden
public class CreditCardProcessor : IPaymentProcessor
{
    private readonly ICardValidator _validator;
    private readonly IFraudDetection _fraudDetection;
    private readonly IEncryptionService _encryption;

    public CreditCardProcessor(
        ICardValidator validator,
        IFraudDetection fraudDetection,
        IEncryptionService encryption)
    {
        _validator = validator;
        _fraudDetection = fraudDetection;
        _encryption = encryption;
    }

    public async Task<PaymentResult> ProcessAsync(PaymentDetails details)
    {
        // Complex implementation hidden from callers
        await _validator.ValidateAsync(details);
        await _fraudDetection.CheckAsync(details);
        var encryptedData = _encryption.Encrypt(details);

        // Process payment...
        return new PaymentResult { Success = true };
    }
}

// Consumer doesn't need to know about validation, fraud detection, etc.
public class CheckoutService
{
    private readonly IPaymentProcessor _paymentProcessor;

    public CheckoutService(IPaymentProcessor paymentProcessor)
    {
        _paymentProcessor = paymentProcessor;
    }

    public async Task CompleteCheckoutAsync(Order order)
    {
        // Simple, clean interface
        var result = await _paymentProcessor.ProcessAsync(order.PaymentDetails);
        if (result.Success)
        {
            // Continue with order...
        }
    }
}

Abstraction Through Abstract Classes

Abstract classes provide partial implementation along with a contract.

public abstract class BaseRepository<T> where T : class
{
    protected DbContext Context { get; }

    protected BaseRepository(DbContext context)
    {
        Context = context;
    }

    // Abstract methods - must be implemented by derived classes
    public abstract Task<T> GetByIdAsync(int id);
    public abstract Task<List<T>> GetAllAsync();

    // Virtual methods - can be overridden, but have default implementation
    protected virtual void ValidateEntity(T entity)
    {
        if (entity == null)
            throw new ArgumentNullException(nameof(entity));
    }

    // Concrete methods - shared implementation
    public async Task AddAsync(T entity)
    {
        ValidateEntity(entity);
        await Context.Set<T>().AddAsync(entity);
        await Context.SaveChangesAsync();
    }
}

// Derived class provides specific implementations
public class ProductRepository : BaseRepository<Product>
{
    public ProductRepository(DbContext context) : base(context) { }

    public override async Task<Product> GetByIdAsync(int id)
    {
        return await Context.Set<Product>()
            .Include(p => p.Category)
            .FirstOrDefaultAsync(p => p.Id == id);
    }

    public override async Task<List<Product>> GetAllAsync()
    {
        return await Context.Set<Product>()
            .Include(p => p.Category)
            .ToListAsync();
    }
}

Levels of Abstraction

Good software design has multiple abstraction layers:

Level 5: API Layer (Highest abstraction)
    [HttpPost("complete")]
    public Task<IActionResult> CompleteOrder(OrderRequest request)

        โ†“

Level 4: Service Layer
    public class OrderService
    {
        public Task CompleteOrderAsync(Order order)
    }

        โ†“

Level 3: Interface
    public interface IPaymentProcessor
    {
        Task<PaymentResult> ProcessAsync(PaymentDetails details);
    }

        โ†“

Level 2: Public Method Implementation
    public async Task<PaymentResult> ProcessAsync(PaymentDetails details)

        โ†“

Level 1: Private Implementation Details (Lowest abstraction)
    private void ValidateBankConnection() { /* 100 lines */ }
    private void EncryptSensitiveData() { /* 50 lines */ }

Bad vs Good Abstraction

// Bad: Over-complicated interface exposing too many details
public class PaymentProcessor
{
    public void ProcessCreditCard(
        string cardNumber,
        string cvv,
        string expiry,
        decimal amount,
        string name,
        string email,
        string address,
        int zipCode,
        string country,
        bool is3DSecure,
        string bankCode)
    {
        // Too many parameters!
        // Implementation details leak through the interface
    }
}

// Good: Clean abstraction with encapsulated details
public interface IPaymentProcessor
{
    Task<PaymentResult> ProcessAsync(PaymentDetails details);
}

public class PaymentDetails
{
    public string CardNumber { get; set; }
    public string CVV { get; set; }
    public string Expiry { get; set; }
    public decimal Amount { get; set; }
    public BillingAddress Address { get; set; }
}

Interview Tips

Common Questions:

  • โ€œExplain abstraction with an example from your experienceโ€
  • โ€œWhatโ€™s the difference between abstraction and encapsulation?โ€
  • โ€œWhen would you use an interface vs. an abstract class?โ€

Key Points to Remember:

  1. Abstraction focuses on hiding complexity
  2. It defines what operations are available, not how they work
  3. Interfaces provide pure abstraction; abstract classes provide partial implementation
  4. Good abstraction makes code easier to use, test, and maintain
  5. Abstraction vs Encapsulation: Abstraction hides complexity; encapsulation hides data

Real-World Examples:

  • IEnumerable<T> - abstracts how collections are iterated
  • Stream - abstracts different data sources (file, network, memory)
  • DbContext - abstracts database operations

๐Ÿ“š Related Articles