Viết CODE đỉnh hơn – Những kinh nghiệm viết code dễ đọc và dễ bảo trì

0
134

Viet code dinh hon- nhung dieu de ma kho

Đối với lĩnh vực lập trình, có những điều tưởng chừng như đơn giản nhưng chưa chắc đã dễ dàng để 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.

Trong quá trình viết code, 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 đã thao tác chính xác hay chưa. Hãy ngâm cứu các bước được “bật mí” dưới đây để trở thành một lập trình viên không những đẹp mà còn tỉnh 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ọc cho các bài học. 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ô ngữ khác nữa. Tựu chung lại, thì 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

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 lớp (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 đáng để chúng ta quan tâm trong trường hợp này
  • Gắn liền với Retrofit library (VehiclesRetrofitApi) hoặc bất cứ library nào khác

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 pass VehiclesRetrofitApi như một tham số khởi tạo (constructor parameter).

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 để pass 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é!

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. Điều bạn cần làm chính là tránh réo tên chúng trực tiếp từ code và sau đó tất cả các singletons đều sẽ trở thành module phụ thuộc (dependency) khác trong lớp (class) của bạn.

Cách chính xác

Hãy pass 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 các lớp (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á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à  pass 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 đượ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.

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ình luận. Đặt câu hỏi cũng là một cách học

avatar
  Theo dõi bình luận  
Thông báo