1. Công việc của một lập trình viên

Các lập trình viên mới vào nghề thường nghĩ về công việc của một lập trình viên hoàn toàn khác so với cách các lập trình viên có kinh nghiệm nghĩ về nó.

Những người mới bắt đầu thường nói những điều như "Chương trình hoạt động, bạn cần gì nữa?" Một lập trình viên có kinh nghiệm biết rằng "hoạt động chính xác" chỉ là một trong những yêu cầu đối với một chương trìnhnó thậm chí không phải là điều quan trọng nhất !

khả năng đọc mã

Điều quan trọng nhất là mã chương trình có thể hiểu được đối với các lập trình viên khác . Điều này quan trọng hơn một chương trình hoạt động chính xác. Nhiều hơn nữa.

Nếu bạn có một chương trình không hoạt động chính xác, bạn có thể sửa nó. Nhưng nếu bạn có một chương trình mà mã không thể hiểu được, thì bạn không thể làm gì với nó.

Chỉ cần lấy bất kỳ chương trình đã biên dịch nào, chẳng hạn như notepad và thay đổi màu nền của nó thành màu đỏ. Bạn có một chương trình đang hoạt động, nhưng bạn không có mã nguồn dễ hiểu: không thể thay đổi một chương trình như vậy.

Một ví dụ trong sách giáo khoa là khi các nhà phát triển của Microsoft xóa trò chơi Pinball khỏi Windows vì họ không thể chuyển nó sang kiến ​​trúc 64-bit. Và họ thậm chí đã có mã nguồn của nó. Họ chỉ đơn giản là không thể hiểu cách hoạt động của mã .

Kế toán cho mọi trường hợp sử dụng

Yêu cầu quan trọng thứ hai đối với một chương trình là tính đến mọi kịch bản. Thông thường, mọi thứ phức tạp hơn một chút so với vẻ ngoài của chúng.

Cách một lập trình viên mới làm quen gửi tin nhắn SMS:

Một chương trình làm việc chính xác

Làm thế nào một lập trình viên chuyên nghiệp nhìn thấy nó:

Một chương trình làm việc chính xác

Kịch bản "hoạt động chính xác" thường chỉ là một trong nhiều kịch bản có thể xảy ra. Và đó là lý do tại sao nhiều người mới phàn nàn về trình xác thực tác vụ của CodeGym: chỉ có một kịch bản trong số 10 kịch bản hoạt động và lập trình viên mới cho rằng thế là đủ.


2. Tình huống bất thường

tình huống bất thường

Các tình huống bất thường có thể phát sinh trong quá trình thực hiện bất kỳ chương trình nào.

Ví dụ: bạn quyết định lưu một tệp nhưng không có dung lượng đĩa. Hoặc chương trình đang cố ghi dữ liệu vào bộ nhớ, nhưng bộ nhớ khả dụng thấp. Hoặc bạn tải một bức ảnh từ Internet nhưng trong quá trình tải về bị mất kết nối.

Đối với mỗi tình huống bất thường, lập trình viên (tác giả của chương trình) phải a) lường trước nó, b) quyết định chính xác chương trình sẽ xử lý nó như thế nào và c) viết một giải pháp gần nhất có thể với giải pháp mong muốn.

Đó là lý do tại sao các chương trình có hành vi rất đơn giản trong một thời gian khá dài: nếu xảy ra lỗi trong chương trình, chương trình sẽ kết thúc. Và đó là một cách tiếp cận khá tốt.

Giả sử bạn muốn lưu tài liệu vào đĩa, trong quá trình lưu, bạn phát hiện ra rằng không có đủ dung lượng đĩa. Bạn thích hành vi nào nhất:

  • chương trình kết thúc
  • Chương trình tiếp tục chạy, nhưng không lưu tệp.

Một lập trình viên mới làm quen có thể nghĩ rằng tùy chọn thứ hai tốt hơn, vì chương trình vẫn đang chạy. Nhưng thực tế không phải như vậy.

Hãy tưởng tượng rằng bạn đã gõ một tài liệu trong Word trong 3 giờ, nhưng sau hai phút trong quá trình viết của bạn, rõ ràng là chương trình sẽ không thể lưu tài liệu vào đĩa. Mất hai phút làm việc hay ba giờ thì tốt hơn?

Nếu chương trình không thể làm những gì nó cần, tốt hơn hết là để nó đóng lại hơn là tiếp tục giả vờ rằng mọi thứ đều ổn. Điều tốt nhất mà một chương trình có thể làm khi gặp lỗi mà nó không thể tự khắc phục là thông báo ngay vấn đề đó cho người dùng.


3. Bối cảnh về ngoại lệ

Các chương trình không phải là những chương trình duy nhất phải đối mặt với các tình huống bất thường. Chúng cũng xảy ra bên trong các chương trình - trong các phương thức. Ví dụ:

  • Một phương thức muốn ghi một tệp vào đĩa, nhưng không có dung lượng.
  • Một phương thức muốn gọi một hàm trên một biến, nhưng biến đó bằng null.
  • Phép chia cho 0 xảy ra trong một phương thức.

Trong trường hợp này, phương thức gọi có thể khắc phục tình huống (thực hiện một kịch bản thay thế) nếu nó biết loại sự cố nào đã xảy ra trong phương thức được gọi.

Nếu chúng tôi đang cố lưu tệp vào đĩa và tệp đó đã tồn tại, chúng tôi chỉ cần yêu cầu người dùng xác nhận rằng chúng tôi nên ghi đè lên tệp. Nếu không có dung lượng đĩa trống, chúng tôi có thể hiển thị thông báo cho người dùng và yêu cầu người dùng chọn một đĩa khác. Nhưng nếu chương trình hết bộ nhớ, nó sẽ bị sập.

Ngày xửa ngày xưa, các lập trình viên đã cân nhắc câu hỏi này và đưa ra giải pháp sau: tất cả các phương thức/hàm phải trả về một mã lỗi cho biết kết quả thực thi của chúng. Nếu một hàm hoạt động hoàn hảo, nó sẽ trả về 0 . Nếu không, nó trả về mã lỗi (không phải số 0).

Với cách tiếp cận lỗi này, sau hầu hết mỗi lệnh gọi hàm, các lập trình viên phải thêm một kiểm tra để xem liệu hàm đó có lỗi hay không. Mã tăng kích thước và trông như thế này:

Mã không xử lý lỗi Mã xử lý lỗi
File file = new File("ca:\\note.txt");
file.writeLine("Text");
file.close();
File file = new File("ca:\\note.txt");
int status = file.writeLine("Text");
if (status == 1)
{
   ...
}
else if (status == 2)
{
   ...
}
status = file.close();
if (status == 3)
{
   ...
}

Hơn nữa, thường thì một chức năng phát hiện ra lỗi xảy ra lại không biết phải làm gì với nó: người gọi phải trả lại lỗi và người gọi của người gọi trả lại cho người gọi của nó, v.v.

Trong một chương trình lớn, một chuỗi hàng chục lệnh gọi hàm là tiêu chuẩn: đôi khi bạn thậm chí có thể tìm thấy độ sâu của lệnh gọi hàng trăm hàm. Và bây giờ bạn phải chuyển mã lỗi từ dưới cùng lên trên cùng. Và nếu ở đâu đó trên đường đi, một số chức năng không xử lý mã thoát, thì lỗi sẽ bị mất.

Một nhược điểm khác của phương pháp này là nếu các hàm trả về mã lỗi, thì chúng không thể trả về kết quả công việc của chính chúng nữa. Kết quả tính toán phải được chuyển qua tham số tham chiếu. Điều này làm cho mã thậm chí còn cồng kềnh hơn và tăng thêm số lượng lỗi.