Bài viết được sự cho phép của tác giả Giang Phan
Ở bài trước tôi đã giới thiệu với các bạn các bạn Các nguyên tắc thiết kế hướng đối tượng – SOLID. Trong bài này, tôi sẽ giới thiệu với một số nguyên tắc, định luật trong lập trình mà các bạn nên biết để phát triển chương trình của bạn tốt hơn.
The Law of Demeter Principle – LoD
Giới thiệu
The Law of Demeter Principle – LoD, còn gọi khác là nguyên tắc Demeter hay nguyên tắc “càng biết ít càng tốt” hay nguyên tắc “Một dấu chấm”. Nó là một nguyên tắc thiết kế để phát triển phần mềm, đặc biệt là các chương trình hướng đối tượng.
LoD là một triết lý nền tảng của việc lập trình được sinh ra từ một aspect-oriented programming (AOP) project cùng tên, là một trường hợp cụ thể của khớp nối lỏng lẻo (loose coupling).
LoD còn được gọi là nguyên tắc “Một dấu chấm”, nghĩa là không nên gọi quá nhiều dấu chấm (lời gọi hàm), là 1 code smell và sẽ dẫn đến việc code rất dễ vỡ khi có thay đổi.
Quan điểm cơ bản của nguyên tắc này chính là : tối giản sự hiểu biết của 1 object về cấu trúc, thuộc tính của các object khác ngoài nó (bao gồm các thành phần con).
Vì sao phải tuân theo LoD?
Giả sử bạn có một đoạn code sau:
store.getOrder().getCustomer().getBillingAddress().getCity();
Như bạn thấy đoạn code trên không có vấn đề gì. Tuy nhiên, nó vi phạm LoD và có thể gây chút phiền phức cho chúng ta sau này:
- store về sau có thể sẽ không truy cập BillingAddress qua Customer, Order nữa.
- Phương thức getCity() trong BillingAddress có thể sẽ không còn tồn tại.
- Có thể sẽ gặp lỗi: NullPointerException, MethodNotFoundException nếu bất kỳ object order, customer, hay BillingAddress bị null.
- Khi đóng gói Store để tái sử dụng, cũng cần phải đóng gói cả Order, Customer, BillingAddress. Khi đó, sự phụ thuộc lẫn nhau giữa các thành phần trong hệ thống tăng cao (tightly coupled).
Ưu điểm và nhược điểm của LoD
Cái gì thì có cũng mặt tốt và mặt xấu của nó cả, việc tuân theo LoD cũng vậy. Tùy theo trường hợp thích hợp hãy áp dụng để có được kết quả tốt nhất.
Lợi ích:
- Class sẽ loosely coupled hơn, những thành phần trong hệ thống sẽ ít phụ thuộc vào nhau hơn.
- Dễ dàng đóng gói và tái sử dụng.
- Việc test, bảo trì sẽ dễ dàng hơn.
Bất lợi:
- Viết nhiều phương thức, class wrapper hơn để có thể gọi được phương thức cần sử dụng.
Thật khó để tuân theo các quy tắc thiết kế này, nhưng một khi hiểu chúng đúng cách, nó sẽ mang đến cho chúng ta lợi ích rất nhiều.
Định luật Brook
Đây là một định luật dựa trên kinh nghiệm thực tế : “Đưa thêm người vào 1 project đang chậm, sẽ chỉ khiến nó càng chậm hơn.”
Hay có thể nói theo một cách khác nữa là “Tập hợp 9 bà bầu lại cũng không thể khiến đứa trẻ ra đời sau 1 tháng.”
Luận thuyết cơ bản của định luật này là
- Cần thời gian để quen với project
- Công sức dành cho việc communication sẽ tăng
http://en.wikipedia.org/wiki/Brooks’s_law
Định luật Conway
“Organizations which design systems … are constrained to produce designs which are copies of the communication structures of these organizations.”
“Một công ty thiết kế hệ thống thế nào cũng sẽ làm ra những thiết kế giống y hệt với thiết kế hệ thống của chính công ty họ.”
http://en.wikipedia.org/wiki/Conway’s_law
Nghiên cứu gần đây chỉ ra rằng hệ thống của công ty là nhân tố ảnh hưởng lớn nhất đến vấn đề phát sinh ra bug của sản phẩm.
http://research.microsoft.com/apps/pubs/default.aspx?id=70535
Nguyên tắc bất ngờ nhỏ nhất (least astonishment)
Trong trường hợp trên cùng 1 interface có 2 yếu tố hành xử mâu thuẫn với nhau, hoặc cách hành xử không rõ ràng thì cần phải chọn cách hành xử nào gây bất ngờ ít nhất cho người sử dụng.
http://en.wikipedia.org/wiki/Principle_of_least_astonishment
Đây là 1 nguyên tắc về giao diện người dùng. Một ví dụ đơn giản :
- Trên 1 interface có 2 chức năng :
- Ấn ctrl+Q để thoát chương trình.
- Nhập macro (lưu 1 tổ hợp phím mang 1 chức năng nào đó để tiện cho việc sử dụng về sau).
- Sẽ có trường hợp user muốn dùng Ctrl+Q cho macro của mình, nên hành xử đúng với nguyên tắc bất ngờ nhỏ nhất chính là : trong khi nhập macro thì ctrl+Q được coi như là tổ hợp phím bình thường, không phải là lệnh tắt chương trình. Đây chính là điều gây bất ngờ ít nhất cho người dùng.
Nguyên tắc Boy Scout
Nguyên tắc của các tổ chức Boy scout chính là : lúc đi phải sạch đẹp hơn lúc đến.
Trong lĩnh vực lập trình thì nguyên tắc đó sẽ được hiểu là “Khi bạn check-in một module thì lúc đó nó phải đẹp hơn lúc bạn check-out.”
Nguyên tắc YAGNI
Viết tắt của “You aren’t gonna need it” – Cái (chức năng, phần) ấy rồi sẽ không cần thiết.
Đó là một câu khẩu ngữ nhắc nhở người lập trình rằng trong quy trình Extreme Programming (lập trình cực hạn) thì : “Chưa phải lúc cần thiết thì chưa được phép làm.”
http://en.wikipedia.org/wiki/YAGNI
Nguyên tắc DRY
Viết tắt của “Don’t repeat yourself” – với ý nghĩa là “Đừng lặp lại những gì giống nhau”.
http://en.wikipedia.org/wiki/Don’t_repeat_yourself
Khi nguyên tắc này được áp dụng tốt, dù ta có thay đổi 1 phần thì những phần không liên quan cũng sẽ không bị thay đổi theo. Hơn nữa, những phần có liên quan sẽ được thay đổi cùng 1 lượt, giúp ích rất nhiều cho cả khâu estimate và khâu thực hiện.
Nguyên tắc KISS
Viết tắt của “Keep it simple, stupid” – “Cứ đơn giản thôi, đồ ngu!”. Đây là 1 triết lí của Hải quân Mỹ.
http://en.wikipedia.org/wiki/KISS_principle
Những triết lý tương tự có thể kể đến là :
- Phương châm dao cạo Okham (Okham’s razor) : Không đưa ra nhiều giả thiết nếu không cần thiết. Cái gì cần ít giả thiết để chứng minh sẽ không thể chứng minh được bằng nhiều giả thiết.
- Albert Einstein : Làm cái gì cũng nên đơn giản nhất có thể, nhưng đơn giản quá thì không được.
- Leonardo da Vinci : Đơn giản nhất chính là điêu luyện nhất.
- Antoine de Saint- Exupéry : Hoàn hảo, không phải là không thêm vào được nữa, mà là không thể bớt đi được nữa.
Nguyên tắc SOLID
Tập hợp những nguyên tắc trong lập trình hướng đối tượng. Các chữ cái đầu hợp lại thành SOLID.
http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)
- SRP (Single Responsibility Principle) : Một class chỉ nên giữ 1 trách nhiệm duy nhất, chỉ có thể sửa đổi class với 1 lý do duy nhất.
- OCP (Open/closed principle) : Có thể thoải mái mở rộng 1 class, nhưng không được sửa đổi bên trong class đó.
- LSP (Liskov substitution principle) : Trong một chương trình, các object của class con có thể thay thế class cha mà không làm thay đổi tính đúng đắn của chương trình.
- ISP (Interface segregation principle) : Thay vì dùng 1 interface lớn, ta nên tách thành nhiều interface nhỏ, với nhiều mục đích cụ thể.
- DIP (Dependency inversion principle) : Các module cấp cao không nên phụ thuộc vào các modules cấp thấp. Cả 2 nên phụ thuộc vào abstraction. Interface (abstraction) không nên phụ thuộc vào chi tiết, mà ngược lại. ( Các class giao tiếp với nhau thông qua interface, không phải thông qua implementation.)
Nguyên tắc này đã được tôi viết khá kỹ trong bài viết “Các nguyên lý thiết kế hướng đối tượng – SOLID“. Các bạn có thể tham khảo thêm ở đây.