Master technical and career interviews with structured answers—short definition, real examples, pitfalls, and how to answer in 60–90 seconds.
The Singleton pattern ensures a class has only one instance and provides a global point of ccess to it. It’s commonly used when exactly one object is needed to coordinate actions cross the system, such as configuration s…
One common thread-safe implementation uses lazy initialization with Lazy<T>: public sealed class Singleton { private static readonly Lazy<Singleton> instance = new Lazy<Singleton>(() => new Singleton…
Global state: Singleton can lead to hidden dependencies and make testing difficult. Tight coupling: Other classes depend on the Singleton instance, reducing flexibility. Concurrency issues: If not implemented thread-safe…
container, though generally discouraged. What interviewers expect A clear definition tied to SOLID in Design Patterns & SOLID projects Trade-offs (performance, maintainability, security, cost) When you would and woul…
Singletons can hinder unit testing because they introduce global state, making tests dependent on a shared instance. This can cause tests to be flaky or order-dependent. To mitigate this, use interfaces and dependency in…
Eager Initialization: The Singleton instance is created at the time of class loading. It's simple but can waste resources if the instance is never used. Lazy Initialization: The instance is created only when it is first…
The Factory pattern is a creational design pattern that provides an interface for creating objects but allows subclasses or implementations to decide which class to instantiate. It’s used to encapsulate object creation,…
Factory Method: Defines an interface for creating an object but lets subclasses decide which class to instantiate. It uses inheritance and relies on subclass overriding. Abstract Factory: Provides an interface to create…
A simple example of Factory Method: // Product interface public interface IAnimal { void Speak(); } // Concrete Products public class Dog : IAnimal { public void Speak() => Console.WriteLine("Woof"); } public class Ca…
Encapsulates object creation: Decouples client code from concrete classes. Promotes code reuse: Centralizes object creation logic. Enhances maintainability: Adding new types requires minimal changes to existing code. Sup…
Yes! Factory pattern can complement Dependency Injection (DI) by abstracting complex object creation logic, especially when the creation involves runtime parameters or complex setup that DI containers can’t handle easily…
The Strategy pattern is a behavioral design pattern that defines a family of algorithms, encapsulates each one, and makes them interchangeable at runtime. It allows the algorithm to vary independently from clients that u…
Example: Payment strategy selection // Strategy Interface public interface IPaymentStrategy { void Pay(decimal amount); } // Concrete Strategies public class CreditCardPayment : IPaymentStrategy { public void Pay(decimal…
Answer: Payment gateways (Credit Card, PayPal, UPI, etc.) Sorting algorithms (QuickSort, MergeSort, BubbleSort) Authentication strategies (OAuth, JWT, LDAP) Compression algorithms (ZIP, RAR, TAR) Loggers (FileLogger, Con…
It promotes the Open/Closed Principle by allowing you to add new strategies (algorithms or behaviors) without modifying the existing code. The context class uses an interface for the strategy, so new behavior can be adde…
Aspect Strategy Pattern State Pattern Purpose Encapsulates interchangeable behaviors (algorithms). Encapsulates states and transitions between them. Client Control Client decides which strategy to use. Object changes its…
pplications? The Repository pattern abstracts the data access layer from the business logic by providing collection-like interface to access domain objects. It helps keep data access logic centralized and makes the codeb…
The Repository pattern abstracts the data access layer from the business logic by providing a collection-like interface to access domain objects. It helps keep data access logic centralized and makes the codebase easier…
Here's a basic example: // Entity public class Product { public int Id { get; set; } public string Name { get; set; } } // Generic Repository Interface public interface IRepository<T> where T : class { Task<IEnu…
Answer: Separation of concerns between business and data access layers Improved testability (can mock repositories) Centralized query logic for maintainability Easier to switch persistence implementations Promotes cleane…
Add custom methods in a specialized repository interface (e.g., IProductRepository) Use Specification pattern or LINQ expressions Inject DbContext into repository if needed for advanced queries Optionally, break out comp…
Over-abstraction: Can add unnecessary complexity for simple apps. Duplication: May duplicate what EF Core already provides (since EF is already a repository/unit-of-work pattern). Hides EF Core features: May obscure adva…
The Unit of Work pattern is a design pattern used to maintain a list of operations to be performed within a single transaction. It ensures that all operations either succeed or fail together, providing consistency and ma…
The Repository pattern abstracts the data access layer, providing a simplified interface to data operations. The Unit of Work pattern complements it by managing multiple repositories nd ensuring that all changes made thr…
In a .NET application (especially using Entity Framework), the Unit of Work is typically implemented around the DbContext, as it already tracks changes and handles transactions. Here's a simplified example: // IUnitOfWor…
Design Patterns & SOLID Design Patterns in C# · SOLID
The Singleton pattern ensures a class has only one instance and provides a global point of
ccess to it. It’s commonly used when exactly one object is needed to coordinate actions
cross the system, such as configuration settings, logging, or caching.
Design Patterns & SOLID Design Patterns in C# · SOLID
One common thread-safe implementation uses lazy initialization with Lazy<T>:
public sealed class Singleton
{
private static readonly Lazy<Singleton> instance = new
Lazy<Singleton>(() => new Singleton());
private Singleton() { }
public static Singleton Instance => instance.Value;
}
This approach ensures thread safety and lazy initialization without locks.
Design Patterns & SOLID Design Patterns in C# · SOLID
concurrent environments.
Design Patterns & SOLID Design Patterns in C# · SOLID
container, though generally discouraged.
In a production Design Patterns & SOLID application, teams apply this when handling user-facing features or integration boundaries. For example, you might use it during a sprint where reliability and observability matter—logging metrics, validating edge cases, and documenting the decision in an ADR so future developers understand why the approach was chosen.
Tip: Practice aloud on Toolliyo mock interview or the Interview Q&A section before your real interview.
Design Patterns & SOLID Design Patterns in C# · SOLID
Singletons can hinder unit testing because they introduce global state, making tests
dependent on a shared instance. This can cause tests to be flaky or order-dependent. To
mitigate this, use interfaces and dependency injection, or design the Singleton to allow
resetting its state for tests.
Design Patterns & SOLID Design Patterns in C# · SOLID
It's simple but can waste resources if the instance is never used.
resources but requires careful implementation for thread safety.
Factory pattern Q&A
Design Patterns & SOLID Design Patterns in C# · SOLID
The Factory pattern is a creational design pattern that provides an interface for creating
objects but allows subclasses or implementations to decide which class to instantiate. It’s
used to encapsulate object creation, promoting loose coupling and flexibility when the exact
types of objects aren’t known until runtime.
Use case: When a class can’t anticipate the class of objects it needs to create, or when you
want to delegate responsibility for object creation to subclasses.
Design Patterns & SOLID Design Patterns in C# · SOLID
decide which class to instantiate. It uses inheritance and relies on subclass
overriding.
objects without specifying their concrete classes. It uses composition and is useful
when you need to create multiple related objects together.
Design Patterns & SOLID Design Patterns in C# · SOLID
A simple example of Factory Method:
// Product interface
public interface IAnimal
{
void Speak();
}
// Concrete Products
public class Dog : IAnimal
{
public void Speak() => Console.WriteLine("Woof");
}
public class Cat : IAnimal
{
public void Speak() => Console.WriteLine("Meow");
}
// Factory
public class AnimalFactory
{
public static IAnimal CreateAnimal(string animalType)
{
return animalType.ToLower() switch
{
"dog" => new Dog(),
"cat" => new Cat(),
_ => throw new ArgumentException("Invalid animal type")
};
}
}
Usage:
var dog = AnimalFactory.CreateAnimal("dog");
dog.Speak(); // Outputs: Woof
Design Patterns & SOLID Design Patterns in C# · SOLID
code.
concrete types.
Design Patterns & SOLID Design Patterns in C# · SOLID
Yes! Factory pattern can complement Dependency Injection (DI) by abstracting complex
object creation logic, especially when the creation involves runtime parameters or complex
setup that DI containers can’t handle easily.
Example: Imagine a service that needs different data repositories based on a runtime
parameter.
public interface IRepository { void Save(); }
public class SqlRepository : IRepository { public void Save() =>
Console.WriteLine("Saving to SQL DB"); }
public class InMemoryRepository : IRepository { public void Save()
=> Console.WriteLine("Saving in Memory"); }
public interface IRepositoryFactory
{
IRepository CreateRepository(string repoType);
}
public class RepositoryFactory : IRepositoryFactory
{
public IRepository CreateRepository(string repoType)
{
return repoType.ToLower() switch
{
"sql" => new SqlRepository(),
"memory" => new InMemoryRepository(),
_ => throw new ArgumentException("Invalid repository
type")
};
}
}
// Consumer class with DI
public class Service
{
private readonly IRepositoryFactory _repositoryFactory;
public Service(IRepositoryFactory repositoryFactory)
{
_repositoryFactory = repositoryFactory;
}
public void SaveData(string repoType)
{
var repo = _repositoryFactory.CreateRepository(repoType);
repo.Save();
}
}
Here, DI injects the IRepositoryFactory while the factory manages object creation
based on runtime input. This promotes loose coupling and flexibility.
Strategy Design Pattern
Design Patterns & SOLID Design Patterns in C# · SOLID
The Strategy pattern is a behavioral design pattern that defines a family of algorithms,
encapsulates each one, and makes them interchangeable at runtime.
It allows the algorithm to vary independently from clients that use it.
Problem it solves:
voids large if-else or switch statements when selecting behavior and promotes
flexibility by decoupling the algorithm from the client using it.
Design Patterns & SOLID Design Patterns in C# · SOLID
Example: Payment strategy selection
// Strategy Interface
public interface IPaymentStrategy
{
void Pay(decimal amount);
}
// Concrete Strategies
public class CreditCardPayment : IPaymentStrategy
{
public void Pay(decimal amount) => Console.WriteLine($"Paid
{amount} using Credit Card");
}
public class PayPalPayment : IPaymentStrategy
{
public void Pay(decimal amount) => Console.WriteLine($"Paid
{amount} using PayPal");
}
// Context
public class PaymentContext
{
private IPaymentStrategy _paymentStrategy;
public PaymentContext(IPaymentStrategy paymentStrategy)
{
_paymentStrategy = paymentStrategy;
}
public void ExecutePayment(decimal amount)
{
_paymentStrategy.Pay(amount);
}
}
Usage:
var context = new PaymentContext(new PayPalPayment());
context.ExecutePayment(200); // Outputs: Paid 200 using PayPal
Design Patterns & SOLID Design Patterns in C# · SOLID
Answer: Payment gateways (Credit Card, PayPal, UPI, etc.) Sorting algorithms (QuickSort, MergeSort, BubbleSort) Authentication strategies (OAuth, JWT, LDAP) Compression algorithms (ZIP, RAR, TAR) Loggers (FileLogger, ConsoleLogger, DatabaseLogger)
In a production Design Patterns & SOLID application, teams apply this when handling user-facing features or integration boundaries. For example, you might use it during a sprint where reliability and observability matter—logging metrics, validating edge cases, and documenting the decision in an ADR so future developers understand why the approach was chosen.
Tip: Practice aloud on Toolliyo mock interview or the Interview Q&A section before your real interview.
Design Patterns & SOLID Design Patterns in C# · SOLID
It promotes the Open/Closed Principle by allowing you to add new strategies (algorithms
or behaviors) without modifying the existing code.
The context class uses an interface for the strategy, so new behavior can be added just by
creating a new class that implements the interface—no need to touch existing logic.
Design Patterns & SOLID Design Patterns in C# · SOLID
Aspect Strategy Pattern State Pattern
Purpose Encapsulates interchangeable
behaviors (algorithms).
Encapsulates states and transitions
between them.
Client
Control
Client decides which strategy to use. Object changes its own state
internally.
Behavior
Switch
Switched externally (e.g., passed as
parameter).
Switched internally (e.g., via
method call).
Example Payment method selection. Document lifecycle (Draft →
Published → Archived).
Repository Design Pattern
Design Patterns & SOLID Design Patterns in C# · SOLID
pplications?
The Repository pattern abstracts the data access layer from the business logic by providing
collection-like interface to access domain objects.
It helps keep data access logic centralized and makes the codebase easier to maintain,
test, and swap out data sources (e.g., switching from EF Core to Dapper or an API).
Design Patterns & SOLID Design Patterns in C# · SOLID
The Repository pattern abstracts the data access layer from the business logic by providing
a collection-like interface to access domain objects.
It helps keep data access logic centralized and makes the codebase easier to maintain,
test, and swap out data sources (e.g., switching from EF Core to Dapper or an API).
Design Patterns & SOLID Design Patterns in C# · SOLID
Here's a basic example:
// Entity
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
// Generic Repository Interface
public interface IRepository<T> where T : class
{
Task<IEnumerable<T>> GetAllAsync();
Task<T> GetByIdAsync(int id);
Task AddAsync(T entity);
void Update(T entity);
void Delete(T entity);
Task SaveAsync();
}
// EF Core implementation
public class Repository<T> : IRepository<T> where T : class
{
private readonly DbContext _context;
private readonly DbSet<T> _dbSet;
public Repository(DbContext context)
{
_context = context;
_dbSet = context.Set<T>();
}
public async Task<IEnumerable<T>> GetAllAsync() => await
_dbSet.ToListAsync();
public async Task<T> GetByIdAsync(int id) => await
_dbSet.FindAsync(id);
public async Task AddAsync(T entity) => await
_dbSet.AddAsync(entity);
public void Update(T entity) => _dbSet.Update(entity);
public void Delete(T entity) => _dbSet.Remove(entity);
public async Task SaveAsync() => await
_context.SaveChangesAsync();
}Design Patterns & SOLID Design Patterns in C# · SOLID
Answer: Separation of concerns between business and data access layers Improved testability (can mock repositories) Centralized query logic for maintainability Easier to switch persistence implementations Promotes cleaner and more organized code
In a production Design Patterns & SOLID application, teams apply this when handling user-facing features or integration boundaries. For example, you might use it during a sprint where reliability and observability matter—logging metrics, validating edge cases, and documenting the decision in an ADR so future developers understand why the approach was chosen.
Tip: Practice aloud on Toolliyo mock interview or the Interview Q&A section before your real interview.
Design Patterns & SOLID Design Patterns in C# · SOLID
IProductRepository)
public interface IProductRepository : IRepository<Product>
{
Task<IEnumerable<Product>> GetProductsWithLowStockAsync(int
threshold);
}Design Patterns & SOLID Design Patterns in C# · SOLID
repository/unit-of-work pattern).
projections.
value.
Unit of Work
Design Patterns & SOLID Design Patterns in C# · SOLID
The Unit of Work pattern is a design pattern used to maintain a list of operations to be
performed within a single transaction. It ensures that all operations either succeed or fail
together, providing consistency and managing changes to multiple business objects during a
transaction. It coordinates the writing out of changes and resolves potential concurrency
issues.
Design Patterns & SOLID Design Patterns in C# · SOLID
The Repository pattern abstracts the data access layer, providing a simplified interface to
data operations. The Unit of Work pattern complements it by managing multiple repositories
nd ensuring that all changes made through these repositories are committed in a single
transaction. This combination separates concerns, promotes clean architecture, and
maintains transactional integrity across multiple operations.
Design Patterns & SOLID Design Patterns in C# · SOLID
In a .NET application (especially using Entity Framework), the Unit of Work is typically
implemented around the DbContext, as it already tracks changes and handles
transactions. Here's a simplified example:
// IUnitOfWork.cs
public interface IUnitOfWork : IDisposable
{
IProductRepository Products { get; }
ICustomerRepository Customers { get; }
int Complete();
}
// UnitOfWork.cs
public class UnitOfWork : IUnitOfWork
{
private readonly AppDbContext _context;
public IProductRepository Products { get; private set; }
public ICustomerRepository Customers { get; private set; }
public UnitOfWork(AppDbContext context)
{
_context = context;
Products = new ProductRepository(_context);
Customers = new CustomerRepository(_context);
}
public int Complete()
{
return _context.SaveChanges(); // All changes in one
transaction
}
public void Dispose()
{
_context.Dispose();
}
}
Then register it using Dependency Injection in Startup.cs or Program.cs:
services.AddScoped<IUnitOfWork, UnitOfWork>();