Interface Design
Well-designed interfaces are contracts that define clear, focused behaviors. They are the backbone of flexible LLD.
Interface Design Principles
1. Small and Focused
// BAD: Fat interface
public interface UserService {
User getUser(String id);
void createUser(User user);
void deleteUser(String id);
void sendEmail(String to, String msg);
List<Order> getUserOrders(String userId);
void generateReport();
}
// GOOD: Focused interfaces
public interface UserReader {
User getUser(String id);
List<User> findAll();
}
public interface UserWriter {
void createUser(User user);
void updateUser(User user);
void deleteUser(String id);
}
public interface UserNotifier {
void sendWelcomeEmail(User user);
}
2. Stable Abstractions
// Interface should change infrequently
public interface PaymentProcessor {
// Core methods that rarely change
PaymentResult process(Money amount, PaymentDetails details);
boolean refund(String transactionId, Money amount);
}
// Implementation can change frequently
public class StripeProcessor implements PaymentProcessor {
// Stripe-specific logic changes as Stripe API evolves
}
3. Program to Interface
// Good: Depends on abstraction
public class OrderService {
private final PaymentProcessor processor;
public OrderService(PaymentProcessor processor) {
this.processor = processor;
}
}
// Bad: Depends on concrete class
public class OrderService {
private final StripeProcessor processor; // Tightly coupled!
}
Interface Naming
| Pattern | Example | When |
|---|---|---|
| Adjective | Comparable, Serializable |
Capability |
| Noun | Repository, Factory |
Role |
| -able/-ible | Cacheable, Validatable |
Capability |
| I-prefix (C#) | IUserService |
Convention |
Interface Methods
public interface Repository<T> {
// Query methods
Optional<T> findById(String id);
List<T> findAll();
// Command methods
void save(T entity);
void delete(String id);
// Default methods (optional implementation)
default boolean exists(String id) {
return findById(id).isPresent();
}
// Static utility
static <T> Repository<T> empty() {
return new EmptyRepository<>();
}
}
Interface Segregation
ISP in LLD means designing interfaces that are small and focused, so implementers only need to implement what they use.
Segregation by Role
// Bad: One interface for all operations
public interface DataStore {
byte[] read(String key);
void write(String key, byte[] data);
void delete(String key);
List<String> list(String prefix);
void flush();
void close();
}
// Good: Segregated by use case
public interface Readable {
byte[] read(String key);
}
public interface Writable {
void write(String key, byte[] data);
}
public interface Deleteable {
void delete(String key);
}
public interface Listable {
List<String> list(String prefix);
}
// Implement only what you need
class ReadOnlyCache implements Readable {
public byte[] read(String key) { ... }
}
class FullStore implements Readable, Writable, Deleteable, Listable {
// Implements all
}
Segregation by Client
// Different clients need different interfaces
public interface AdminOperations {
void deleteUser(String id);
void resetPassword(String id);
void suspendAccount(String id);
}
public interface UserOperations {
User getProfile();
void updateProfile(User user);
}
public interface GuestOperations {
User login(String email, String password);
void register(User user);
}
// Each role implements only its interface
class AdminService implements AdminOperations { }
class UserService implements UserOperations { }
class AuthService implements GuestOperations { }
Segregation by Operation Type
// Read vs Write separation (CQRS lite)
public interface ReadModel {
Order getOrder(String id);
List<Order> getUserOrders(String userId);
}
public interface WriteModel {
void createOrder(Order order);
void cancelOrder(String orderId);
}
// Different implementations for different needs
class OrderQueryService implements ReadModel {
// Optimized for reads (uses cache, read replica)
}
public class OrderCommandService implements WriteModel {
// Optimized for writes (uses primary DB)
}
ISP Benefits
- Easier to implement: Fewer methods to code
- Easier to test: Mock only needed methods
- Easier to understand: Clear, focused contracts
- Better composability: Mix and match interfaces
- Less breaking: Changes affect fewer implementations
Dependency Inversion
DIP through interfaces creates loose coupling between high-level and low-level modules.
The Inversion
Traditional: DIP:
High → Low High → Interface ← Low
OrderService → MySQLDatabase OrderService → Database ← MySQLDatabase
(Tight coupling) (Loose coupling)
Interface as Contract
// Interface defines the contract
public interface NotificationService {
void send(User user, String message);
List<Notification> getHistory(String userId);
}
// Multiple implementations
public class EmailNotification implements NotificationService {
public void send(User user, String message) {
// Email implementation
}
}
public class SMSNotification implements NotificationService {
public void send(User user, String message) {
// SMS implementation
}
}
public class PushNotification implements NotificationService {
public void send(User user, String message) {
// Push implementation
}
}
// Consumer depends on interface, not implementation
public class OrderService {
private final NotificationService notifications;
public OrderService(NotificationService notifications) {
this.notifications = notifications;
}
public void completeOrder(Order order) {
// Process order...
notifications.send(order.getUser(), "Order complete!");
}
}
Interface-Based Testing
// Easy to test with mocks
public class OrderServiceTest {
@Test
void shouldNotifyUserOnOrderComplete() {
// Mock the interface
NotificationService mockNotifications = mock(NotificationService.class);
OrderService service = new OrderService(mockNotifications);
service.completeOrder(testOrder);
// Verify interaction
verify(mockNotifications).send(any(), contains("Order complete"));
}
}
Interface Hierarchies
// Base repository interface
public interface Repository<T> {
Optional<T> findById(String id);
void save(T entity);
}
// Extended for specific features
public interface SearchableRepository<T> extends Repository<T> {
List<T> search(String query);
}
public interface CacheableRepository<T> extends Repository<T> {
void invalidateCache(String id);
}
// Concrete interface combines capabilities
public interface ProductRepository extends SearchableRepository<Product>,
CacheableRepository<Product> {
List<Product> findByCategory(String category);
}
Common Interface Patterns in LLD
| Pattern | Interface | Purpose |
|---|---|---|
| Repository | UserRepository |
Data access |
| Service | PaymentService |
Business logic |
| Factory | NotificationFactory |
Object creation |
| Strategy | SortStrategy |
Algorithm selection |
| Observer | EventListener |
Event handling |
| Adapter | ExternalAPI |
Interface conversion |
Practice Problems
Design a scalable Interfaces in LLD system. Cover high-level architecture, data model, and API design.
Solution
// Complete system design:
// - Functional + Non-functional requirements
// - Capacity estimation
// - Data model (SQL/NoSQL choice)
// - API endpoints
// - Component architecture
// - Scaling strategy
// - Monitoring & reliabilityHow would you scale Interfaces in LLD to handle 10x the current load? Identify bottlenecks and solutions.
Solution
// Scaling approach:
// 1. Load balancing
// 2. Database sharding/replication
// 3. Cache layer (Redis)
// 4. CDN for static assets
// 5. Async processing (queues)
// 6. Microservices decompositionAnalyze potential failure modes for Interfaces in LLD and design mitigation strategies.
Solution
// Failure mitigation:
// 1. Redundancy (multi-AZ)
// 2. Circuit breakers
// 3. Retry with backoff
// 4. Dead letter queues
// 5. Health checks
// 6. Graceful degradationQuiz
1. What is the primary goal of interface segregation in LLD?
2. How does Dependency Inversion improve testability?
3. What is 'programming to interface'?
4. Which is an example of interface segregation?
5. What is an interface hierarchy?
Flashcards
Question
What are the key principles of interface design?
Click to reveal answer
Answer
Small and focused, stable abstractions, program to interface, clear naming, and appropriate method granularity.
Question
What is Interface Segregation?
Click to reveal answer
Answer
Splitting large interfaces into smaller, focused ones. Implementers only need to implement methods they actually use. Reduces coupling.
Question
How does Dependency Inversion work with interfaces?
Click to reveal answer
Answer
High-level modules depend on interfaces, not concrete implementations. Low-level modules implement the interfaces. Both depend on abstractions.
Question
What are common interface patterns in LLD?
Click to reveal answer
Answer
Repository (data access), Service (business logic), Factory (creation), Strategy (algorithms), Observer (events), Adapter (conversion).
Question
What is interface hierarchy?
Click to reveal answer
Answer
Extended interfaces that inherit from base interfaces. BaseRepository → SearchableRepository → ProductRepository. Each level adds specialized methods.
Revision Notes
Key Takeaways
- 1.Design interfaces to be small, focused, and stable
- 2.Apply ISP: split large interfaces into smaller, role-specific ones
- 3.DIP through interfaces enables loose coupling and easy testing
- 4.Program to interfaces throughout your design
- 5.Use interface hierarchies for specialized variations
Interview Tips
- •Show interface-based design when discussing flexibility
- •Demonstrate how interfaces enable easy testing through mocking
- •Explain ISP when discussing fat interface problems
- •Use interface hierarchies to show extensible designs
Cheat Sheet
Interfaces in LLD - Cheat Sheet
Interface Design:
- Small and focused (1-3 methods ideal)
- Stable (change infrequently)
- Program to interface, not implementation
- Clear naming: -able/-ible for capabilities
Interface Segregation:
- Split by role: AdminOps, UserOps, GuestOps
- Split by operation: Readable, Writable
- Split by client: each client gets its own
Dependency Inversion:
- High-level → Interface ← Low-level
- Enables mocking for testing
- Loose coupling between modules
Common Patterns:
| Pattern | Interface |
|---|---|
| Repository | UserRepository |
| Service | PaymentService |
| Factory | NotificationFactory |
| Strategy | SortStrategy |
| Observer | EventListener |