Lộ trình 7 ngày

Ngày 2 — OOP và SOLID

4 tính chất OOP, Interface vs Abstract class, 5 nguyên lý SOLID kèm ví dụ C Sharp.

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

Encapsulation
Đóng gói
Che giấu chi tiết bên trong, chỉ expose những gì cần. Dùng private cho field, public cho property/method.
Abstraction
Trừu tượng
Tách cái gì làm khỏi làm như thế nào. Dùng interface hoặc abstract class.
Inheritance
Kế thừa
Class con dùng lại được code của class cha. Quan hệ "is-a".
Polymorphism
Đa hình
Một interface, nhiều cách hiện thực. 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()}");
Câu hỏi mẫu:"Đa hình runtime khác compile-time chỗ nào?"
  • 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ạnhinterfaceabstract class
Kế thừaClass implements nhiềuClass extends 1 duy nhất
Field/state❌ Không (đến C# 8 mới có default member)✅ Có
Constructor❌ Không✅ Có
Access modifierMặc định publicĐa dạng
Mục đíchCapability ("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");
}
Quy tắc thực dụng:
  • 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(); }
}
→ Dễ thay đổi: muốn Car chạy điện, chỉ cần inject 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
}
Mối liên hệ DIP ↔ DI: Dependency Inversion là nguyên lý, Dependency Injection là kỹ thuật để thực thi nguyên lý đó (constructor / setter / framework như ASP.NET Core).

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.

6. Câu hỏi tự test cuối ngày

  1. Cho ví dụ Encapsulation trong code mình từng viết.
  2. abstract methodvirtual method khác nhau chỗ nào?
  3. C# có hỗ trợ đa kế thừa class không? Còn interface?
  4. SOLID nào quan trọng nhất với fresher? Vì sao?
  5. Trong ASP.NET Core, DIP được áp dụng ở đâu?
  6. Pattern nào hay dùng cho data access? (→ Repository) ::
Đáp án chi tiết: xem Ngân hàng câu hỏi OOP.
➡️ Ngày mai: Ngày 3 — C# nâng cao

© 2026 .NET Fresher Guide. All rights reserved.

Về Trang Web

Hướng dẫn toàn diện để chuẩn bị phỏng vấn vị trí .NET Fresher với nội dung từ lý thuyết đến thực hành.