Tôi trở lại phòng làm việc sau buổi trưa nhưng Jean không có đó. Chẳng có một mẩu tin hay e-mail của bà để lại; và ngay cả giỏ đồ đan của bà cũng chẳng thấy tăm hơi. Sau vài phút, tôi quyết định ngồi xuống và tiếp tục làm việc với SMCRemoteServer.
Cho đến lúc này server chẳng phục vụ gì hết. Nó chỉ có vài cái hàm dùng để dựng và thi hành dòng lệnh SMC. Tôi nghĩ đúng ra mã nguồn của server phải mở một socket và tiếp nhận các đường nối từ SMCRemoteClient mà chúng tôi đã viết lúc trước.
Tôi khá ngán ngẩm với cái đà làm việc của chúng tôi hôm nay. Mất cả buổi sáng mà chúng tôi chỉ hoàn thành hai cái hàm bé tẹo thêm vào mấy cái kiểm thử đơn vị. Tôi muốn thấy sự tiến triển. Bởi thế tôi vớ lấy bàn phím và bắt đầu gõ.
“Đầu tiên,” tôi nghĩ, “các server này cần phục vụ gì đó. Thế thì hãy dùng lớp SocketServer mà Jerry và tôi xong tuần trước.”
Làm cho server phục vụ khá đơn giản. Tôi chỉ cần viết một hàm khởi tạo dùng để tạo một đối tượng của SocketService và chuyển vào trong một SocketServer. Rồi SocketServer.serve hẳn sẽ tự động được gọi khi SMCRemoteClient muốn truy cập.
public SMCRemoteServer() throws Exception { service = new SocketService(9000, new SocketServer(){ public void serve(Socket socket) { // SMCRemoteClient has connected. } }); }
Tôi xem trong mã nguồn của SMCRemoteClient và thấy rằng client đợi một string được gởi đi (từ server) sau khi đã kết nối. string đó bắt đầu bằng “SMCR”. Thế nên tôi viết đoạn string đó trong method serve().
public void serve(Socket socket) { // SMCRemoteClient has connected. try { ObjectOutputStream os = new ObjectOutputStream(socket.getOutputStream()); os.writeObject("SMCR"); }catch (IOException e) { } }
Kế tiếp server cần đọc một CompileFileTransaction từ client. Nó cần viết các tệp tin chứa trong transaction ấy, gọi trình dịch và sau đó trả vể kết quả (các hồ sơ) bên trong CompileResultsTransaction. Viết nên nó có vẻ không khó mấy, thế….
public void serve(Socket socket) { // SMCRemoteClient has connected. try { ObjectOutputStream os = new ObjectOutputStream(socket.getOutputStream()); os.writeObject("SMCR"); ObjectInputStream is = new ObjectInputStream(socket.getInputStream()); CompileFileTransaction cft = (CompileFileTransaction)is.readObject(); String filename = cft.getFilename(); cft.sourceFile.write(); String command = buildCommandLine(filename); executeCommand(command); //OK, what file do I put into the Result? }catch (Exception e) { } }
Hừm! Biên dịch tệp tin có vẻ không khó lắm nhưng tôi nên đưa vào kết quả transaction của một tệp tin xuất ra sao? Tên nó là gì? Tôi nhớ hồi sáng nay Jean đã hướng dẫn tôi viết một cái test để gọi trình dịch. Tôi xem lại phần test ấy và thấy tệp tin nhập có cái đuôi là a.sm, và tệp tin xuất có cái đuôi a.java . Thế nên tôi chỉ thay thế “.sm” bằng “.java” cho tên hồ sơ.
public void serve(Socket socket) { // SMCRemoteClient has connected. try { ObjectOutputStream os = new ObjectOutputStream(socket.getOutputStream()); os.writeObject("SMCR"); ObjectInputStream is = new ObjectInputStream(socket.getInputStream()); CompileFileTransaction cft = (CompileFileTransaction)is.readObject(); String filename = cft.getFilename(); cft.sourceFile.write(); String command = buildCommandLine(filename); executeCommand(command); //Figure out the file name. String compiledFile = filename.replaceAll(".sm", ".java"); CompilerResultsTransaction crt = new CompilerResultsTransaction(compiledFile); os.writeObject(crt); socket.close(); }catch (Exception e) { } }
Nhìn có vẻ ngon lành. Bây giờ tôi chỉ cần khởi động server và chạy client. Đơn giản thôi. Thế rồi tôi tạo tệp tin nguồn trong thư mục của tôi có cái tên là F.sm, y như cái Jean muốn tôi tạo ra sáng nay.
Context C FSMName F Initial I {I{E I A}}
Và rồi tôi viết một hàm main trong SMCRemoteServer
public static void main(String[] args) throws Exception { SMCRemoteServer server = new SMCRemoteServer(); }
Sau đó tôi chạy thử. Và nó chỉ treo ngay đó, đợi cho client truy cập. Cái này mới thật là thích! Ít ra tôi đã hoàn thành được cái gì đó! Thế rồi tiếp theo đó tôi chạy client với “F.sm” làm thông số. Và nó CHẠY! Hay nói cách khác, nó thoát ra sau nhiều giây đồng hồ với tín hiệu thoát -2- bình thường và chẳng có bất cứ thông báo lỗi nào cả từ client cũng như server. Quá thích!
Nhưng nó đã làm những gì? tôi không thể xác định ngay được. Bởi thế, tôi thử kiểm tra thư mục và thấy một tệp tin F.java nắm chễm chệ ở đó! Nó có đúng ngày trên đó, thế có nghĩa nó đã được tạo ra từ lần client chạy vừa rồi. Quá tuyệt! Tôi mở tệp tin ra xem và nó trông giống như mã Java đã được tạo nên. Nó còn nói là được SMC tạo ra. Mã nguồn của tôi chạy được! -3-
Đã mấy tuần nay tôi chưa hề cảm thấy vui sướng thế này, thật sự mà nói, kể cả từ trước khi làm việc với Jerry. Đây mới đúng là lập trình! Tôi dâng tràn cảm giác muốn chóng làm cho mã nguồn chạy. Tôi là “độc cô cầu bại”! -4- Tôi đứng lên và nhún nhảy một vòng quanh chiếc ghế, miệng lảm nhảm “Ôi! ôi! tôi là một lập trình viên. Ôi!”
Jerry hẳn ở đâu đó gần bên bởi vì gã bước vào phòng làm việc ngay lúc ấy. “Ê Alphonse, mày đang rộn ràng chuyện gì vậy?”
“Ồ, chào Jerry! Xem này! tôi mới làm cho SMCRemoteServer chạy được!”
“Thật vậy à? Vậy là chiến lắm đó.” Vẻ nhìn trên mặt gã trông buồn cười — như thể gã không tin tôi. Thế nên tôi chỉ cho gã xem. Tôi chỉ cho gã xem server ấy làm việc ra sao. Tôi chạy client một lần nữa. Tôi chỉ cho gã xem tệp tin F.java với đúng ngày giờ được tạo ra. Thậm chí tôi chỉ cho gã xem tệp tin này chứa mã java.
Jerry nhìn tôi gần như kinh hãi, và rồi gã liếc ra cửa một cách sợ sệt. “Alphonse, mấy cái tests của mày đâu?”
“Jerry, ông không cần mấy cái unit tests cho những thứ đơn giản như thế này. Nhìn xem, chỉ có một chục dòng mã nguồn hoặc hơn thôi mà. Jerry, tôi đã có tiến bộ đây này. Tôi đã hoàn tất -5- dăm ba điều. Và nó không cần phải mất cả ngày để hoàn thành! Tôi nghĩ mấy ông bà phí quá nhiều thời gian với mấy cái unit test!”
Jerry nhìn tôi chằm chặp vài giây như thể gã không thể tiếp thu những điều tôi bảo gã. Thế rồi gã đóng cửa phòng làm việc lại và ngồi xuống bên cạnh tôi.
“Alphonse, bà Jean thấy mấy cái này chưa vậy?”
“Chưa, tôi chưa thấy bà trở lại sau giờ trưa.”
“Xoá nó đi, Alphonse.”
“Xoá cái gì?”
“Xoá đoạn mã mày vừa viết xong đó. Nó vô dụng.”
“Tôi đã từng đụng kiểu phản ứng này trước đây. Tôi cong cớn bảo: “Ôi, thôi đi Jerry! nó chạy mà! làm sao nó có thể vô dụng?”
“Mày có chắc nó chạy không?”
“Thì chính ông cũng thấy đó!”
“MÀY có chắc nó chạy không?”
“Tất nhiên là nó chạy. tệp tin F.java chính là bằng chứng!”
“Alphonse, tại sao ngày giờ của tệp tin F.sm giống y hệt ngày giờ trên tệp tin F.java vậy?”
“Cái gì?” Tôi nhìn vào thư mục thì quả thật, chúng giống nhau cho đến từng giây. Nhưng không thể hiểu nổi. Tôi viết tệp tin F.sm bằng tay nhiều phút trước đây. Tại sao nó có cùng ngày giờ với tệp tin F.java vừa được tạo ra mấy giây trước, khi tôi biểu diễn cho Jerry xem? Tôi chăm chăm nhìn nó một đỗi và thú nhận tôi không biết tại sao.
“Xoá mã nguồn này đi Alphonse. Mày không hiểu gì hết.”
“Ôi, thôi đi Jerry, tôi hiểu mà. Tôi chỉ không tìm ra lý do tại sao mớ ngày giờ lại không đúng thôi. Nó chạy mà Jerry… nó chạy.” Nhưng tôi không còn dám chắc nữa. Tại sao phần ngày giờ ấy phải khác nhau nhỉ?
“Alphonse, có thể nào mày chạy client và server từ cùng một thư mục không?”
“Ui da…” Tôi không ấn định thư mục khác nhau cho chúng nên tôi đoán chúng chắc đã chạy trong cùng một nơi. “…. Tôi đoán là thế.”
“Vậy cả client và server đều đọc và viết cùng tệp tin trong cùng một thư mục?”
“… ôi..”
Jerry gật đầu với vẻ đắc thắng. “Ừa.”
Tôi gật đầu. “OK, Jerry, ông có lý lắm. Tôi không hiểu hết những gì xảy ra. Nhưng hãy xem, tệp tin F.java nằm chường ra kìa. Nhất định phải có cái gì đó chạy được!”
“Thế thì sao? Mày không biết cái gì chạy và cái gì không. Mày không hiểu mày đã làm gì. Mấy thứ này trông có vẻ như chạy được theo kiểu chó ngáp nhầm ruồi. -6- Có thể nào, ví dụ như tệp tin F.java bị sót lại từ cái unit test trước không?”
Có thể lắm! Chu choa, Jean và tôi làm cho hệ thống viết một cái F.java sáng nay! “Quỷ tha!
Vâng, có thể lắm — nhưng không e không chắc là như vậy!”
“Xoá đoạn mã đi Alphonse. Toàn rác rưởi.”
Tôi nhìn gã chằm chằm. Tôi đã tiến triển quỷ tha nó đi! Bây giờ gã lại muốn tôi xoá hết những dấu ấn tiến triển ấy. Nhưng gã ấy đúng. Tôi không hiểu đoạn mã. Và tôi chẳng có tí test nào có thể chứng minh từng bước một là mọi thứ chạy đúng nhưng dự tưởng. Nếu mã nguồn của tôi thực sự làm việc đúng (và bây giờ tôi bắt đầu thật sự băn khoăn không biết nó làm việc đúng được mấy phần) nó chạy được do may mắn hơn do thiết kế đàng hoàng.
“Xoá đoạn mã đi Alphonse. Mày chớ có để bà ấy thấy mớ mã nguồn đó. Ngay lúc này, bà ta hết lòng quan tâm -7- đến mày, và mớ mã này sẽ làm bà thất vọng đó.”
Lập trường của tôi rốt cuộc hỏng bét. Đôi vai trễ xuống, cái đầu cứng đơ và tôi chồm sang, xoá hết đoạn mã. Jerry bước ra khỏi phòng, đầu ngúc ngoắc.
Vài phút sau, bà Jean bước vào. “Chào Alphonse thân mến. Tôi xin lỗi vì chậm trễ vì tôi bị dính vào câu chuyện với người bạn cũ và bọn tôi sa đà vào chuyện so hình của mấy đứa cháu. Tôi mê khoe hình mấy đứa cháu tôi lắm. Cậu thấy chúng chưa nhỉ, cậu bé thân mến? Ồ, đừng để ý đến tôi, chúng ta có việc cần phải làm. Cậu làm gì trong khi tôi bị “tạm giam” vậy?”
“Không làm gì cả Jean, tôi chỉ đợi bà thôi.”
-1- Backslide: chỉ cho tình trạng hụt hẫng, bị rớt xuống một mức thấp hơn. Tạm dịch là “chổng gọng” cho thêm phần… dí dỏm.
-2- Exit Code: tạm dịch là tín hiệu thoát. Exit code là thuật ngữ quen thuộc với những ai đã từng lập trình, nó dựa trên các mã số đã được quy định trước để xác định một chương trình sau khi thoát ra thuộc tình trạng nào.
-3- Đoạn này tác giả dùng rất nhiều dấu chấm thang (!) sau mỗi câu Alphonse thốt ra để nhấn mạnh tình trạng cảm xúc của Alphonse lúc này. Thông thường dấu chấm thang được xếp loại và diện “kỵ dùng” quá nhiều trong văn viết nhưng trong phần này, dấu chấm thang được huy động tối đa và có tác dụng rất thích đáng.
-4- Invincible: không thể bại. Tạm dịch là “độc cô cầu bại” cho gần với tinh thần “kiếm hiệp” của dân Á châu nói chung.
-5- Chú ý các chữ hoặc cụm chữ in nghiêng trong bài này. Chúng dùng để nhấn mạnh cũng như để tạo kịch tính trong câu chuyện.
-6- working by accident: sát nghĩa là “chạy được nhờ may rủi”. Tạm dịch là “chạy được theo kiểu chó ngáp nhầm ruồi” để cố gắng lột tả cá tính của Jerry.
-7- think the world of someone: hết lòng hết dạ với ai đó, để hết tâm ý đến ai đó.
Bài trước: Thợ lành nghề #19: Kiên nhẫn (SMCRemote Phần 9)
Tác giả: Robert C. Martin
Người dịch: Hoàng Ngọc Diêu
0 Lời bình