[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

Bài đăng phổ biến