Những điều cần biết về lập trình

1. Những yêu cầu với 1 chương trình máy tính

Đầu tiên ta cần biết lập trình là viết ra những chương trình nhằm giúp máy tính giải quyết những bài toán, những yêu cầu nào đó trong thực tế cho ta. Vì vậy có 1 số yêu cầu nhất định cho việc viết chương trình:
+Đúng đắn: chương trình phải đáp ứng được những yêu cầu được đặt ra.

+Chính xác

+Tin cậy: bảo đảm cùng 1 dữ liệu nhập vào thì kết quả trả ra luôn luôn giống nhau

+Tổng quát: người lập trình viên phải có khả năng nhìn thấy tất cả mọi khả năng có thể của dữ liệu nhập vào và chương trình phải làm việc tốt được với mọi dữ liệu nhập vào.

+An toàn: chương trình phải làm việc tốt ngay cả trong những điều kiện bất thường xảy ra, hạn chế việc chương trình bị “chết” trong quá trình chạy vì nhiều nguyên nhân như dữ liệu nhập vào không phù hợp, lỗi truy cập…vv… (hình như việc này ngay cả Microsoft cũng…ko làm được vì Widows cứ bị hư hoài à…).

+Hiệu quả: Chương trình phải tiết kiệm tài nguyên ở mức thấp nhất có thể, chạy nhanh nhất ở mức có thể…

+Dễ nâng cấp, sửa chữa.

2.Thiết kế chương trình:

a. Các xu hướng thiết kế:
Hiện nay có 2 xu hướng chính được chọn lựa trong việc thiết kế chương trình máy tính đó là : hướng thủ tục và hướng đối tượng

a.1.Hướng thủ tục(MP):
Thông qua 1 quá trình phân tích nhất định, lập trình viên sẽ tách bài toán hay yêu cầu ban đầu thành những bài toán con, yêu cầu con đơn giản hơn và giải quyết từng thứ 1. Việc này sẽ giúp bạn thực hiện được các bài toán phức tạp 1 cách dễ dàng. Bởi vì viết tổng thể cả 1 bài toán phức tạp là 1 việc làm rất khó và gần như Mission Impossible, nhưng chia nhỏ nó ra thành từng phần nhỏ hơn để giải quyết rùi gắn từng phần riêng đó lại là lựa chọn dễ dàng và khôn ngoan hơn. Việc này còn giúp cho chương trình của bạn dễ đọc, dễ bảo quản và dễ sửa chữa hơn.

Vd: yêu cầu đặt ra là bạn phải viết 1 chương trình dọc dữ liệu từ 1 file chứa các thông tin về số nhân viên, thông tin của từng nhân viên của 1 công ty, xử lí và trả lại thông tin về lương sẽ trả tháng này của từng nhân viên.

Như vậy nếu bạn để y nguyên như vậy mà làm sẽ rất khó và khi có sai sót bạn sẽ rất khó dò lỗi trong 1 khối chương trình gộp chung như vậy. Vì vậy bạn sẽ tách quá chương trình ra 1 số quá trình đơn giản hơn như sau:

Xử lí việc đọc file
Xứ lí thông tin đọc được từ file
Xử lí việc thông báo kết quả.

Từ các quá trình này bạn có thể tách thêm các quá trình con nữa nếu cần.

Như vậy khi xảy ra lỗi trong lập trình thì việc tìm lỗi và sữ chữa sẽ dễ dàng hơn cho bạn. Chẳng hạn khi lỗi thuộc về việc đọc file thì bạn chỉ cần dò tìm nó trong đoạn thủ tục bạn viết dùng để đọc file.

a.2.Hướng đối tượng(OOP): đây là 1 hướng thiết kế mới và gần với thực tế hơn phương pháp hướng thủ tục nhưng đương nhiên đòi hỏi ở bạn nhiều việc để làm và suy nghĩ hơn. 1 cách cơ bản mà nói thì hướng đối tượng nhìn vấn đề với 1 “con mắt khác” so với hướng thủ tục.

Khác như thế nào???
Nếu như ở hướng thủ tục bạn nhìn chương trình như “1 đống những công việc nhỏ cần phải làm tuần tự”. Thì khác 1 chút ở hướng đối tượng bạn nhìn theo hướng “1 đống những dữ liệu cần được tách riêng và xử lí” (đây là cảm nhận và cách hiểu riêng của snow về 2 hướng lập trình này…).

Vậy tại sao nói hướng đối tượng gần với thực tế hơn hướng thủ tục???
Đơn giản là vì trong thực tế mọi thứ tồn tại vốn dưới dạng các đối tượng (dữ liệu) khác nhau và các đối tượng đó tương tác với nhau chứ ko phải là các thủ tục.

Vd: bạn có thể hình dung như thế này. Cũng với ví dụ tính lương ở trên.

+Bây giờ giả sử tất cả các nhân viên đó đều thuộc về 1 công ty và có những chức vụ khác nhau. Như vậy nếu nhìn theo hướng thủ tục bạn sẽ chỉ thấy việc phải xử lí riêng cho từng loại nhân viên thuộc từng chức vụ 1 và việc này hơi “rối mù“ 1 chút trong chương trình và dễ gây nhầm lẫn cũng như sẽ khó cho việc sửa chữa, nâng cấp về sau nếu như xuất hiện thêm những chức vụ mới.

+Nhưng nếu bạn nhìn theo hướng đối tượng bạn sẽ thấy rằng đầu tiên ở đây có chung 1 loại đối tượng lớn nhất là “nhân viên”. Tiếp theo từ “nhân viên” ta có thể tách ra những đối tượng con của nó là “công nhân”, “trưởng phong”, “thư kí”, “giám đốc”, mỗi 1 đối tượng con sẽ có chung các đặc điểm của đối tượng cha và có riêng các đặc tính riêng của nó. Như vậy khi có thêm chức vụ thì bạn chỉ đơn giản thêm vào 1 đối tượng con mới chứ ko cần sửa lại đoạn mã đã viết ban đầu.

Nhưng bạn phải hiểu đối tượng trong hướng đối tượng ko chỉ đơn giản là “dữ liệu” mà mỗi đối tượng ngoài dữ liệu còn mang trong nó những thủ tục, hành vi của riêng đối tượng đó và đối tượng tương tác với các đối tượng khác thông qua các hành vi này. Và các đối tượng giống nhau sẽ có cùng những hành vi giống nhau. Đây là tính đóng gói của hướng đối tượng.

Ngoài ra hướng đối tượng còn có tính thừa kế nghĩa là tất cả những kiểu đối tượng con dẫn xuất từ cùng 1 kiểu đối tượng cha sẽ được thừa kế những “đặc tính” di truyền của cha (về dữ liệu cũng như hành vi…).

Một tính chất quan trọng khác của hướng đối tượng là tính đa hình, nghĩa là cùng 1 hành vi nhưng đối tượng sẽ có cách “xử sự” khác nhau với từng loại đối tượng khác nhau tuỳ theo định nghĩa của bạn về hành vi của đối tượng đối với từng loại đối tượng khác nhau…

b.Các phương pháp thiết kế:

Ở đây cũng có 2 phương pháp thiết kế chính là từ trên xuống (Top-down) và từ dưới lên (Bottom-up)

b.1.Top-down:
Đây là phương pháp thiết kế mang tính đi sâu vào tổng quát trước. Ở từng tầng của phương pháp thiết kế này chức năng tổng quát nhất được đem ra làm tên gọi, sau đó bạn phát triển những tầng tiếp theo nhỏ hơn của chức năng đó. Và cứ tiếp tục phân tầng như vậy cho tới khi các chức năng đã được đơn giản ở mức có thể thực hiện dễ dàng và hiệu quả.

b.2.Bottom-up:
Ngược lại với kiểu thiết kế top-down là kiểu bottom-up. Kiểu này được sử dụng chủ yếu khi bạn cần xây dựng chương trình từ những thứ đã có sẵn. Như vậy bạn chỉ cần tìm các thành phần cần thiết từ những thứ đã có. Một ví dụ đơn giản là khi bạn sử dụng các hàm đã có sẵn và được hỗ trợ của ngôn ngữ lập trình để xây dựng 1 ứng dụng đơn giản nào đó.

3.Các cấu trúc điều khiển cơ bản:

Dù chương trình của bạn có được xây dựng phức tạp như thế nào, có ứng dụng những giải thuật cao siêu tới đâu thì thực chất 2 thành phần cơ bản nhất xây dựng ra chúng vẫn chỉ là dữ liệu và các cấu trúc điều khiển. ở đây snow nói về các cấu trúc điều khiển cơ bản:

+Cấu trúc điều khiển tuần tự:
các toán tử, hàm, câu lệnh, dữ liệu được xử lí theo nguyên tắc tuần tự: trái sang phải, trên xuống dưới. Bạn phải nắm rõ sự tuần tự này và cả các thay đổi của dữ liệu trong thứ tự làm việc của chương trình nếu muốn đảm bảo chương trình sẽ chạy đúng.

+Cấu trúc điều kiện:
Bao gồm những toán tử, hàm, câu lệnh, dữ liệu chỉ được thực hiện hoặc xử lí khi 1 điều kiện nào đó được thỏa mãn.

+Cấu trúc lặp:
Gồm những toán tử, hàm, câu lệnh, dữ liện sẽ được xử lí lặp lại 1 số hữu hạn lần nào đó. Bạn phải chú ý khi sử dụng cấu trúc lặp để tránh tạo ra những cấu trúc lặp vô hạn sẽ dẫn đến việc treo máy. Vì vậy yêu cầu đầu tiên khi sử dụng cấu trúc lặp là điều kiện dừng của vòng lặp phải đúng đắn. Bạn còn phải lưu ý tính nhớ và sự thay đổi của dữ liệu trong vòng lặp để đảm bảo cho chương trình chạy đúng.

+Ngoài 3 cấu trúc cơ bản trên còn 1 kiểu lập trình rất cần thiết cho 1 số bài toán, đó là kiểu đệ qui. Đôi khi đây là “điều không thể tránh khỏi” khi bạn “đụng” vào những bài toán có bạn chất là đệ qui.

1 thủ tục đệ qui sẽ gọi lại bản thân nó tại 1 nơi nào đó bên trong thủ tục đó nếu 1 điều kiện dừng nào đó ko được thoả. Cũng giống như cấu trúc lặp, khi sử dụng đệ qui bạn phải đảm bảo điều kiện dừng cho nó. Nhưng bạn cần lưu ý là đệ qui sẽ chiếm 1 số khá lớn tài nguyên bộ nhớ do mỗi 1 lần đệ qui, các mã lệnh sẽ được “quăng” vào stack của bộ nhớ và lưu lại đó cho tới khi được xử lí. Như vậy đệ qui càng nhiều lần thì bộ nhớ sẽ “nặng” và ảnh hưởng tới hiệu quả của các chương trình khác. Trong 1 số trường hợp bạn có thể khử đệ qui bằng cách sử dụng các vòng lặp, stack hoặc queue…

1 ví dụ đơn giản về đệ qui trên C:
Chẳng hạn để tính 1 giai thừa n!, bạn có thể sử dụng 1 hàm đệ qui như sau:

int Giai_thua(int n)
{
if(n==1)
return 1;
return n*Giai_thua(n-1);
}

Nhưng cũng có thể sử dụng vòng lặp để giải quyết:

int Giai_thua(int n)
{
int result=1;
for(int i=2; i<=n; i++)
result *= i
return result;
}

4. Tổng quát về trình biên dịch và thông dịch của ngôn ngữ lập trình:

Ở đây snow chỉ nói sơ sơ những gì snow hiểu về trình biên dịch và thông dịch để giúp các bạn khỏi thắc mắc xem “nó làm ăn ra sao???”. Và đây cũng được coi là những hiểu biết sơ đẳng nhất mà bạn cần biết khi lập trình:

Như snow đã từng đề cập ở trên, việc lập trình ngày nay ko phải là cái kiểu ngồi dịch từng mã nhị phân như “cái thuở ban đầu lưu luyến ấy” nữa. Ngày nay các lập trình viên tạo ra các chương trình bằng việc lập trình bằng 1 ngôn ngữ nào đó và đem biên dịch bằng trình biên dịch của ngôn ngữ đó trước khi chạy chương trình, hoặc vừa chạy vừa dịch(chạy tới đâu dịch tới đó) nếu là trình thông dịch.

2 chương trình trên thực chất có thể coi là trung gian giao tiếp giữa người lập trình viên và máy tính. Hiểu nó bạn sẽ hiểu “Ê, tại sao tui viết cái lệnh toàn tiếng Anh hông hà mà mấy cái máy nó hiểu hay vậy???”. Thiệt ra thì máy đâu có hiểu mấy cái “tiếng Anh nửa mùa” mà bạn viết. Nó chỉ hiểu mã nhị phân 0,1 của những gì bạn viết ra đã được trình biên dịch hoặc thông dịch dịch lại cho nó “nghe” thôi. Nhưng giữa 2 chương trình thông dịch và biên dịch có những khác biệt nhất định dù chúng có cùng nhiệm vụ là “dịch” lại cho máy “nghe” những gì bạn “nói” với nó.

4.1 Trình biên dịch:
Toàn bộ mã chương trình mà bạn viết sẽ được trình này dịch ra hợp ngữ (assembly) để chuyển thành chỉ thị máy. Trong quá trình này có thể bao gồm quá trình tiền xử lí, biên dịch từng đoạn nhỏ của chương trình, kiểm tra lỗi, kiểm tra kiểu của các biến…, lắp ráp lại thành chương trình cuối cùng để máy có thể thực thi.

4.2 Trình thông dịch:
Khác với trình biên dịch, trình thôn dịch không dịch cùng lúc toàn bộ mã chương trình mà nó dịch từng dòng mã lệnh thành những chỉ thị mà máy có thể thực thi ngay. Từng lệnh sẽ được dịch và được thi hành ngay trong khi dịch chứ ko phải dịch xong tất cả, ráp nối lại rồi mới chạy như trình biên dịch. Nhưng đây cũng có thể là điểm bất lợi của trình thông dịch vì cùng 1 câu lệnh như nhau bạn có thể phải dịch lại nhiều lần ở nhiều thời điểm khác nhau. Trong khi ở trình biên dịch bạn sẽ chỉ phải dịch 1lần, khi cần thì trỏ vào đoạn mã chương trình đã được dịch trước đó.

(Sưu tầm)