[Học lập trình C++] Chương 1: 1.10a – Làm thế nào để thiết kế chương trình đâu tiên của bạn
1.10a – Làm thế nào để thiết kế chương trình
đâu tiên của bạn
Bây giờ bạn đã học được
một vài khái niệm cơ bản về chương trình, hãy quan sát kĩ hơn thiết kế một
chương trình như thế nào. Khi bạn ngồi xuống viết một chương trình, thông thường
bạn phải có một vài vấn đề muốn giải quyết, hoặc tình huống mà bạn muốn mô phỏng.
Lập trình viên mới thường có một vấn đề là chuyển ý tưởng thành code thực sự.
Nhưng không thể, bạn có nhiều kĩ năng giải quyết vấn đề bạn cần rồi, thu được từ
cuộc sống hằng ngày.
Thứ quan trọng nhất để
nhớ (và thứ khó nhất để làm) là thiết kế chương trình của bạn trước khi bạn bắt
đầu lập trình. Trong nhiều khía cạnh, lập trình giống như một nhà kiến trúc. Những
gì xảy ra nếu bạn thử xây dựng một ngôi nhà mà không theo một kế hoạch kiến trúc?
Điểm khác biệt là đây, trừ khi bạn là một người rất tài năng, bạn sẽ hoàn thành
ngôi nhà mà có nhiều vấn đề: tường không được thẳng, mái bị rỉ nước, vân
vân…Tương tự, nếu bạn thử lập trình trước khi bạn có một kế hoạch tốt để tiến về
phía trước, bạn sẽ tìm thấy code của bạn gặp nhiều vấn đề, và bạn sẽ phải dành
nhiều thời gian để sửa những vấn đề đó mà bạn có thể tránh được những thứ đó nếu
chịu khó suy nghĩ một chút.
Một ít kế hoạch sẽ tiết
kiệm cả thời gian và sự thất bại trên đường dài.
Bước 1: Định nghĩa vấn đề
Điều đầu tiên bạn cần
tìm ra đó là vấn đề của chương trình của bạn đang giải quyết. Lý tưởng, bạn nên
liệt kê ra trong một hoặc hai. Ví dụ.
Tôi muốn viết một ứng
dụng phone book để giúp tôi lưu trữ số điện thoại của bạn bè.
Tôi muốn viết một hàm
tạo số ngẫu nhiên để tạo hiệu ứng thú vị.
Tôi muốn viết một
chương trình đọc thông tin về chứng khoán từ mạng internet và dự đoán được cái
nào nên mua.
Mặc dù bước này trong
có vẻ hiển nhiên, nhưng rất quan trọng. Điều tồi tệ bạn có thể làm là viết một
chương trình mà không thực sự làm những gì bạn (hoặc sếp bạn) muốn.
Bước 2: Định nghĩa công cụ của bạn, mục đích,
và kế hoạch dự phòng
Khi bạn là một lập
trình viên có kinh nghiệm, có nhiều bước mà thông thường diễn ra ở điểm này bao
gồm:
Hiểu ai là khách hàng
mục tiêu và những gì họ muốn.
Định nghĩa mục tiêu cấu
trúc là gì và hệ điều hành mà chương trình của bạn sẽ chạy trên đó.
Xác định những công cụ
mà bạn cần sử dụng.
Xác định xem bạn nên
viết chương trình của mình một mình hay là tham gia vào một đội.
Thu thập những yêu cầu
(một danh sách tài liệu của những gì chương trình cần phải làm).
Định nghĩa phương
pháp testing/feeback/release.
Xác định bạn sẽ khôi
phục dữ như thế nào.
Tuy nhiên, đối với một
lập trình viên mới, câu trả lời cho những câu hỏi này khá đơn giản: Bạn đang viết một chương trình cho chính bạn,
một mình, trên hệ điều hành của bạn, sử dụng IDE bạn mua hoặc bạn downloaded,
và mã lệnh của bạn có thể không được sử dụng bởi bất kì ai ngoài bạn. Mọi thứ
thật dễ dàng.
Có thể nói rằng, nếu
bạn dự định làm việc trên bất kì thứ gì phức tạp không bình thường, bạn sẽ có một
kế hoạch lưu trữ code của bạn. Nó không đủ để chỉ nén hoặc sao chép đường dẫn đến
một vị trí khác trên máy của bạn (mặc dù điều này không tốt hơn). Nếu hệ thống
của bạn bị crashes, bạn sẽ mất mọi thứ. Có nhiều cách để sao lưu: Gửi mail cho
chính bạn, sao chép sang một Dropbox hoặc một dịch vụ cloud khác. FTP nó sang một
máy khác, hoặc sao chép sang máy khác trên mạng nội bộ.
Bước 3: Chia vấn đề khó thành những vấn đề nhỏ
hơn.
Trong đời thực, chúng
ta thường thực thi task rất phức tạp. Có gắng tìm ra cách thực hiện công việc
đó có thể rất khó khăn. Trong những trường hợp như vậy, chúng ta thường sử dụng
phương thức top down để giải quyết. Thay vì chúng ta giải quyết một vấn đề đơn
phức tạp, chúng ta chia nhỏ thành nhiều task. Mỗi một vấn đề nhỏ đó thông thường
dễ giải quyết, chúng có thể chia nhỏ hơn nữa. Bằng cách tiếp tục chia nhỏ những
công việc phức tạp thành những công việc nhỏ hơn.
Hãy xem một ví dụ. Giả
sử chúng ta muốn viết một báo cáo về củ cà rốt. Công việc của chúng ta là:
Viết một báo cáo về củ
cà rốt. Viết báo cáo về cà rốt là một công việc khá lớn để làm, vì vậy hãy chia
nhỏ trong những task nhỏ hơn:
Viết một báo cáo về
cà rốt:
·
Nghiện cứu
cà rốt
·
Viết đề
cương
·
Điền trong
đề cương chi tiết thông tin về cà rốt
·
Thêm mục
lục
Đây dễ quản lý hơn,
như chúng ta biết khi có những công việc nhỏ hơn, chúng ta có thể tập trung
trên riêng rẽ. Tuy nhiên, trong trường hợp này, “Thực hiện nghiên cứu trên cà rốt
là một cái gì đó mơ hồ”, vì vậy chúng ta có thể chia nhỏ nó ra hơn nữa:
·
Nghiện cứu
cà rốt
o
Đến thư
viện và đọc sách về cà rốt.
o
Tìm kiếm
thông tin về cà rốt trên internet
o
Chú ý những
phần thích hợp trong tài liệu tham khảo
·
Viết đề
cương
o
Thông tin
về sự phát triển
o
Thông tin
về sinh trưởng.
o
Thông tin
về dinh dưỡng.
·
Điền
trong đề cương chi tiết thông tin về cà rốt
·
Thêm mục
lục
Bây giờ chúng ta đã
có một cây cấp bậc của công việc, không có phần nào khó. Bằng cách hoàn thành mỗi
phần liên quan, chúng ta có thể hoàn thành những vấn đề khó khăn của việc viết
báo cáo về cà rốt.
Một cách khác để tạo
một hệ thống cấp bậc công việc là thực hiện phương pháp bottom up. Trong phương
pháp này, chúng ta sẽ bắt đầu từ một danh sách của công việc dễ dàng, và xây dựng
hệ thống cấp bậc bởi việc nhóm chúng lại.
Một ví dụ, nhiều người
phải đi làm hoặc đi học mỗi ngày, vì vậy giả sử chúng ta muốn giải quyết một vấn
đề của “Công việc từ lúc thức dậy cho đến khi đi làm”. Nếu bạn được yêu cầu
công việc gì bạn làm trong buổi sáng từ lúc thức dậy đến khi đi làm, bạn phải
thực hiện những điêu sau:
Thay quần áo cũ
Mặc quần áo mới
Ăn sáng
Lái xe đi làm
Đánh răng
Ra khỏi giường
Chuẩn bị bữa sáng
Lên xe
Tắm
Sử dụng bottom up
method, chúng ta có thể tổ chức chúng thành một hệ thống cấp bậc bằng cách tìm
cách nhóm những thứ tương tự lại với nhau:
Những việc làm từ lúc
thức dậy đến lúc đi làm
·
Những thứ
trong phòng ngủ
o
Ra khỏi
giường
o
Thay quần
áo
·
Những thứ
trong phòng tắm
o
Tắm
o
Đánh răng
·
Những thứ
của bữa sáng
o
Chuẩn bị
bữa sáng
o
Ăn sáng
·
Những thứ
liên quan đến di chuyển
o
Lên xe
o
Lái xe đi
làm
Những hệ thống cấp bậc
công việc cực kì hữu ích trong lập trình, bởi vì một khi bạn có một cấu trúc
các task, về bản chất bạn sẽ định nghĩa cấu trúc của toàn một chương trình.
Công việc trên cùng
(trong trường hợp, “Viết một báo cáo về cà rốt” hoặc “Công việc từ lúc thức dậy
cho tới khi đi làm”) trở thành hàm main() (bởi vì nó là vấn đề chính mà bạn có
gắng giải quyết. Những phần khác trở thành hàm trong chương trình.
Nếu bạn gặp một hàm
nào đó quá khó thì bạn nên chia nhỏ nó ra cho đến khi nào trở thành những hàm dễ
thực thi hơn.
Bước 4: Tìm ra thứ tự của các sự kiện
Bây giờ chương trình
của bạn có một cấu trúc, đã đến thời điểm xác định làm thế nào để liên kết tất
cả các công việc lại với nhau. Bước đầu tiên là xác định trình tự các sự kiện
được thực thi. Ví dụ, khi bạn thức dậy vào sáng, bạn làm nhiệm vụ trên theo thứ
tự nào? Nó có thể giống như sau:
Ra khỏi giường
Thay quần áo
Đi tắm
Mặc quần áo
Chuẩn bị bữa sáng
Ăn sáng
Đánh răng
Lên xe
Lái xe đi làm
Nếu bạn viết một phép
tính, chúng ta có thể làm những điều sau:
Lấy các số từ người
dùng
Lấy phép tính toán học
từ người dùng
Lấy số thứ hai từ người
dùng
Tính toán kết quả
In kết quả
int main()
{
getOutOfBed();
pickOutClothes();
takeAShower();
getDressed();
prepareBreakfast();
eatBreakfast();
brushTeeth();
getInCar();
driveToWork();
}
Hoặc trong trường hợp
của máy tính:
int main()
{
// Get first number from user
getUserInput();
// Get mathematical operation from user
getMathematicalOperation();
// Get second number from user
getUserInput();
// Calculate result
calculateResult();
// Print result
printResult();
}
Chú ý rằng nếu bạn
đang sử dụng phương pháp đề cương để xây dựng chương trình của bạn, nó là một ý
kiến hay nếu bạn comment các hàm cho đến khi thực sự viết chúng. Và mỗi khi viết
xong một hàm hãy kiểm tra chúng luôn.
Bước 5: Tìm ra dữ liệu đầu vào và đầu ra cho mỗi
công việc
Một khi bạn đã có một
hệ thống cấp bậc và trình từ các sự kiện, thứ tiếp theo cần tìm ra đó là dữ liệu
đầu vào cho mỗi công việc là gì, nó tạo ra kết quả như thế nào. Nếu bạn có dữ
liệu đầu vào từ bước trước đó, thì đầu vào sẽ trở thành tham số. Nếu bạn đang
tính toán ngõ ra để hàm khác có thể sử dụng, đầu ra sẽ thường trả về giá trị.
Khi chúng ta thực hiện
xong, chúng ta nên tạo nguyên mẫu hàm cho mỗi hàm. Trong trường hợp bạn quên, một
nguyên mẫu hàm là một khai báo của hàm mà bao gồm tên hàm, tham số truyền vào,
kiểu trả về và không có thân hàm.
Hãy thực hiện một vài
ví dụ. hàm calculateResult() sẽ cần 3 đầu vào: hai số và một toán tử toán học.
Chúng ta nên có tất cả là 3 tham số. Hàm calculateResult() sẽ tính toán giá trị
trả về, nhưng nó không hiển thị kết quả, do đó chúng ta cần trả về một kết quả
mà hàm khác có thể sử dụng được.
Chúng ta có thể viết
nguyên mẫu hàm như sau:
int calculateResult(int input1, int op, int input2);
Bước 6: Viết chi tiết công việc
Trong bước này, cho mỗi
công việc, bạn sẽ viết hàm thực thi chi tiết. Nếu bạn chia công việc thành những
phần đủ nhỏ, mỗi công việc nên đơn giản và dễ hiểu. Nếu một công việc dường như
vẫn khó hiểu có lẽ nó cần được chia nhỏ thêm để dễ thực thi.
Ví dụ:
int getMathematicalOperation()
{
std::cout << "Please
enter which operator you want (1 = +, 2 = -, 3 = *, 4 = /): ";
int op;
std::cin >> op;
// What if the user enters an invalid character?
// We'll ignore this
possibility for now
return op;
}
Bước 7: Kết nối dữ liệu đầu vào và đầu ra
Cuối cùng, bước cuối
cùng là kết nối đầu vào và đầu ra của mỗi công việc theo một cách thích hợp nào
đó. Ví dụ, bạn có thể gửi đầu ra của calculateResult() thành một ngõ vào của
hàm printResult(), vì vậy nó có thể in kết quả. Thường sẽ phải sử dụng biến tạm
để lưu trữ kết quả.
// result is a temporary value used to transfer the
output of calculateResult()
// into an input of printResult()
int result = calculateResult(input1, op, input2); //
temporarily store the calculated result in result
printResult(result);
Điều này có xu hướng
dẽ đọc hơn là dùng phiên bản không sử dụng biến tạm.
printResult( calculateResult(input1, op, input2) );
Đây thường là bước
khó nhất cho người mới lập trình.
// #include "stdafx.h" // uncomment if
using visual studio
#include <iostream>
int getUserInput()
{
std::cout << "Please enter an integer:
";
int value;
std::cin >> value;
return value;
}
int getMathematicalOperation()
{
std::cout << "Please enter which operator
you want (1 = +, 2 = -, 3 = *, 4 = /): ";
int op;
std::cin >> op;
// What if the user enters an
invalid character?
// We'll ignore this possibility for now
return op;
}
int calculateResult(int x, int op, int y)
{
// note: we use the == operator
to compare two values to see if they are equal
// we need to use if statements here because there's
no direct way to convert op into the appropriate operator
if (op == 1) // if user chose addition (#1)
return x + y; // execute this
line
if (op == 2) // if user chose subtraction (#2)
return x - y; // execute this
line
if (op == 3) // if user chose multiplication (#3)
return x * y; // execute this
line
if (op == 4) // if user chose division (#4)
return x / y; // execute this
line
return -1; // default
"error" value in case user passed in an invalid op
// note: This isn't a good way to handle errors,
since -1 could be returned as a legitimate value
}
void printResult(int result)
{
std::cout << "Your
result is: " << result << std::endl;
}
int main()
{
// Get first number from user
int input1 = getUserInput();
// Get mathematical operation
from user
int op = getMathematicalOperation();
// Get second number from user
int input2 = getUserInput();
// Calculate result and store
in temporary variable (for readability/debug-ability)
int result = calculateResult(input1, op, input2 );
// Print result
printResult(result);
}
Một vài lời khuyên khi viết chương trình
Giữ cho chương trình của bạn đơn giản. Thông thường nếu những lập trình viên có một
tầm nhìn rộng lớn cho tất cả mọi thứ mà họ muốn chương trình của họ thực hiện.
“Tôi muốn viết một trò chơi với đồ họa và âm thanh và có quái vật ngẫu nhiên”.
Nếu bạn thử viết một cái gì đó quá phức tạp để bắt đầu, bạn sẽ trở nên quá tải
và chán ngán về những thứ bạn còn thiếu. Thay vào đó, đặt ra mực tiêu đầu tiên
đơn giản nhất có thể, một cái gì đó chắc chắn trong tầm kiểm soát của bạn. Ví dụ,
“Tôi muốn có thể hiển thị 2d trên màn hình”
Thêm tính năng dần. Một khi bạn có chương trình làm việc tốt rồi,
sau đó bạn có thể thêm chức năng vào nó. Ví dụ, khi bạn có thể hiển thị trường
2d, thêm một nhân vật có thể đi lại xung quanh. Một khi bạn đi lại xung quanh,
thêm tường bảo vệ. Một khi bạn có những bức tường, xây dựng thị trấn đơn giản.
Một khi bạn có thị trấn, thêm chợ. Bằng cách tăng thêm tính năng, chương trình
của bạn sẽ càng phức tạp hơn mà không bị rối trong tiến trình.
Tập trung vào một vùng trong một thời điểm. Đừng cố gắng code mọi thứ một lúc, và đừng
phân tán sự chú ý vào nhiều task. Tập trung vào một task tại một thời điểm, và dành
nhiều thời gian nhất có thể cho nó đến khi hoàn thành. Nó tốt hơn là có một
task đã hoàn thành và 5 task chưa hoàn thành còn hơn là có 6 task đang thực hiện.
Nếu bạn phân tán sự tập trung của bạn, bạn dễ mắc phải những sai lầm và quên những
chi tiết quan trọng.
Kiểm tra mỗi phần code ngay khi làm xong. Lập trình viên mới sẽ thường viết tonaf bộ
chương trình một lần. Sau đó họ sẽ biên dịch nó lần đầu, có đến hàng trăm lỗi.
Điều này không chỉ đáng sợ, nếu code của bạn không hoạt động rất khó có thể tìm
ra tại sao. Thay vào đó, viết một phần code, và sau đó biên dịch và kiểm tra
ngay lập tức. Nếu nó không hoạt động, bạn sẽ biết chính xác vấn đề xảy ra ở
đâu, và nó dễ dàng sửa. Một khi bạn chắc chắn code của bạn hoạt động, chuyển
sang phần kế tiếp và lặp lại. Nó có thể mất nhiều thời gian hơn để viết xong,
nhưng sẽ không phải dành thời gian lần thứ hai để cố gắng tìm ra tại sao nó
không hoạt động.
Hầu hết lập trình
viên mới sẽ bỏ qua nhiều bước này và đề nghị (bởi vì nó trông có vẻ có nhiều việc
và không thú vị khi viết code). Tuy nhiên, đối với những project đặc biệt, làm
theo những bước như thế này sẽ chắc chắn sẽ tiết kiệm nhiều thời gian về lâu
dài. Lập kế hoạch trước một ít sẽ tiết kiệm nhiều thời gian gỡ rối khi viết
xong.
Tin tốt là một khi bạn
trở nên thoải mái với tất cả khái niệm, chúng sẽ bắt đầu trở nên quen thuộc với
bạn mà không cần nghĩ về nó. Cuối cùng, bạn sẽ có được nơi mà bạn có thể viết
toàn bộ hàm mà không cần lập bất kì kế hoạch nào trước.
Nguồn: learncpp.com
Nhận xét
Đăng nhận xét