CodeGym /Các khóa học /C# SELF /Mẹo về style và tính dễ đọc cho code OOP

Mẹo về style và tính dễ đọc cho code OOP

C# SELF
Mức độ, Bài học
Có sẵn

1. Giới thiệu

"Code sạch" không phải là con bò thần thánh gì đâu, mà là công cụ sinh tồn thực sự cho dev. Trong bất kỳ dự án OOP nào, dù đẹp đến đâu, class, field, method, các mối liên kết phức tạp sẽ chất đống rất nhanh... Nếu phá vỡ tính thẩm mỹ và cấu trúc, chỉ sau một tuần chính bạn cũng không giải nổi code của mình. Đọc thêm về "cuộc chiến sinh tồn" này trong sách của Robert Martin, "Code sạch" — đúng kiểu những trận chiến như vậy.

Style trong code không phải là "ai thích gì làm nấy", mà là để mọi người đều dễ thở hơn:

  • Đồng nghiệp (hoặc chính bạn) có thể hiểu nhanh chuyện gì đang diễn ra.
  • Lỗi (nhất là lỗi kiến trúc) lộ ra ngay.
  • Code dễ sửa, ít lỗi hơn khi phát triển tiếp.

Cùng xem làm sao để code OOP của bạn được reviewer, đồng nghiệp và cả linter yêu thích nhé.

2. Đặt tên: tuyến phòng thủ đầu tiên của bạn

Đặt tên class

Class trong C# nên đặt theo PascalCase (mỗi phần của từ bắt đầu bằng chữ hoa, ví dụ: MyNewClass) và tên phải trả lời rõ ràng "Nó là cái gì?". Tên nên là danh từ!

public class TaiKhoanSinhVien { /* ... */ }
public class TaoHoaDon { /* ... */ }

Tệ:

class lamPhepMau { ... } // Tệ: Phep mau gì vậy? Sai PascalCase.

Đặt tên method

Method — cũng dùng PascalCase, nhưng nên dùng động từ + đối tượng tác động:

public void InBaoCao() { ... }
public string LayTenDinhDang() { ... }

Method nên thể hiện hành động (In, Lay, Luu, TinhToan v.v.), để đọc vào là biết nó làm gì.

Đặt tên field và property

Field thường là private, đặt tên bằng chữ thường theo camelCase, hay có dấu gạch dưới:

private int _soLuong;
private SinhVien _chuSoHuu;

Property — PascalCase, vì là phần giao diện bên ngoài của class:

public int SoDu { get; set; }

Biến

Biến cục bộ — camelCase, càng ngắn gọn, dễ hiểu theo ngữ cảnh càng tốt:

string tenNhapVao;
int soLuongSinhVien;

Và làm ơn, các biến kiểu a1, ketQua2, giDo — chỉ dùng nếu bạn muốn tự tạo quest cho tháng sau thôi.

3. Tổ chức cấu trúc class

Sắp xếp thành viên class hợp lý giúp dễ tìm kiếm và hiểu nhanh thứ tự các phần.

Thường sắp xếp như sau:

  1. Constructor
  2. Property
  3. Method
  4. Kiểu lồng nhau (enum, class v.v.)

Ví dụ:


public class SinhVien
{
    // --- Field ---
    private string _ten;

    // --- Constructor ---
    public SinhVien(string ten)
    {
        _ten = ten;
    }

    // --- Property ---
    public string Ten
    {
        get => _ten;
        set => _ten = value;
    }

    // --- Method ---
    public void InThongTin()
    {
        Console.WriteLine($"Tên: {_ten}");
    }
}

Các "block" này nên tách bằng comment (// --- Method ---), nhất là với class dài. JetBrains Rider, Visual Studio và các IDE khác cho phép gập/mở nhanh các section.

4. Comment và tài liệu hóa

Comment là tốt. Nhưng nếu lạm dụng hoặc viết "giải thích cho code khó hiểu" trong khi có thể sửa lại code cho dễ hiểu thì lại dở!

Comment tốt — là comment giải thích "tại sao", không phải "cái gì".

// Dùng Guid làm mã định danh duy nhất vì hệ thống phân tán
public Guid MaSo { get; set; }

Tài liệu hóa method, class và property

Dùng xml-doc cho class và method public. IDE sẽ hiện mô tả này khi hover chuột.


/// <summary>
/// Đại diện cho sinh viên đại học.
/// </summary>
public class SinhVien
{
    /// <summary>
    /// Tên sinh viên.
/// </summary>
    public string Ten { get; set; }
}

Không nên comment gì

  • Những thứ đơn giản (i++ // tăng i lên 1).
  • Biến đặt tên tệ ("// ở đây có gì đó xảy ra" — ừ, nhưng là gì?).

5. Chia nhỏ để dễ kiểm soát

Class và method nhỏ gọn

Quy tắc vàng: một class — một trách nhiệm (xem Single Responsibility Principle). Nếu class SinhVien vừa quản lý điểm, vừa xử lý email, vừa quản lý lịch học — có gì đó sai sai rồi.

  • Class tầm 300-400 dòng là ổn. Dài hơn thì nên xem lại.
  • Method tầm 15-20 dòng là dễ đọc. Trừ khi là method xử lý case lớn.

Ví dụ method "phình to":


public void XuLy()
{
    // Thông báo cho khách hàng
    // Lưu thay đổi
    // Gửi email
    // Ghi log
    // ... (15 bước)
}

Tốt hơn:


public void XuLy()
{
    ThongBaoKhachHang();
    LuuThayDoi();
    GuiEmail();
    GhiLog();
}

Mỗi bước tách thành method riêng, code gọn hơn và dễ test hơn.

6. Mẹo hữu ích

Cấu trúc trực quan: format, thụt lề, dòng trống

IDE có thể tự format code đẹp (Ctrl+K, D trong Visual Studio, Ctrl+Alt+L trong Rider), nhưng vẫn nên biết nguyên tắc.

  • THỤT LỀ — 4 dấu cách. Không dùng tab, không dùng 2 dấu cách.
  • DÒNG TRỐNG — tách method với nhau, field với property, property với method.
  • DẤU NGOẶC luôn ở dòng mới cho class và method (kiểu Allman):

public class KiemTra
{
    public void In()
    {
        Console.WriteLine("Xin chào");
    }
}

Thành viên "mạnh" và "yếu": access modifier

Luôn cố gắng đóng càng nhiều càng tốt: chỉ mở cái gì thực sự cần thiết. Nếu field hay method chỉ dùng trong class — để private. Nếu cần cho class con — protected. public — chỉ cho contract.

Tệ:

public string ChuoiKetNoi; // Ai cũng có thể sửa!

Tốt hơn:


private string _chuoiKetNoi;
public string ChuoiKetNoi
{
    get => _chuoiKetNoi;
    private set => _chuoiKetNoi = value;
}

Dùng auto property

Có auto property và init-only setter rồi thì viết property "thủ công" là lỗi thời.

Ví dụ:


public string Ten { get; set; } // Quá ổn!
public int Tuoi { get; init; }    // Chỉ cho khởi tạo, an toàn hơn.

Nếu cần property tính toán:


public string HoTenDayDu => $"{Ho} {Ten}";

Encapsulation và getter/setter

Nếu property có ý nghĩa nghiệp vụ khi thay đổi hoặc cần kiểm soát, hãy dùng field private + getter/setter public có logic.


private int _diem;
public int Diem
{
    get => _diem;
    set
    {
        if (value < 0) _diem = 0;
        else if (value > 100) _diem = 100;
        else _diem = value;
    }
}

Như vậy bạn (hoặc người khác) sẽ không vô tình "phá" object.

7. Thêm mẹo hay

Đừng ngại interface và abstraction

Interface dùng để tạo contract tiện lợi, test và mở rộng app.

Tệ:

  • interface chỉ có 1 method mà chẳng ai dùng;
  • interface chỉ có 1 class implement.
Tốt:
  • interface được 2+ class dùng;
  • interface để trừu tượng hóa hệ thống ngoài (ví dụ logging, lưu trữ dữ liệu).

Viết code dễ test

Một dấu hiệu của code OOP tốt là dễ test.

  • Đừng viết method phụ thuộc vào biến global hoặc field static.
  • Đừng ngại inject dependency — qua parameter constructor (Dependency Injection).
  • Tách logic tính toán và giao tiếp với user (input/output). Như vậy vừa dễ test, vừa dễ phát triển tiếp.

Tips và anti-pattern cho người mới

  • Đừng viết "class Thượng Đế" (God Object) làm mọi thứ.
  • Đừng dùng "con số ma" không giải thích (if (trangThai == 42) — tại sao lại 42?).
  • Đừng lạm dụng inheritance chỉ để inheritance — đôi khi composition (class chứa field của class khác) tốt hơn.
  • Đừng viết method dài 100 dòng — không ai test hay hiểu nổi đâu.
  • Luôn để chỗ cho mở rộng (open/closed principle).

8. Code xấu và code đẹp trông thế nào

Ví dụ xấu:


class s // Tệ: tên class chữ thường.
{
    public int a; // vô nghĩa, tên tệ
    public void m() // Tệ: method tên 1 chữ.
    {
        Console.WriteLine(a);
        // Tệ: không rõ method làm gì.
    }
}

Ví dụ đẹp:


// Đại diện cho sinh viên.
public class SinhVien
{
    // Tuổi sinh viên.    
    private int _tuoi;
    public int Tuoi
    {
        get => _tuoi;
        set => _tuoi = value < 0 ? 0 : value;
    }

    // In thông tin sinh viên.
    public void InThongTin()
    {
        Console.WriteLine($"Tuổi sinh viên: {Tuoi}");
    }
}
2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Đặt tên class và method đúng chuẩn
Đặt tên class và method đúng chuẩn
2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Tổ chức cấu trúc lớp
Tổ chức cấu trúc lớp
1
Khảo sát/đố vui
, cấp độ , bài học
Không có sẵn
Lỗi khi kế thừa
Những lỗi thường gặp khi khai báo class và object
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION