[ Sách ] The pragmatic programmer – Lập trình viên tiêu biểu. (P2)

     Tiếp tục phần 1, mình sẽ tiếp tục đi qua những ý chính của cuốn sách này. Nội dung chính của phần còn lại bao gồm: những cách tiếp cận vấn đề khôn ngoan, những lầm tưởng cần tránh trong lập trình, nguyên tắc để tăng tính linh hoạt của phần mềm, cách thức và thái độ để xây dựng những sản phẩm tiêu biểu.

Những cách tiếp cận khôn ngoan

a. Tránh lặp code bằng cách xây dựng các component chung

      Mỗi phần mềm được viết ra, sẽ có những tính năng tương tự nhau ở những màn hình khác nhau, khi đó để đảm bảo tính nhất quán của phần mềm, ta nên xây dựng những component chung để có thể tái sử dụng ở nhiều nơi. Một lợi điểm khác nữa của việc làm này chính là đảm bảo tính nhất quán của ứng dụng. Chỉ cần test ở một màn hình, là ta có thể biết được chính xác cả phần mềm của ta hoạt động đúng hay không, mỗi lần cần chỉnh sửa gì đó cũng sẽ không sợ bị phân mảnh hoặc thiếu sót.

software layer.png
Tách biệt phần mềm thành nhiều module và component riêng biệt

     Bên cạnh đó, để phần mềm được đơn giản, ta nên thiết kế kiến trúc ứng dụng sao cho những module khác nhau sẽ trực giao với nhau, tức là chúng sẽ có những tính năng độc lập với nhau. Ví dụ: phân tầng ứng dụng, một tầng chuyên xử lí truy vấn DB và trả kết quả truy vấn, một lớp chuyên nhận đầu vào là kết quả truy vấn để xử lí các ràng buộc dữ liệu (tương tự mẫu Facade design pattern), một lớp chuyên xử lí nghiệp vụ, … Như vậy phần mềm sẽ rất rõ ràng, tách bạch, từ đó dễ bảo trì và phát triển.

b. Viên đạn chỉ đường (Tracer bullets)

      Nếu bạn đang không giải quyết một vấn đề như thế nào, đây có lẽ là cách giúp ích cho bạn.

trace bullet.jpg

      Hình dung rằng trong bóng tối, bạn cần phải bắn nhiều viên đạn trúng đích, bạn sẽ làm thế nào? Cho dù có nhiều đạn, thì cũng đừng vội nhắm mắt bắn đại. Thông thường, người ta sẽ ước lượng trước toạ độ mục tiêu, và giương súng về phía đó. Để biết được mình có bắn chính xác hay không, trước tiên ta bắn 1 viên đạn sáng (đạn chỉ đường), dựa vào viên đạn này ta điều chỉnh lại hướng súng, từ đó bắn các phát đạn sau chính xác hơn. Như vậy, ta chỉ tốn 1 vài viên đạn sáng ban đầu (hi sinh 1 chút tài nguyên nhỏ) để chắc chắn rằng nhiều viên đạn sau được chính xác (tài nguyên lớn được dùng đúng).

       Phần mềm cũng giống như vậy, nếu bạn chưa biết liệu 1 giải pháp nào đó có phù hợp hay không, hãy thử triển khai giải pháp đó cho 1 tính năng nhỏ trong dự án, sau đó đánh giá thử bản nháp này. Nếu phù hợp, ta có thể triển khai toàn bộ dự án tiếp theo đó, còn nếu không, hãy điều chỉnh điểm chưa hợp lí. Đừng dại đột lao vào làm toàn bộ dự án khi chưa biết rõ giải pháp đó có phù hợp hay không.

       Lưu ý: Phương pháp “đạn chỉ đường” khác với cách làm prototype. Làm prototype là ta làm ra tính năng xài được ở bề mặt, nhưng bên dưới có thể cheat (hardcode, không tách lớp, …). Phương pháp “đạn chỉ đường” là thực sự triển khai hệ thống đúng với thiết kế, chỉ có điều là được áp dụng cho 1 tính năng nhỏ mà thôi.

c. Nhận định chính xác “phạm vi vấn đề”

      Rất nhiều người không đánh giá chính xác phạm vi vấn đề, từ đó dẫn tới việc phải giải quyết rất nhiều bài toán rắc rối liên quan. Điển hình nhất là việc phải xác định rõ người dùng phần mềm thuộc loại đối tượng nào. Nếu là người dùng phổ thông, họ sẽ cần phần mềm có mức tự động hoá cao bởi vì họ không có nhiều kĩ năng chuyên môn. Nếu là người dùng mức cao (vd: nhân viên công ty đã được đào tạo), phần mềm có thể lược bớt một số quy trình khó tự động hoá (nhưng tăng độ phức tạp khi sử dụng). Một phần mềm auto từ đầu đến đuôi chưa hẳn đã được lòng người sử dụng, quan trọng là bạn phải biết người dùng cần gì.

what customer need.jpeg
Đừng tưởng nhiều tính năng là tốt, quan trọng là phải đúng ý người dùng.

Nhưng đừng vội hoang tưởng

Bạn không thể viết được 1 phần mềm hoàn hảo, bời vì điều đó … không tồn tại

      Câu nói trên có khiến bạn khó chịu? Chấp nhận đi, sớm muộn rồi bạn cũng nhận ra chân lí này mà thôi.

      Vậy thì chúng ta có thể làm gì? Rất đơn giản: áp dụng kĩ thuật “design by contract” – làm ra đúng thứ mà khách hàng cần. Đừng cố gắng làm ra một thứ khi mà bạn không chắc khách hàng sẽ dùng tới và ảo tưởng về tính năng đó. Điều này không chỉ đúng với sản phẩm và đúng với cả source code của bạn: hãy thiết kế một lớp (hoặc 1 hàm, 1 interface, …) với đúng 1 trách nhiệm duy nhất và rõ ràng, có input và output đúng như yêu cầu. Ví dụ như bạn cần xây dựng một hàm nhận đầu vào là mảng chứa điểm các môn học, giá trị trả về là điểm trung bình, vậy thì hãy viết code sao cho hàm trả về đúng là điểm trung bình mong muốn, đừng cố nhồi thêm thông tin như xếp loại học sinh, điểm hệ chữ, … khi đó code của bạn trở nên cồng kềnh và dễ phát sinh bug hơn. Xem thêm kĩ thuật SOLID trong lập trình để có thể viết code vừa ít vừa dễ mở rộng.

Design_by_contract.svg.png
Hiểu rõ input và output để biết phải làm gì

        Ngoài ra, source code của bạn không được nói dối. Nếu phần mềm của bạn gặp bất cứ sự cố hoặc lỗi, hãy dừng lại hoặc thông báo sớm nhất có thể. Dữ liệu đầu ra tất nhiên là phải chính xác, nhưng bạn đừng quên kiểm tra dữ liệu đầu vào. Nhiều người quên mất việc kiểm tra input dẫn tới việc mất kiểm soát output, lúc đó bạn rất khó để biết rằng liệu phần mềm của bạn còn xử lí đúng hay không. Hãy luôn kiểm tra, và kiểm tra sớm. Cách này được gọi là “defensive programming“.

      Code thì phải có lỗi, và nếu phải dừng trương trình lại vì lỗi, bạn sẽ để đoạn code bắt lỗi ở đâu? Thông thường thì: code bắt exception phải do lớp controller phía trên thực hiện. Các hàm thao tác cơ bản phía dưới chỉ nên thực hiện đúng chức năng của nó, hãy để nhiệm vụ kiểm soát tính đúng đắn của quy trình hoặc nghiệp vụ ở các lớp xử lí cao hơn.

Mềm dẻo và cứng nhắc

      Người ta có nói rằng: Sản phẩm làm ra là để thay đổi, phần mềm viết ra là để nâng cấp. Chẳng có gì tồn tại mãi mãi, vì thế hãy cố gắng viết code càng linh hoạt càng tốt. Công nghệ biến đổi rất nhanh chóng, nghiệp vụ chặt chẽ rồi cũng sẽ có lúc thay đổi, vậy nên hãy viết code thành các module tách biệt và tránh phụ thuộc, sao cho có thể dễ thay đổi hoặc tận dụng lại dễ dàng. Nếu các module có liên kết với nhau thì hãy đảm bảo sợi dây liên kết ít bị thay đổi nhất có thể.

     Một ví dụ về việc giảm sự phụ thuộc vào nhau ở các module. Giả sử bạn nhận được dữ liệu về thời gian, bạn muốn hiển thị các thông tin thời gian này theo timezone của User, đoạn code xử lí như sau:

void printDate( Date _date, User _user )
{
Timezone tz = _user.getLocation().getTimezone();
//do something else
...
}

     Đoạn code trên nhìn có vẻ ổn, nhưng thực ra các module lại đang bị gắn chặt vào nhau quá nhiều. Code xử lí printDate() bị gắn chặt vào lớp User, thậm chí là bị gắn luôn vào lớp Location. Ta xử lí để giảm bớt sự liên kết như sau:

void printDate( Date _date, Timezone _tz )
{
//do something else
...
}

printDate( _date, _user.getLocation().getTimezone() );

    Chỉ bằng một vài sửa đổi, chúng ta đã tách biệt sự liên kết giữa module hiển thị và module User, sự liên kết bây giờ được thể hiện qua lớp Timezone – một lớp rất ít khi phải thay đổi – từ đó tránh được các rắc rối có thể phát sinh bởi sự phụ thuộc vào nhau của các module.

     Hãy nhớ nguyên tắc Demeter trong lập trình hướng đối tượng (Law of Demeter):

  • Bạn có thể gọi các method khác của cùng một class với method hiện tại.
  • Nếu trong class có các thuộc tính (property) thì bạn có thể gọi các method của các thuộc tính này.
  • Nếu bên trong method có các biến local được tạo và các biến này là object thì bạn cũng có thể gọi method của các biến object này.
  • Nếu method chấp nhận đối số truyền vào là object thì bạn có thể gọi method của đối số truyền vào này.

    Ngoài ra, nếu muốn biết thêm nhiều kĩ thuật các, bộ quy tắc SOLID có thể là câu trả lời. Hãy làm mọi thứ tách bạch, hãy làm mọi thứ có khả năng tái sử dụng. Kĩ thuật black board có thể là 1 giải pháp nhằm quy về một mối tất cả những thứ chung: hãy làm cho phần mềm có khả năng configure, tránh hard-code.

Xây dựng những sản phẩm tiêu biểu

    Những sản phẩm tốt không chỉ chạy được và chạy đúng, nó còn đòi hỏi những thứ như:

  • Đáp ứng được về mặt thời gian: Hãy cân nhắc khả năng chạy tuần tự và song song.
  • Code có khả năng test được tự động: Một khi sản phẩm của bạn quá lớn, bạn phải có khả năng tự động hoá việc test sản phẩm để luôn đảm bảo phần mềm chạy đúng.
  • Đáp ứng được yêu cầu người dùng: Một phần mềm tốt hay không được đo lường bởi sự hài lòng người dùng. Hãy đáp ứng đủ thứ họ cần trước, rồi mở rộng ra sau. Đừng vội mà ôm đồm.

Để làm được những phần mềm tốt, mỗi chúng ta nên:

  • Tránh code “cầu may”: bạn phải thực sự hiểu bản chất những thứ bạn đang viết, nếu chỉ dừng lại ở mức chạy được mà không hiểu tại sao, rất khó để bạn nâng cao chất lượng sản phẩm.
  • Hãy refactor code: Đầu tiên hãy làm code chạy được, sau đó hãy cải tiến nó, đừng để code xấu mãi. (First, make it work, then, make it right)
  • Làm thử nếu như chưa chắc chắn: Nếu bạn không chắc được một giải pháp có tốt hay không, hãy cứ thử bắt tay vào làm prototype, hoặc xây dựng 1 “tracer bullet”. Như thế tốt hơn là ngồi im lo lắng.

Đằng sau những sản phẩm tốt, đều là những con người tuyệt vời:

  • Hãy xây dựng “nhóm tiêu biểu”: Một cây làm chẳng nên non, ba cây chụm lại nên hòn núi cao. Hãy luôn cộng tác, để hỗ trợ và học hỏi lẫn nhau. Những quy tắc cho code vẫn áp dụng được phần nào đó cho con người: No broken windows – hãy luôn cải tiến, Con ếch luộc (boiled frog) – giải quyết ngay hoặc có kế hoạch giải quyết vấn đề gặp phải, giao tiếp thường xuyên, don’t repeat yourself – phân công rõ ràng, tránh trùng lặp nhiệm vụ. Mỗi người nên có trách nhiệm theo kĩ năng và chức năng sản phẩm, tránh có tư tưởng làm việc theo “chức vị”
  • Tôn trọng người khác: Đối xử với đồng nghiệp như chính những gì bạn mong được nhận lại.
  • Tự hào về những thứ bạn làm được: Người tiêu biểu không trốn tránh trách nhiệm, họ sẵn sàng chấp nhận thử thách, và nỗ lực đạt tới sự hoàn hảo. Hãy để tên tuổi của bạn là minh chứng cho những sản phẩm chất lượng – hoạt động hoàn hảo, kiểm thử kĩ càng và mô tả văn bản đầy đủ.

Mình rất thích đoạn kết trong sách, trích 1 đoạn ngắn:

A really professional job. Written by a real professional.
A Pragmatic Programmer.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s