Có lẽ bạn đã xem vô số hướng dẫn viết code sử dụng câu lệnh If-Else. Có thể bạn cũng đã đọc sách lập trình quảng cáo việc sử dụng If-Else làm kỹ thuật phân nhánh trên thực tế. Hoặc có lẽ chế độ mặc định của bạn thậm chí là sử dụng If-Else. Tuy nhiên, hãy chấm dứt điều đó ngay bây giờ, bằng cách thay thế If-Else bằng state objects.

Lưu ý rằng bạn sẽ sử dụng phương pháp này nếu bạn đang viết một class có các phương thức cần thay đổi giai đoạn triển khai của nó tùy thuộc vào trạng thái lúc đó. Nếu bạn không đối phó với trạng thái thay đổi của đối tượng (object’s changing state) thì bạn cần một cách tiếp cận khác.

Ngay cả khi bạn đã nghe nói về state pattern, bạn có thể tự hỏi làm thế nào nó được triển khai trong production-ready code.

Đối với bất kỳ ai vẫn còn ngẩn ngơ chưa biết gì, đây là phần giới thiệu rất ngắn gọn.

Bạn sẽ tăng độ phức tạp với bất kỳ yêu cầu có điều kiện mới nào được triển khai bằng If-Else. Khi áp dụng state pattern, bạn chỉ cần thay đổi object behavior bằng cách sử dụng các state object chuyên biệt thay vì các câu lệnh If-Else.

Đã qua rồi cái thời mã trông như thế này:

Poor if-else statements

Bạn chắc chắn đã viết phân nhánh phức tạp hơn trước đây. Logic phân nhánh ở trên thậm chí không quá phức tạp - nhưng hãy thử thêm các điều kiện mới và bạn sẽ thấy mọi thứ bùng nổ.

Ngoài ra, nếu bạn nghĩ rằng việc tạo các class mới thay vì chỉ sử dụng các câu lệnh rẽ nhánh nghe có vẻ khó chịu, hãy đợi cho đến khi bạn thấy nó hoạt động. Rất ngắn gọn và trang nhã. Còn hơn thế, nó sẽ làm cho codebase của bạn VỮNG CHẮC hơn.

"Được rồi, tôi tin rằng If-Else cũng không ngon nghẻ cho lắm, bây giờ hãy chỉ cho tôi cách tránh mã phân nhánh lộn xộn"

Bây giờ, chúng ta cùng xem qua cách thay thế phân nhánh If-Else trong production-ready code. Đó chỉ là một ví dụ, nhưng cách tiếp cận sẽ tương tự như cách được sử dụng trong các codebase cho các khách hàng lớn.

Hãy tạo một class booking rất đơn giản với một số trạng thái. Nó cũng sẽ có hai phương thức công khai: Accept ()Cancel ().

Bên dưới là một sơ đồ để hiển thị các trạng thái khác nhau có thể có của một booking.

Image for post

Cấu trúc lại logic phân nhánh từ code của chúng ta là một quy trình ba bước:

1. Tạo một class state trừu tượng

2. Triển khai mỗi state như một class riêng biệt kế thừa từ state cơ sở

3. Để cho class booking` có một phương thức private hoặc internal lấy class state cơ sở làm tham số

Thời gian demo

Đầu tiên, chúng ta cần một class state cơ sở mà tất cả các state sẽ kế thừa.

Image for post

Lưu ý rằng class cơ sở này cũng có hai phương thức, Accept và Cancel - mặc dù ở đây chúng được đánh dấu là nội bộ.

Ngoài ra, base state có một phương thức EnterState(Booking booking) “đặc biệt”. Phương thức này được gọi bất cứ khi nào một state mới được gán cho booking.

Thứ hai, chúng ta đang tạo các class riêng biệt cho từng state mà chúng ta muốn đại diện.

Image for post

Chú ý cách mỗi class đại diện cho một state như được mô tả trong sơ đồ đẹp ở trên. Ngoài ra, CancellingState sẽ không cho phép booking của chúng ta chuyển sang trạng thái mới. Class này có tinh thần rất giống với Mẫu đối tượng Null (Null object pattern).

Cuối cùng là booking class.

Image for post

Bạn thấy cách booking class chỉ đơn giản là ủy quyền việc thực hiện Accept và Cancel cho state object của nó chứ?

Làm thế này cho phép chúng ta loại bỏ phần lớn logic có điều kiện và cho phép mỗi state chỉ tập trung vào những gì quan trọng đối với chính nó - state hiện tại cũng có cơ hội chuyển booking sang state mới.

Cách lập trình với các tính năng có điều kiện mới này?

Nếu tính năng mới thường được triển khai bằng cách sử dụng một số kiểm thử điều kiện, thì bây giờ bạn có thể tạo một state class mới.

Đơn giản vậy đấy. Bạn sẽ không còn phải đối phó với các câu lệnh if-else khó sử dụng nữa.

Cách để duy trì state object trong database?

Câu trả lời là không cần. State object là yếu tố không quan trọng khi lưu một object vào cơ sở dữ liệu SQL hoặc NoSQL. Biết state của object và cách nó được map vào một cột mới là điều quan trọng.

Bạn có thể map một state thành một cái tên thân thiện, enum hoặc số nguyên. Bạn cảm thấy thoải mái với điều gì, miễn là bạn có một số cách chuyển đổi giá trị đã lưu trở lại thành state object.

Nhưng bạn vẫn đang sử dụng các câu lệnh IF?

Đúng là chúng rất cần thiết. Đặc biệt là khi được sử dụng như mệnh đề bảo vệ. Chính sự kết hợp If-Else là nguyên nhân gốc rễ gây ra chứng đau đầu về khả năng duy trì.

Vấn đề là cần thêm rất nhiều class bổ sung!

Đúng vậy. Sự phức tạp không bắt nguồn từ số lượng class bạn có, mà từ trách nhiệm mà các class đó đảm nhận. Có nhiều class chuyên biệt sẽ làm cho codebase của bạn dễ đọc hơn, dễ bảo trì hơn và đơn giản là giúp bạn thoải mái hơn nhiều khi làm việc trên nó.

______________________________________________

Bên cạnh những cái "vỗ tay" đồng tình, bài viết của tác giả cũng nhận được khá nhiều ý kiến trái chiều cho rằng việc thay thế mệnh đề If-Else quen thuộc bằng hàng loại dòng code khác là phức tạp hóa vấn đề và hoàn toàn không cần thiết. Một số bình luận nổi bật của độc giả như: 

"Tiêu đề hấp dẫn đấy, nhưng tuyên bố bao quát rằng "if-else" là không tốt bằng cách cung cấp một trường hợp sử dụng (không phổ biến lắm) mà trường hợp đó áp dụng cũng sai. Tôi đào tạo các nhà phát triển hàng ngày và họ thường đưa ra những ý tưởng rằng nên tránh sử dụng if-else ở bất kỳ đâu vì ai đó trên internet đã nói như vậy.

Bạn lập luận rằng nếu việc phân nhánh khác dẫn đến sự phức tạp của code, rồi bạn cung cấp một ví dụ về cách giới thiệu nhiều lớp và lập luận rằng đôi khi sự phức tạp là OK?

Và tóm lại, tôi sẽ chỉ sử dụng một state machine library như Stateless để tạo mô hình các trường hợp sử dụng của tôi thay vì tạo POCO tùy chỉnh." 

"Thiệt luôn?! Bạn đã thay thế 6 dòng code bằng >100 dòng? Tôi không thể nghĩ ra bất kỳ tình huống nào mà tôi thích kiểu này.

Thay vì vậy, bạn có thể hoàn thành với if-else-logic, viết các bài test và tài liệu mà vẫn tiết kiệm thời gian. Chưa kể đến người đồng nghiệp tội nghiệp phải đọc qua 2 trang code thế này để phát hiện ra rằng nó hoạt động như một if-else đơn giản… Cần hẳn một sơ đồ để giải thích giải pháp của bạn hẳn đã là một dấu hiệu báo động.

Xin lỗi, nhưng chắc chắn tôi nói không với cách này!"

Còn bạn, bạn nghĩ sao về cách viết code này?

VietnamWorks inTECH
Xem bài viết gốc trên Medium