Quy tắc viết code dễ đọc, tối ưu và dễ bảo trì nhất

5866

Bài viết được sự cho phép của tác giả Sơn Dương

Đối với ngành lập trình, có những quy tắc tưởng chừng như đơn giản nhưng chưa chắc dễ thực hiện. Chúng tương tự như việc Chi Pu nghĩ rằng chỉ cần cầm mic là trở thành ca sĩ. Nhưng từ hát được tới hát hay lại là cả một quãng đường dài.

Lập trình cũng vậy, cho dù bạn có sở hữu kinh nghiệm viết code lâu năm, nhưng nếu không để ý và chỉn chu cũng sẽ dễ để lại những dòng code khó chịu cho người khác.

Trong quá trình làm dự án phần mềm, sẽ có những vấn đề bạn thấy bình thường như cân đường hộp sữa, nhưng không ai đảm bảo rằng bạn đã làm chính xác hay chưa.

Hãy cùng nhau ngâm cứu các quy tắc, kinh nghiệm được “bật mí” dưới đây để trở thành một lập trình viên không những đẹp trai mà còn “tỉnh đòn” nữa nhé!

Những kinh nghiệm viết code để code dễ đọc và dễ bảo trì

Trong bài viết này mình sẽ sử dụng ngôn ngữ Kotlin để làm các ví dụ minh họa. Tất nhiên, những kinh nghiệm viết code này cũng có thể ứng dụng cho các ngôn ngữ khác nữa.

Tựu chung lại, mình chỉ muốn đề cập đến 4 vấn đề: Hạn chế inner class, sử dụng các pattern hợp lý, cách đặt tên class và subclass chuẩn.

1. Hạn chế tạo các Object mới trong cùng một Class

Trừ khi đó là các đối tượng dữ liệu (data object) bạn đã tạo, hoặc class là một factory pattern. Để dễ hình dung hơn, hãy theo dõi đoạn code sau:

class VehiclesManager(val context: Context) {

  fun getVehiclesNextToMe(): List<Vehicle> {
    val api = VehiclesRetrofitApi(context)
    val currentLocation = LocationProvider.currentLocation()

    return api.getVehicles()
        .filter { it.coordinates.distanceTo(currentLocation) < 100 }
  }

}

Đối với class này, chúng ta có thể rút ra được 3 gạch đầu dòng:

  • Hoàn toàn không thể Unit Test được.
  • Context ư? Không cần thiết trong trường hợp này.
  • Class này gắn liền với Retrofit library (VehiclesRetrofitApi).

Cách chính xác

Việc này đơn giản hơn bạn tưởng tượng! Trước hết, hãy truyền VehiclesRetrofitApi như một tham số khởi tạo (constructor parameter) thay vì phải khởi tạo nó trong class.

class VehiclesManager(val vehiclesApi: VehiclesRetrofitApi) {

  fun getVehiclesNextToMe(): List<Vehicle> {
    val currentLocation = LocationProvider.currentLocation()

    return vehiclesApi.getVehicles()
        .filter { it.coordinates.distanceTo(currentLocation) < 100 }
  }

}

Tiếp theo, làm thế nào để truyền vehiclesApi một cách dễ dàng?

Bạn có thể sử dụng Dependency injection. Trong trường hợp bạn không biết Dependency injection ra sao, đừng quên tham khảo kiến thức từ bác Google nhé!

  Thói quen viết code an toàn trong khi xây dựng ứng dụng PHP

  Là lập trình viên thì có nên chỉ viết code thôi không ?

2. Không nên sử dụng singletons pattern bừa bãi

Việc giữ lại singletons không ảnh hưởng gì tới code của bạn. Kinh nghiệm viết code ở đây chính là tránh réo tên chúng trực tiếp từ code. Sau đó tất cả các singletons đều sẽ trở thành module phụ thuộc (dependency) khác trong class của bạn.

Cách chính xác

Hãy truyền nó như một constructor parameter.

class VehiclesManager(val vehiclesApi: VehiclesRetrofitApi, val locationProvider: LocationProvider) {

  fun getVehiclesNextToMe(): List<Vehicle> {
    val currentLocation = locationProvider.currentLocation()

    return vehiclesApi.getVehicles()
        .filter { it.coordinates.distanceTo(currentLocation) < 100 }
  }

}

3. Đừng đặt tên các class là Managers

Cũng đừng gọi chúng là Handler, Controller hay Processor.

Bởi những cái tên đó chính là lý do để các lập trình viên và kể cả bạn tiếp tục mở rộng Class bằng những thao tác không – liên – quan.

Thử thực hiện một bước đơn giản thế này nhé: Hãy xoá thử một từ trong lớp và xét xem liệu chúng có trở nên không rõ ràng hay không?

Với kinh nghiệm thực tế, mình thấy rằng Vehicles cũng gợi nhiều thông tin tương đương như VehiclesManager không mang thông tin. Cũng là một kinh nghiệm viết code đúng không nào?

Cách chính xác

Đây là thao tác có vẻ như sẽ cung cấp vehicles quen thuộc với chúng ta hơn. Vì vậy hãy gọi nó là ClosestVehiclesProvider. Đây chưa chắc đã là cái tên phù hợp hoàn toàn, song vẫn là ý tưởng không tồi mà bạn có thể lựa chọn. Ít nhất để không ai có thể dẫn ra lý do bổ sung thêm yếu tố nào khác như getVehiclesByType.

class ClosestVehiclesProvider(val vehiclesApi: VehiclesRetrofitApi, val locationProvider: LocationProvider) {

  fun getVehiclesNextToMe(): List<Vehicle> {
    val currentLocation = locationProvider.currentLocation()

    return vehiclesApi.getVehicles()
        .filter { it.coordinates.distanceTo(currentLocation) < 100 }
  }

}

4. Không nên tạo thêm subclasses

Trừ khi chúng là các lớp dữ liệu đại số (algebraic data classes) hay Interfaces. Hãy theo dõi đoạn code minh họa sau:

abstract class BaseVehiclesProvider(val repository: Repository) {

  protected fun getVehicles(): List<Vehicles> {
    return repository.loadStuffFromServerAndCache()
  }

}

Hình như có gì đó sai sai?

class ClosestVehiclesProvider(val repository: Repository, 
                              val locationProvider: LocationProvider) : BaseVehiclesProvider(repository) {

  fun getVehiclesNextToMe(): List<Vehicle> {
    val currentLocation = locationProvider.currentLocation()

    return getVehicles().filter {
      it.coordinates.distanceTo(currentLocation) < 100
    }
  }

}

Điều không ổn nằm ở đâu?

  • Chúng ta không cần quan tâm nhiều đến Repository trong ClosestVehiclesProvider.
  • Đối tượng nào cần phải kiểm tra? Chúng ta không thể tạo một instance của BaseVehiclesProvider.
  • Trong trường hợp cụ thể này, ClosestVehiclesProvider không được sử dụng như một phần thay thế (substitute) cho BaseVehiclesProvider.
  • Điều gì sẽ xảy ra nếu chúng ta không cần phải caching từ base class? Và điều gì sẽ xảy ra nếu chúng ta không cần server trong base class?

Cách chính xác

Cách giải quyết đơn giản chính là truyền nó như một constructor parameter.

class ClosestVehiclesProvider(val vehiclesProvider: VehiclesProvider, 
                              val locationProvider: LocationProvider) {


  fun getVehiclesNextToMe(): List<Vehicle> {
    val currentLocation = locationProvider.currentLocation()

    return vehiclesProvider.getVehicles().filter {
      it.coordinates.distanceTo(currentLocation) < 100
    }
  }

}

Như vậy, mình vừa giải thích một cách ngắn gọn những điểm lưu ý giúp bạn viết code tốt hơn. Đây cũng là những kinh nghiệm viết code được rút ra từ những lập trình viên lão làng và truyền lại cho hậu bối.

Tạm kết

Bác Hồ nói rằng: “Không có việc gì khó, chỉ sợ lòng không bền”. Còn mình chỉ khuyên các bạn hãy thực hành thật nhiều và mọi thứ tưởng chừng như đơn giản sẽ… đúng là đơn giản thật khi bạn đã thấu hiểu mọi thứ. Luyện những “ngón nghề” này trong vài tuần tới, chắc chắn khả năng lập trình của bạn sẽ được cải thiện đáng kể!

Bài viết gốc được đăng tải tại vntalking.com

Xem thêm:

Xem thêm Việc làm IT hấp dẫn trên TopDev