Ngày 2 — OOP và SOLID
Mục tiêu ngày 2
OOP và SOLID là câu hỏi gần như 100% được hỏi ở phỏng vấn fresher .NET. Sau ngày 2 bạn phải:
- Giải thích được 4 tính chất OOP + cho ví dụ C# cụ thể.
- Phân biệt rõ Interface vs Abstract class — khi nào dùng cái nào.
- Đọc/viết ví dụ cho mỗi nguyên lý SOLID.
- Trả lời được: "Composition over Inheritance là gì?"
1. Bốn tính chất OOP
private cho field, public cho property/method.interface hoặc abstract class.virtual/override (runtime) và overload (compile-time).Code ví dụ đủ 4 tính chất
// 1. Abstraction — định nghĩa hành vi, không định nghĩa cách
public abstract class Animal {
public string Name { get; } // 2. Encapsulation — set qua ctor
protected Animal(string name) => Name = name;
public abstract string Speak(); // hành vi trừu tượng
}
// 3. Inheritance — Dog "is-a" Animal
public class Dog : Animal {
public Dog(string name) : base(name) {}
public override string Speak() => "Woof!"; // 4. Polymorphism — override
}
public class Cat : Animal {
public Cat(string name) : base(name) {}
public override string Speak() => "Meow!";
}
// Sử dụng đa hình:
Animal[] zoo = { new Dog("Rex"), new Cat("Tom") };
foreach (var a in zoo) Console.WriteLine($"{a.Name}: {a.Speak()}");
- Compile-time (Method Overloading): cùng tên, khác signature — quyết định khi build.
- Runtime (Method Overriding):
virtualở cha,overrideở con — quyết định khi chạy qua bảng vtable. ::
2. Interface vs Abstract class
| Khía cạnh | interface | abstract class |
|---|---|---|
| Kế thừa | Class implements nhiều | Class extends 1 duy nhất |
| Field/state | ❌ Không (đến C# 8 mới có default member) | ✅ Có |
| Constructor | ❌ Không | ✅ Có |
| Access modifier | Mặc định public | Đa dạng |
| Mục đích | Capability ("can do X") | Type / Bản chất ("is-a") |
// Capability — bất kỳ thứ gì cũng có thể bay
public interface IFlyable { void Fly(); }
// Bản chất — Bird LÀ một Animal
public abstract class Animal {
public string Name { get; protected set; }
public abstract void Eat();
}
public class Bird : Animal, IFlyable {
public override void Eat() => Console.WriteLine("Eat seeds");
public void Fly() => Console.WriteLine("Flapping wings");
}
- Cần share code (method có body, field) →
abstract class. - Chỉ cần share hợp đồng (signature) →
interface. - Multiple inheritance →
interface. - Trong .NET hiện đại, ưu tiên
interface+ composition cho dễ test (mock). ::
3. Composition over Inheritance
// ❌ Lạm dụng kế thừa — Square IS-A Rectangle? Vi phạm LSP nếu Setter có side-effect
public class Rectangle { public virtual int Width { get; set; } ... }
public class Square : Rectangle { /* phải override cả Width và Height */ }
// ✅ Composition — Car HAS-A Engine
public class Engine { public void Start() {} }
public class Car {
private readonly Engine _engine;
public Car(Engine engine) => _engine = engine;
public void Drive() { _engine.Start(); }
}
ElectricEngine : IEngine.4. SOLID — 5 nguyên lý
S — Single Responsibility Principle
Một class chỉ có một lý do để thay đổi.
// ❌ Class này có 3 lý do để đổi: thay đổi format hóa đơn, đổi cách tính thuế, đổi cách gửi mail
public class Invoice {
public string Format() {...}
public decimal CalculateTax() {...}
public void SendByEmail() {...}
}
// ✅ Tách ra
public class Invoice { public decimal Total { get; set; } }
public class InvoiceFormatter { public string Format(Invoice i) {...} }
public class TaxCalculator { public decimal Calc(Invoice i) {...} }
public class InvoiceEmailService { public void Send(Invoice i) {...} }
O — Open/Closed Principle
Class mở để mở rộng, đóng để sửa đổi.
// ❌ Mỗi khi thêm shape mới phải sửa AreaCalculator
public class AreaCalculator {
public double Area(object shape) {
if (shape is Rectangle r) return r.W * r.H;
if (shape is Circle c) return Math.PI * c.R * c.R;
// Thêm Triangle? PHẢI SỬA file này.
throw new ArgumentException();
}
}
// ✅ Thêm shape mới chỉ cần tạo class mới, không động AreaCalculator
public abstract class Shape { public abstract double Area(); }
public class Rectangle : Shape { public override double Area() => W * H; }
public class Circle : Shape { public override double Area() => Math.PI * R * R; }
public class Triangle : Shape { public override double Area() => 0.5 * B * H; }
L — Liskov Substitution Principle
Subclass phải dùng được thay cho superclass mà không phá vỡ chương trình.
// ❌ Vi phạm — Penguin không bay được mà vẫn là Bird có Fly()
public class Bird { public virtual void Fly() {...} }
public class Penguin : Bird {
public override void Fly() => throw new NotSupportedException();
}
// ✅ Phân tách
public abstract class Bird {}
public interface IFlyable { void Fly(); }
public class Sparrow : Bird, IFlyable { public void Fly() {} }
public class Penguin : Bird { /* không IFlyable */ }
I — Interface Segregation Principle
Không bắt class implement những method nó không cần.
// ❌ "Fat" interface
public interface IWorker {
void Work();
void Eat();
void Sleep();
}
public class Robot : IWorker {
public void Work() {}
public void Eat() => throw new NotSupportedException(); // 🚫
public void Sleep() => throw new NotSupportedException();
}
// ✅ Tách nhỏ
public interface IWorkable { void Work(); }
public interface IFeedable { void Eat(); }
public interface ISleepable { void Sleep(); }
public class Robot : IWorkable {}
public class Human : IWorkable, IFeedable, ISleepable {}
D — Dependency Inversion Principle
Module cấp cao không phụ thuộc module cấp thấp. Cả hai cùng phụ thuộc abstraction.
// ❌ OrderService gắn cứng với SqlServerRepo
public class OrderService {
private SqlServerRepo _repo = new SqlServerRepo();
}
// ✅ Phụ thuộc interface, repo được inject vào
public interface IOrderRepository { void Save(Order o); }
public class SqlServerRepo : IOrderRepository { public void Save(Order o) {...} }
public class MongoRepo : IOrderRepository { public void Save(Order o) {...} }
public class OrderService {
private readonly IOrderRepository _repo;
public OrderService(IOrderRepository repo) => _repo = repo; // DI
}
5. Design pattern fresher hay được hỏi
public sealed class Logger {
private static readonly Lazy<Logger> _instance = new(() => new Logger());
public static Logger Instance => _instance.Value;
private Logger() {}
public void Log(string msg) { /*...*/ }
}
Trong ASP.NET Core: dùng services.AddSingleton<ILogger, Logger>() thay vì viết tay.
public interface IShape { void Draw(); }
public class Circle : IShape { public void Draw() {} }
public class Square : IShape { public void Draw() {} }
public class ShapeFactory {
public IShape Create(string type) => type switch {
"circle" => new Circle(),
"square" => new Square(),
_ => throw new ArgumentException()
};
}
public interface IDiscount { decimal Apply(decimal price); }
public class NoDiscount : IDiscount { public decimal Apply(decimal p) => p; }
public class TenPercent : IDiscount { public decimal Apply(decimal p) => p * 0.9m; }
public class StudentDiscount: IDiscount { public decimal Apply(decimal p) => p * 0.7m; }
public class Cart {
private readonly IDiscount _strategy;
public Cart(IDiscount strategy) => _strategy = strategy;
public decimal Checkout(decimal total) => _strategy.Apply(total);
}
public interface IUserRepository {
Task<User?> GetByIdAsync(int id);
Task AddAsync(User u);
}
public class EfUserRepository : IUserRepository {
private readonly AppDbContext _ctx;
public EfUserRepository(AppDbContext ctx) => _ctx = ctx;
public Task<User?> GetByIdAsync(int id) => _ctx.Users.FindAsync(id).AsTask();
public async Task AddAsync(User u) { _ctx.Users.Add(u); await _ctx.SaveChangesAsync(); }
}
6. Câu hỏi tự test cuối ngày
- Cho ví dụ Encapsulation trong code mình từng viết.
abstract methodvàvirtual methodkhác nhau chỗ nào?- C# có hỗ trợ đa kế thừa class không? Còn interface?
- SOLID nào quan trọng nhất với fresher? Vì sao?
- Trong ASP.NET Core, DIP được áp dụng ở đâu?
- Pattern nào hay dùng cho data access? (→ Repository) ::
➡️ Ngày mai: Ngày 3 — C# nâng cao
Ngày 1 — C# cơ bản
Stack/heap, value vs reference type, boxing/unboxing, string/StringBuilder, exception. Giải thích từ con số 0 cho intern → kiến thức nâng cao cho senior.
Ngày 3 — C# nâng cao
Generic internals, LINQ deferred execution, async/await state machine, delegate vs event, extension method. Giải thích sâu cho mọi level.