Chuyển tới nội dung chính

SOLID Principles và Best Practices

Mục tiêu bài học

Sau bài này, bạn sẽ:

  • Biết khi nào nên và không nên áp dụng design patterns
  • Hiểu anti-patterns phổ biến: over-engineering, pattern fever, God class
  • Nắm được nguyên tắc "Prefer composition over inheritance" trong thực tế
  • Áp dụng SOLID principles kết hợp với patterns cho code maintainable
  • Biết cách refactor code hiện tại sang patterns khi cần thiết

Bài trước: Behavioral Patterns — Đã học về Strategy, Observer, Template Method, Command. Bài này sẽ tổng kết best practices khi áp dụng design patterns trong dự án thực tế.

SOLID là 5 nguyên tắc cơ bản của lập trình hướng đối tượng, giúp code dễ hiểu, linh hoạt, và dễ bảo trì. Được giới thiệu bởi Robert C. Martin (Uncle Bob).

S - Single Responsibility Principle (SRP)

Định nghĩa

Một class chỉ nên có MỘT lý do để thay đổi. Mỗi class chỉ đảm nhận một trách nhiệm duy nhất.

Vi phạm SRP

// BAD: Class làm quá nhiều việc
public class User {
private String name;
private String email;

// 1. Business logic
public void register() {
// Validation
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("Invalid email");
}
// Save to database
saveToDatabase();
// Send email
sendWelcomeEmail();
}

// 2. Database logic
private void saveToDatabase() {
System.out.println("Saving to database...");
// JDBC code here
}

// 3. Email logic
private void sendWelcomeEmail() {
System.out.println("Sending welcome email to " + email);
// SMTP code here
}

// 4. Reporting
public void generateReport() {
System.out.println("Generating user report...");
}
}

Vấn đề:

  • Thay đổi database → phải sửa User class
  • Thay đổi email template → phải sửa User class
  • Thay đổi report format → phải sửa User class
  • Khó test, khó maintain

Tuân thủ SRP

// GOOD: Mỗi class một trách nhiệm

// 1. Domain model - chỉ chứa data và business rules
public class User {
private String name;
private String email;

public User(String name, String email) {
this.name = name;
this.email = email;
}

public void validate() {
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("Invalid email");
}
}

// Getters
public String getName() { return name; }
public String getEmail() { return email; }
}

// 2. Repository - trách nhiệm về persistence
public class UserRepository {
public void save(User user) {
System.out.println("Saving user to database: " + user.getName());
// JDBC code
}

public User findByEmail(String email) {
// Query database
return null;
}
}

// 3. Email service - trách nhiệm về email
public class EmailService {
public void sendWelcomeEmail(User user) {
System.out.println("Sending welcome email to: " + user.getEmail());
// SMTP code
}
}

// 4. Report generator - trách nhiệm về reporting
public class UserReportGenerator {
public void generateReport(User user) {
System.out.println("Generating report for: " + user.getName());
// Report generation logic
}
}

// 5. Service/Use case - orchestrate các components
public class UserRegistrationService {
private UserRepository repository;
private EmailService emailService;

public UserRegistrationService(UserRepository repository, EmailService emailService) {
this.repository = repository;
this.emailService = emailService;
}

public void register(User user) {
user.validate();
repository.save(user);
emailService.sendWelcomeEmail(user);
}
}

Lợi ích:

  • Mỗi class dễ hiểu, focused
  • Thay đổi một concern không ảnh hưởng các concerns khác
  • Dễ test từng phần riêng biệt
  • Dễ reuse (EmailService có thể dùng cho nhiều purposes)
Cách nhận biết vi phạm SRP
  • Class có tên chứa "And", "Or", "Manager" (UserAndEmailManager)
  • Class có quá nhiều dependencies
  • Class có quá nhiều methods không liên quan
  • Khi mô tả class, bạn dùng từ "và" nhiều lần

O - Open/Closed Principle (OCP)

Định nghĩa

Software entities nên OPEN for extension, nhưng CLOSED for modification.

Có thể thêm tính năng mới mà không sửa code hiện tại.

Vi phạm OCP

// BAD: Thêm payment method mới phải sửa code
public class PaymentProcessor {
public void processPayment(String type, double amount) {
if (type.equals("credit")) {
System.out.println("Processing credit card: $" + amount);
} else if (type.equals("paypal")) {
System.out.println("Processing PayPal: $" + amount);
} else if (type.equals("bitcoin")) {
System.out.println("Processing Bitcoin: $" + amount);
}
// Thêm payment method mới → phải sửa đây!
}
}

Tuân thủ OCP

// GOOD: Dùng abstraction + polymorphism

// Abstract interface
interface PaymentMethod {
void pay(double amount);
}

// Concrete implementations
class CreditCardPayment implements PaymentMethod {
@Override
public void pay(double amount) {
System.out.println("Processing credit card: $" + amount);
}
}

class PayPalPayment implements PaymentMethod {
@Override
public void pay(double amount) {
System.out.println("Processing PayPal: $" + amount);
}
}

class BitcoinPayment implements PaymentMethod {
@Override
public void pay(double amount) {
System.out.println("Processing Bitcoin: $" + amount);
}
}

// Processor (CLOSED for modification)
class PaymentProcessor {
public void processPayment(PaymentMethod paymentMethod, double amount) {
paymentMethod.pay(amount);
}
}

// Thêm payment method mới - KHÔNG sửa code cũ!
class ApplePayPayment implements PaymentMethod {
@Override
public void pay(double amount) {
System.out.println("Processing Apple Pay: $" + amount);
}
}

// Sử dụng
PaymentProcessor processor = new PaymentProcessor();
processor.processPayment(new CreditCardPayment(), 100.0);
processor.processPayment(new PayPalPayment(), 200.0);
processor.processPayment(new ApplePayPayment(), 150.0); // Mới thêm!

Ví dụ 2: Shape Area Calculator

// BAD
class AreaCalculator {
public double calculateArea(Object shape) {
if (shape instanceof Circle) {
Circle circle = (Circle) shape;
return Math.PI * circle.radius * circle.radius;
} else if (shape instanceof Rectangle) {
Rectangle rect = (Rectangle) shape;
return rect.width * rect.height;
}
// Thêm shape mới → phải sửa đây!
return 0;
}
}

// GOOD
interface Shape {
double calculateArea();
}

class Circle implements Shape {
private double radius;

public Circle(double radius) {
this.radius = radius;
}

@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}

class Rectangle implements Shape {
private double width;
private double height;

public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}

@Override
public double calculateArea() {
return width * height;
}
}

class Triangle implements Shape {
private double base;
private double height;

public Triangle(double base, double height) {
this.base = base;
this.height = height;
}

@Override
public double calculateArea() {
return 0.5 * base * height;
}
}

// Calculator (KHÔNG cần sửa khi thêm shape mới!)
class AreaCalculator {
public double calculateArea(Shape shape) {
return shape.calculateArea();
}
}
OCP qua Design Patterns
  • Strategy Pattern: Thêm algorithm mới không sửa context
  • Decorator Pattern: Thêm behavior mới không sửa component
  • Factory Pattern: Thêm product type mới không sửa factory interface

L - Liskov Substitution Principle (LSP)

Định nghĩa

Subtype phải có thể thay thế supertype mà không làm hỏng chương trình.

Nếu S là subtype của T, thì objects của type T có thể thay bằng objects của type S.

Vi phạm LSP

// BAD: Square vi phạm LSP
class Rectangle {
protected int width;
protected int height;

public void setWidth(int width) {
this.width = width;
}

public void setHeight(int height) {
this.height = height;
}

public int getArea() {
return width * height;
}
}

class Square extends Rectangle {
@Override
public void setWidth(int width) {
this.width = width;
this.height = width; // Force square constraint
}

@Override
public void setHeight(int height) {
this.width = height;
this.height = height; // Force square constraint
}
}

// Test code - Vi phạm LSP!
void testRectangle(Rectangle rect) {
rect.setWidth(5);
rect.setHeight(4);
assert rect.getArea() == 20; // Expected: 20
}

Rectangle rect = new Rectangle();
testRectangle(rect); // PASS: 5 * 4 = 20

Rectangle square = new Square();
testRectangle(square); // FAIL: 4 * 4 = 16 (không phải 20!)

Vấn đề: Square không thể thay thế Rectangle vì behavior khác!

Tuân thủ LSP

// GOOD: Dùng interface, không inheritance
interface Shape {
int getArea();
}

class Rectangle implements Shape {
private int width;
private int height;

public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}

public void setWidth(int width) {
this.width = width;
}

public void setHeight(int height) {
this.height = height;
}

@Override
public int getArea() {
return width * height;
}
}

class Square implements Shape {
private int side;

public Square(int side) {
this.side = side;
}

public void setSide(int side) {
this.side = side;
}

@Override
public int getArea() {
return side * side;
}
}

// Test code
void testShape(Shape shape) {
System.out.println("Area: " + shape.getArea());
}

testShape(new Rectangle(5, 4)); // Works
testShape(new Square(5)); // Works

Ví dụ 2: Bird

// BAD
class Bird {
public void fly() {
System.out.println("Flying...");
}
}

class Penguin extends Bird {
@Override
public void fly() {
throw new UnsupportedOperationException("Penguins can't fly!");
}
}

// Vi phạm LSP!
void makeBirdFly(Bird bird) {
bird.fly(); // Crash nếu là Penguin!
}

// GOOD
interface Bird {
void eat();
}

interface FlyingBird extends Bird {
void fly();
}

class Sparrow implements FlyingBird {
public void eat() {
System.out.println("Sparrow eating");
}

public void fly() {
System.out.println("Sparrow flying");
}
}

class Penguin implements Bird {
public void eat() {
System.out.println("Penguin eating");
}
// No fly() method!
}

void makeBirdFly(FlyingBird bird) {
bird.fly(); // Safe! Chỉ nhận FlyingBird
}
LSP Rules
  1. Preconditions không được strengthen trong subtype
  2. Postconditions không được weaken trong subtype
  3. Không throw exceptions mới không có trong supertype
  4. History constraint: Subtype không thể modify immutable properties của supertype

I - Interface Segregation Principle (ISP)

Định nghĩa

Clients không nên bị ép phụ thuộc vào interfaces mà chúng không sử dụng.

Nhiều interfaces nhỏ, focused tốt hơn một interface lớn, "fat".

Vi phạm ISP

// BAD: Fat interface
interface Worker {
void work();
void eat();
void sleep();
void getSalary();
}

// Human worker - OK
class HumanWorker implements Worker {
public void work() {
System.out.println("Working...");
}

public void eat() {
System.out.println("Eating lunch...");
}

public void sleep() {
System.out.println("Sleeping...");
}

public void getSalary() {
System.out.println("Getting salary");
}
}

// Robot worker - Vi phạm ISP!
class RobotWorker implements Worker {
public void work() {
System.out.println("Robot working...");
}

public void eat() {
// Robots don't eat!
throw new UnsupportedOperationException("Robots don't eat");
}

public void sleep() {
// Robots don't sleep!
throw new UnsupportedOperationException("Robots don't sleep");
}

public void getSalary() {
// Robots don't get paid!
throw new UnsupportedOperationException("Robots don't get salary");
}
}

Tuân thủ ISP

// GOOD: Nhiều interfaces nhỏ, focused

interface Workable {
void work();
}

interface Eatable {
void eat();
}

interface Sleepable {
void sleep();
}

interface Payable {
void getSalary();
}

// Human implements tất cả
class HumanWorker implements Workable, Eatable, Sleepable, Payable {
public void work() {
System.out.println("Working...");
}

public void eat() {
System.out.println("Eating lunch...");
}

public void sleep() {
System.out.println("Sleeping...");
}

public void getSalary() {
System.out.println("Getting salary");
}
}

// Robot chỉ implements cái cần thiết
class RobotWorker implements Workable {
public void work() {
System.out.println("Robot working 24/7...");
}
}

// Client code - chỉ depend vào interface cần thiết
class WorkManager {
public void manageWork(Workable worker) {
worker.work();
}
}

class PayrollManager {
public void processSalary(Payable employee) {
employee.getSalary();
}
}

Ví dụ 2: Printer

// BAD
interface Printer {
void print();
void scan();
void fax();
void copy();
}

class AllInOnePrinter implements Printer {
public void print() { /* ... */ }
public void scan() { /* ... */ }
public void fax() { /* ... */ }
public void copy() { /* ... */ }
}

class SimplePrinter implements Printer {
public void print() { /* ... */ }

public void scan() {
throw new UnsupportedOperationException(); // BAD!
}

public void fax() {
throw new UnsupportedOperationException(); // BAD!
}

public void copy() {
throw new UnsupportedOperationException(); // BAD!
}
}

// GOOD
interface Printable {
void print();
}

interface Scannable {
void scan();
}

interface Faxable {
void fax();
}

interface Copyable {
void copy();
}

class AllInOnePrinter implements Printable, Scannable, Faxable, Copyable {
public void print() { /* ... */ }
public void scan() { /* ... */ }
public void fax() { /* ... */ }
public void copy() { /* ... */ }
}

class SimplePrinter implements Printable {
public void print() { /* ... */ }
}

class PrinterWithScanner implements Printable, Scannable {
public void print() { /* ... */ }
public void scan() { /* ... */ }
}
ISP Benefits
  • Classes chỉ implement methods thực sự cần
  • Giảm coupling
  • Code dễ maintain và refactor
  • Tránh UnsupportedOperationException

D - Dependency Inversion Principle (DIP)

Định nghĩa

  1. High-level modules không nên depend vào low-level modules. Cả hai nên depend vào abstractions.
  2. Abstractions không nên depend vào details. Details nên depend vào abstractions.

Đơn giản: Depend on interfaces/abstractions, không depend vào concrete classes.

Vi phạm DIP

// BAD: High-level depend vào low-level

// Low-level module
class MySQLDatabase {
public void save(String data) {
System.out.println("Saving to MySQL: " + data);
}
}

// High-level module
class UserService {
private MySQLDatabase database; // Depend vào concrete class!

public UserService() {
this.database = new MySQLDatabase(); // Tight coupling!
}

public void saveUser(String userData) {
database.save(userData);
}
}

// Vấn đề: Muốn đổi sang PostgreSQL → phải sửa UserService!

Tuân thủ DIP

// GOOD: Cả hai depend vào abstraction

// Abstraction (interface)
interface Database {
void save(String data);
}

// Low-level modules implement abstraction
class MySQLDatabase implements Database {
@Override
public void save(String data) {
System.out.println("Saving to MySQL: " + data);
}
}

class PostgreSQLDatabase implements Database {
@Override
public void save(String data) {
System.out.println("Saving to PostgreSQL: " + data);
}
}

class MongoDatabase implements Database {
@Override
public void save(String data) {
System.out.println("Saving to MongoDB: " + data);
}
}

// High-level module depend vào abstraction
class UserService {
private Database database; // Depend vào interface!

// Dependency Injection via constructor
public UserService(Database database) {
this.database = database;
}

public void saveUser(String userData) {
database.save(userData);
}
}

// Sử dụng - flexible!
Database mysql = new MySQLDatabase();
UserService service1 = new UserService(mysql);
service1.saveUser("User data");

Database postgres = new PostgreSQLDatabase();
UserService service2 = new UserService(postgres);
service2.saveUser("User data");

// Đổi implementation không cần sửa UserService!

Dependency Injection (DI)

DIP thường được implement thông qua Dependency Injection.

class EmailService {
private final EmailProvider provider; // final = immutable

public EmailService(EmailProvider provider) {
this.provider = provider;
}

public void sendEmail(String to, String message) {
provider.send(to, message);
}
}

Ưu điểm: Dependencies rõ ràng, immutable, dễ test.

2. Setter Injection

class EmailService {
private EmailProvider provider;

public void setProvider(EmailProvider provider) {
this.provider = provider;
}

public void sendEmail(String to, String message) {
if (provider == null) {
throw new IllegalStateException("Provider not set");
}
provider.send(to, message);
}
}

Nhược điểm: Có thể quên set dependency.

3. Interface Injection (ít dùng)

interface EmailProviderSetter {
void setEmailProvider(EmailProvider provider);
}

class EmailService implements EmailProviderSetter {
private EmailProvider provider;

@Override
public void setEmailProvider(EmailProvider provider) {
this.provider = provider;
}
}
DIP với Frameworks
  • Spring: @Autowired, @Inject
  • Guice: Google's DI framework
  • Dagger: Compile-time DI
  • CDI: Java EE standard

Các Best Practices khác

1. DRY - Don't Repeat Yourself

Mỗi piece of knowledge nên có một representation duy nhất trong hệ thống.

// BAD: Duplicate code
class OrderService {
public void processOrder(Order order) {
if (order.getTotal() < 0) {
throw new IllegalArgumentException("Total cannot be negative");
}
if (order.getItems().isEmpty()) {
throw new IllegalArgumentException("Order must have items");
}
// Process...
}

public void updateOrder(Order order) {
if (order.getTotal() < 0) {
throw new IllegalArgumentException("Total cannot be negative");
}
if (order.getItems().isEmpty()) {
throw new IllegalArgumentException("Order must have items");
}
// Update...
}
}

// GOOD: Extract validation
class OrderService {
public void processOrder(Order order) {
validateOrder(order);
// Process...
}

public void updateOrder(Order order) {
validateOrder(order);
// Update...
}

private void validateOrder(Order order) {
if (order.getTotal() < 0) {
throw new IllegalArgumentException("Total cannot be negative");
}
if (order.getItems().isEmpty()) {
throw new IllegalArgumentException("Order must have items");
}
}
}

2. KISS - Keep It Simple, Stupid

Đơn giản tốt hơn phức tạp. Không over-engineer.

// BAD: Over-engineered
interface StringTransformerFactory {
StringTransformer createTransformer();
}

interface StringTransformer {
String transform(String input);
}

class UpperCaseTransformerFactory implements StringTransformerFactory {
public StringTransformer createTransformer() {
return new StringTransformer() {
public String transform(String input) {
return input.toUpperCase();
}
};
}
}

// GOOD: Simple and clear
class StringUtils {
public static String toUpperCase(String input) {
return input.toUpperCase();
}
}

3. YAGNI - You Aren't Gonna Need It

Không implement tính năng cho đến khi thực sự cần.

// BAD: Implement tính năng chưa cần
class User {
private String name;
private String email;
private String phone;
private String address;
private String facebookId; // Chưa dùng
private String twitterHandle; // Chưa dùng
private String linkedInId; // Chưa dùng
private List<String> tags; // Chưa dùng
private Map<String, Object> metadata; // Chưa dùng

// Hàng tá getters/setters không dùng đến
}

// GOOD: Chỉ implement cái đang cần
class User {
private String name;
private String email;

// Thêm fields sau khi thực sự cần
}

4. Composition over Inheritance

Ưu tiên composition (has-a) hơn inheritance (is-a).

// BAD: Inheritance hierarchy phức tạp
class Animal {
void eat() { /* ... */ }
}

class FlyingAnimal extends Animal {
void fly() { /* ... */ }
}

class SwimmingAnimal extends Animal {
void swim() { /* ... */ }
}

// Vấn đề: Duck vừa fly vừa swim → không biết extend cái nào?

// GOOD: Composition
interface Eatable {
void eat();
}

interface Flyable {
void fly();
}

interface Swimmable {
void swim();
}

class Duck {
private Eatable eatBehavior;
private Flyable flyBehavior;
private Swimmable swimBehavior;

public Duck(Eatable eatBehavior, Flyable flyBehavior, Swimmable swimBehavior) {
this.eatBehavior = eatBehavior;
this.flyBehavior = flyBehavior;
this.swimBehavior = swimBehavior;
}

public void performEat() {
eatBehavior.eat();
}

public void performFly() {
flyBehavior.fly();
}

public void performSwim() {
swimBehavior.swim();
}
}

Ưu điểm của Composition:

  • Linh hoạt hơn: có thể thay đổi behavior lúc runtime
  • Tránh inheritance hierarchy phức tạp
  • Tránh fragile base class problem

5. Favor Immutability

Ưu tiên immutable objects khi có thể.

// BAD: Mutable
class Point {
private int x;
private int y;

public void setX(int x) { this.x = x; }
public void setY(int y) { this.y = y; }
}

Point p = new Point();
p.setX(10);
p.setY(20);
// Ai cũng có thể modify p!

// GOOD: Immutable
class Point {
private final int x;
private final int y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}

public int getX() { return x; }
public int getY() { return y; }

// Return new instance thay vì modify
public Point move(int dx, int dy) {
return new Point(x + dx, y + dy);
}
}

Point p1 = new Point(10, 20);
Point p2 = p1.move(5, 5); // p1 unchanged!

Ưu điểm của Immutability:

  • Thread-safe
  • Dễ reason about code
  • Có thể cache và share safely
  • Tránh defensive copying

6. Tell, Don't Ask

Nói object làm gì, đừng hỏi state rồi tự làm.

// BAD: Ask then act
class BankAccount {
private double balance;

public double getBalance() {
return balance;
}

public void setBalance(double balance) {
this.balance = balance;
}
}

// Client code
if (account.getBalance() >= amount) {
account.setBalance(account.getBalance() - amount);
}

// GOOD: Tell
class BankAccount {
private double balance;

public void withdraw(double amount) {
if (balance < amount) {
throw new IllegalArgumentException("Insufficient funds");
}
balance -= amount;
}

public double getBalance() {
return balance;
}
}

// Client code
account.withdraw(amount); // Clean!

Bài tập tổng hợp: E-Commerce System

Yêu cầu

Thiết kế hệ thống e-commerce nhỏ với:

  1. Products: Có nhiều loại (Book, Electronic, Clothing)
  2. Shopping Cart: Thêm/xóa products
  3. Discount: Nhiều loại discount (Percentage, Fixed Amount, Buy X Get Y)
  4. Payment: Nhiều phương thức (Credit Card, PayPal, Crypto)
  5. Notification: Gửi email/SMS khi đặt hàng thành công
  6. Order Processing: Tạo order, apply discount, process payment, send notification

Áp dụng SOLID

  • SRP: Mỗi class một trách nhiệm (Product, Cart, DiscountCalculator, PaymentProcessor, NotificationService, OrderService)
  • OCP: Thêm discount/payment type mới không sửa code cũ
  • LSP: Subclasses có thể thay thế superclasses
  • ISP: Interfaces nhỏ, focused
  • DIP: Depend on abstractions, dùng dependency injection

Template code để bắt đầu

// Domain models
interface Product {
String getName();
double getPrice();
}

class Book implements Product {
private String name;
private double price;
private String author;

// Constructor, getters
}

// Shopping cart
class ShoppingCart {
private List<Product> items = new ArrayList<>();

public void addItem(Product product) {
items.add(product);
}

public double getTotal() {
return items.stream()
.mapToDouble(Product::getPrice)
.sum();
}

public List<Product> getItems() {
return new ArrayList<>(items); // Defensive copy
}
}

// Discount strategy
interface DiscountStrategy {
double applyDiscount(double total);
}

class PercentageDiscount implements DiscountStrategy {
private double percentage;

public PercentageDiscount(double percentage) {
this.percentage = percentage;
}

@Override
public double applyDiscount(double total) {
return total * (1 - percentage / 100);
}
}

// Payment strategy
interface PaymentMethod {
boolean pay(double amount);
}

// Notification service
interface NotificationService {
void sendOrderConfirmation(Order order);
}

// Order service (orchestrates everything)
class OrderService {
private PaymentMethod paymentMethod;
private NotificationService notificationService;

public OrderService(PaymentMethod paymentMethod,
NotificationService notificationService) {
this.paymentMethod = paymentMethod;
this.notificationService = notificationService;
}

public Order createOrder(ShoppingCart cart, DiscountStrategy discount) {
double total = cart.getTotal();
double finalAmount = discount.applyDiscount(total);

if (paymentMethod.pay(finalAmount)) {
Order order = new Order(cart.getItems(), finalAmount);
notificationService.sendOrderConfirmation(order);
return order;
} else {
throw new PaymentException("Payment failed");
}
}
}

Nhiệm vụ

  1. Hoàn thiện các classes trên
  2. Implement thêm:
    • 2 loại products khác
    • 2 discount strategies khác
    • 2 payment methods khác
    • Email và SMS notification services
  3. Viết main method để demo toàn bộ flow
  4. Ensure code tuân thủ tất cả SOLID principles

Tóm tắt

PrincipleMô tảBenefit
SRPMột class một trách nhiệmDễ hiểu, dễ maintain
OCPOpen for extension, closed for modificationDễ mở rộng, ổn định
LSPSubtype thay thế được supertypePolymorphism đúng đắn
ISPInterfaces nhỏ, focusedGiảm coupling, dễ implement
DIPDepend on abstractionsFlexible, testable
Best PracticeMô tả
DRYDon't repeat yourself
KISSKeep it simple
YAGNIYou aren't gonna need it
Composition over InheritanceƯu tiên has-a hơn is-a
Favor ImmutabilityImmutable objects an toàn hơn
Tell, Don't AskEncapsulation đúng nghĩa

Chúc mừng! Bạn đã hoàn thành Module 12: Design Patterns. Tiếp tục thực hành và áp dụng những kiến thức này vào projects thực tế để trở thành một developer giỏi hơn.

Đọc thêm