SERIES PHẢN PHÁC QUY CHÂN – CHUYỆN VỀ NHỮNG “Ổ GÀ” TRÊN CON ĐƯỜNG LẬP TRÌNH
Hôm nay, trước khi nói về ngành lập trình, ta hãy nói về ngành cầu đường. Trên đời này làm gì có đường, người ta đi mãi cũng thành đường thôi. – Lỗ Tấn Từ chuyện xây đường… Thưở xưa, để đến nơi, người ta phải đi chân trần, trèo đèo lội suối, đập đá băng ...
Hôm nay, trước khi nói về ngành lập trình, ta hãy nói về ngành cầu đường.
Trên đời này làm gì có đường, người ta đi mãi cũng thành đường thôi. – Lỗ Tấn
Từ chuyện xây đường…
Thưở xưa, để đến nơi, người ta phải đi chân trần, trèo đèo lội suối, đập đá băng rừng. Dần dần người ta chặt cây, nhổ cỏ, làm đường đất để mà đi. Đường đất vừa bẩn vừa nhiều bụi, người ta lại lát đá, lát gạch cho đường sạch đẹp, dễ đi hơn. Thế rồi khoa học tiến bộ, người ta không lát đá nữa mà ủi phẳng, trải nhựa, trải bê tông. Trên đường nhựa, người ta xây cầu vượt, xây vòng xoay, xe cộ băng băng qua lại, nhanh chóng và tiện lợi hơn nhiều so với ngày xưa.
Giờ ta hãy cùng xem lại một chút về lịch sử ngành lập trình. Ngày xưa, các cụ phải lập trình bằng cách bật tắt các công tắc switch để đổi chiều dòng điện. Thấy làm vậy cực quá, họ dùng bit (0 và 1) để thay thế cho các công tắc. Tuy nhiên, làm việc với những chuỗi số 0110 dài ngoằn nghèo rất mệt mỏi và mất thời gian, do đó họ tạo ra các ngôn ngữ lập trình bậc thấp, vd như Assembly (tức mã máy). Assembly là chữ, do đó dễ đọc dễ sửa hơn. Sau khi viết, Assembly sẽ được dịch thành bytebode.
Dù vậy, việc sử dụng Assembly để viết một chương trình hoàn chỉnh vẫn vô cùng khó khăn. Thế rồi, một số ngôn ngữ lập trình bậc cao (C, C++, Java, C#) dần đần được tạo ra, giải quyết được nhiều công việc hơn với ít code hơn. Trên nền các ngôn ngữ này, họ xây các thư viện/framework (Zoomla, Ruby on Rail, …). Sử dụng các thư viện này, việc viết một ứng dụng/website trở nên nhanh chóng và đơn giản hơn xưa nhiều.
Ta dễ thấy sự giống nhau đến kì lạ giữa việc làm đường và lập trình. Một chương trình được xây dựng dựa trên vô số nền tảng bên dưới (Framework => Code C#, Java => IL/Java bytecode => bytecode), cũng giống như một con đường được xây dựng dựa trên vô số lớp đường phía dưới. Các bác Khoa học Máy tính gọi những lớp (ngôn ngữ, thư viện, framework) này là lớp trừu tượng (Layer of abstraction).
Các lớp trừu tượng này che giấu các khái niệm phức tạp bên dưới, trừu tượng chúng bằng các khái niệm đơn giản hơn. Ví dụ như khi ta code bằng framework ASP.NET Web Form, Web Form sẽ che giấu các khái niệm HTML, CSS, JavaScript phía dưới, “trừu tượng” nó bằng các khái niệm control, button, textbox để lập trình viên dễ sử dụng. Hoặc khi ta sử dụng jQuery, nó sẽ che giấu sự khác biệt giữa các trình duyệt, ta chỉ cần viết code 1 lần, chạy được ở mọi trình duyệt.
… cho tới chuyện ổ gà
Buồn thay, con đường của lập trình viên không bằng phẳng mà lại có khá nhiều ổ gà, ổ voi. Đôi khi, các lớp trừu tượng này không che giấu được những ổ gà bên dưới nó. Trong nhiều trường hợp gặp bug hoặc performance chậm, ta cần phải bới các “lớp trừu tượng” này lên để tìm nguyên nhân và cách sửa. Dưới đây là một số ví dụ:
- Với WinForm, ai cũng có thể dễ dàng kéo, thả, tạo form, double click vào button để viết code vì việc kéo thả, đã “trừu tượng hóa” việc tạo giao diện bằng code. Tuy nhiên, nếu muốn viết 1 control hoàn toàn mới, hoặc tạo control động, ta cần phải hiểu rõ code WinForm được tự động sinh như thế nào (Khi double click vào button, ta gán một event handler vào event của một button đó, đại loại thế, …).
- Hiện tại, có rất nhiều bạn đi làm, tham gia các project có sử dụngDependency Injection với Ninject, Unity nhưng không hề biết bản chất nó là gì, cũng như lý do phải sử dụng nó.
- LINQ là thứ rất dễ dùng, dễ viết. Tuy nhiên, muốn viết một hàm tương tự Where, OrderBy của LINQ, ta phải nắm vững một số khái niệm lằng nhằng như Generic, Callback, Lambda Expression.
- EntityFramework hỗ trợ chúng ta tương tác với database rất đơn giản về dễ dàng, vì nó đã “trừu tượng hóa” SQL và database bên dưới. Tuy nhiên, các bug của EF thường khá khó fix. Để xử lý bug, ta cần phải hiểu rõ cơ chế hoạt động bên dưới của nó (Query được sinh ra thế nào, các trường được mapping ra sao). Đơn cử như trong trường hợp sau:
1 2 3 4 5 6 7 8 9 10 11 12 |
public string getUpperName(string name) { return name.ToUpper(); } // Hàm A này sẽ bị lỗi var upperNames = _db.Users.Select(x => getUpperName(x.Name)).ToList(); // Hàm B này không lỗi var upperNames = _db.Users.Select(x => x.Name.ToUpper()).ToList(); |
Hai hàm A và B trông có vẻ tương tự nhau, nhưng hàm A bị lỗi còn hàm B thì không. Nguyên do là: Entity Framework sẽ đọc Lambda Expression truyền vào, dịch ra SQL. Với hàm A, EF không thể dịch hàm getUpperName ra SQL được, trong khi hàm ToUpper thì lạidịch ra SQL được. Đấy, vì “ổ gà” trong EF, ta phải hiểu cơ chế hoạt động của nó thì mới trị được những bug kiểu này.
Làm sao “trám” ổ gà
Những kiến thức về các “ổ gà” như thế thường ít được đề cập trong sách vở mà thường phải tích lũy theo thời gian tiếp xúc và kinh nghiệm làm việc. Đây cũng là thứ để phân biệt trình độ giữa senior và junior. Vì lý do trên, chúng ta có thể dễ dàng học nhanh một ngôn ngữ/framework như WebForm, AngularJS, … nhưng lại cần nhiều thời gian để tiếp xúc, sử dụng và thành thạo một ngôn ngôn ngữ đó.
Vậy phải làm sao để “trám” những ổ gà này, để đạt tới một “cảnh giới” cao hơn? Cùng đón xem trong bài viết sau của series: “Điều gì ngăn cản bạn đạt tới cảnh giới “tối cao” trong code học” nhé.
Chú thích:
Khái niệm này được bác Joel (đồng sáng lậpstackoverflow) đưa ra trong một bài viết trên blog, nguyên văn tiếng Anh là leaky abstraction: All non-trivial abstractions, to some degree, are leaky. Bài viết này cũng gây khá nhiều tiếng vang khi mới ra mắt. Nếu có thể, các bạn nên đọc bài viết gốc để “thấm” điều tác giả muốn nói: http://www.joelonsoftware.com/articles/LeakyAbstractions.html
Nếu dịch nguyên văn “leaky abstraction” là “rò rỉ trừu tượng” thì nghe sida quá. Lục tìm khắp các bài viết ở VN cũng chẳng thấy ai nhắc đến gái niệm này, nên mình xin mạn phép Việt hóa khái niệm này bằng cái tên “ổ gà” cho dễ nhớ dễ thuộc vậy.
Techtalk via toidicodedao