1. The Hook (The "Byte-Sized" Intro)
- In a Nutshell: MVC = Model (data) + View (UI) + Controller (logic), MVP = MVC variant (Presenter replaces Controller, testable), MVVM = Model + View + ViewModel (data binding), Dependency Injection = inject dependencies (loose coupling), Repository = data access abstraction.
- Benefits: Separation of concerns, testability, maintainability.
- Use: MVC for web apps, MVP for testable UI, MVVM for data-binding frameworks, DI everywhere, Repository for data access!
MVC = restaurant (kitchen=Model, menu=View, waiter=Controller). MVP = theatre (actors=Model, audience=View, director=Presenter). DI = hiring (company doesn't train employees, hires trained ones). Repository = library (abstract away book storage)!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy
- MVC: Restaurant service flow
- DI: Power outlet (plug in any device)
- Repository: Storage warehouse (hide how items are stored)
3. Technical Mastery (The "Deep Dive")
java
// ===========================================
// 1. MVC PATTERN
// ===========================================
// Model (data + business logic)
class UserModel {
private String name;
private String email;
String getName() { return name; }
void setName(String name) { this.name = name; }
String getEmail() { return email; }
void setEmail(String email) { this.email = email; }
}
// View (presentation)
class UserView {
void displayUser(String name, String email) {
System.out.println("Name: " + name);
System.out.println("Email: " + email);
}
}
// Controller (mediates Model + View)
class UserController {
private UserModel model;
private UserView view;
UserController(UserModel model, UserView view) {
this.model = model;
this.view = view;
}
void setUserName(String name) {
model.setName(name);
}
void updateView() {
view.displayUser(model.getName(), model.getEmail());
}
}
// Usage:
UserModel model = new UserModel();
model.setName("Alice");
model.setEmail("alice@example.com");
UserView view = new UserView();
UserController controller = new UserController(model, view);
controller.updateView();
// ===========================================
// 2. DEPENDENCY INJECTION
// ===========================================
// ❌ BAD: Tight coupling
class EmailService {
void send(String message) {
System.out.println("Email: " + message);
}
}
class Notification {
private EmailService emailService = new EmailService(); // ❌ Tightly coupled!
void notify(String message) {
emailService.send(message);
}
}
// ✅ GOOD: Dependency Injection
interface MessageService {
void send(String message);
}
class EmailService implements MessageService {
public void send(String message) {
System.out.println("Email: " + message);
}
}
class SMSService implements MessageService {
public void send(String message) {
System.out.println("SMS: " + message);
}
}
class Notification {
private MessageService messageService;
// Constructor injection
Notification(MessageService messageService) {
this.messageService = messageService;
}
void notify(String message) {
messageService.send(message);
}
}
// Usage:
MessageService emailService = new EmailService();
Notification notif = new Notification(emailService); // Inject dependency!
notif.notify("Hello");
// Switch to SMS easily:
MessageService smsService = new SMSService();
Notification smsNotif = new Notification(smsService);
// ===========================================
// 3. REPOSITORY PATTERN
// ===========================================
// Entity
class User {
private int id;
private String name;
User(int id, String name) {
this.id = id;
this.name = name;
}
int getId() { return id; }
String getName() { return name; }
}
// Repository interface
interface UserRepository {
User findById(int id);
List<User> findAll();
void save(User user);
void delete(int id);
}
// Concrete repository (in-memory)
class InMemoryUserRepository implements UserRepository {
private Map<Integer, User> users = new HashMap<>();
public User findById(int id) {
return users.get(id);
}
public List<User> findAll() {
return new ArrayList<>(users.values());
}
public void save(User user) {
users.put(user.getId(), user);
}
public void delete(int id) {
users.remove(id);
}
}
// Concrete repository (database)
class DatabaseUserRepository implements UserRepository {
public User findById(int id) {
// SELECT * FROM users WHERE id = ?
return null; // Simplified
}
public List<User> findAll() {
// SELECT * FROM users
return null; // Simplified
}
public void save(User user) {
// INSERT INTO users ...
}
public void delete(int id) {
// DELETE FROM users WHERE id = ?
}
}
// Service layer uses repository
class UserService {
private UserRepository repository;
UserService(UserRepository repository) {
this.repository = repository;
}
void createUser(String name) {
User user = new User(1, name);
repository.save(user);
}
User getUser(int id) {
return repository.findById(id);
}
}
// Usage: Switch between in-memory and database easily!
UserRepository repo = new InMemoryUserRepository();
UserService service = new UserService(repo);
service.createUser("Alice");
User user = service.getUser(1);5. The Comparison & Decision Layer
| Pattern | Use When |
|---|---|
| MVC | Web applications, clear separation of concerns |
| MVP | Testable UI (presenter can be unit tested) |
| MVVM | Data-binding frameworks (Angular, WPF) |
| Dependency Injection | Always! (loose coupling, testability) |
| Repository | Abstract data access layer |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "Benefits of Dependency Injection?" Answer:
- Loose coupling: Change implementation without modifying client
- Testability: Inject mocks for testing
- Flexibility: Switch implementations easily
java
// Without DI: Hard to test
class Service {
private Database db = new Database(); // Can't inject mock!
}
// With DI: Easy to test
class Service {
private Database db;
Service(Database db) { this.db = db; } // Inject mock!
}
// Test:
Database mockDb = mock(Database.class);
Service service = new Service(mockDb); // ✅ Testable!Pro-Tip: Spring Framework DI:
java
@Service
class UserService {
@Autowired // Spring injects dependency!
private UserRepository repository;
}
// Spring container manages dependencies
// No manual 'new' keyword needed!