Bây giờ bạn đã học được một số kiến ​​thức cơ bản về chương trình, hãy cùng xem xét kỹ hơn cách thiết kế chương trình. Bài hơi dài một tí,  nhưng kiến thức khá quan trọng cho người mới nha.

Khi bạn ngồi xuống để viết một chương trình, nói chung là bạn có một số loại ý tưởng mà bạn muốn viết một chương trình. Các lập trình viên mới thường gặp khó khăn khi tìm cách chuyển đổi ý tưởng đó thành code thực tế. Nhưng hóa ra, bạn có nhiều kỹ năng giải quyết vấn đề mà bạn cần từ cuộc sống hàng ngày.

Điều quan trọng nhất cần nhớ (và điều khó làm nhất) là thiết kế chương trình của bạn trước khi bạn bắt đầu viết code. Về nhiều mặt, lập trình cũng giống như kiến ​​trúc. Điều gì sẽ xảy ra nếu bạn cố gắng xây một ngôi nhà mà không tuân theo kế hoạch kiến ​​trúc? Tỷ lệ cược là, trừ khi bạn rất tài năng, bạn sẽ kết thúc với một ngôi nhà có rất nhiều vấn đề: tường không thẳng, mái dột, v.v.… Tương tự, nếu bạn cố gắng lập trình trước khi chơi tốt -Kế hoạch về phía trước, bạn có thể sẽ thấy rằng code của bạn có rất nhiều vấn đề và bạn sẽ phải dành nhiều thời gian để khắc phục các vấn đề mà lẽ ra có thể tránh được hoàn toàn với một chút suy nghĩ trước.

Lập kế hoạch từ trước một chút sẽ giúp bạn tiết kiệm cả thời gian và sự thất vọng về lâu dài.

Trong bài học này, cafedev sẽ đưa ra một cách tiếp cận tổng quát để chuyển đổi ý tưởng thành các chương trình với chức năng đơn giản.

1. Bước thiết kế 1: Xác định mục tiêu của bạn

Để viết một chương trình thành công, trước tiên bạn cần xác định mục tiêu của mình là gì. Lý tưởng nhất là bạn có thể nêu điều này trong một hoặc hai câu. Nó thường hữu ích khi thể hiện điều này như một kết quả mà người dùng phải đối mặt. Ví dụ:

Cho phép người dùng sắp xếp danh sách tên và số điện thoại liên quan.

Tạo danh sách khuyến nghị cổ phiếu cho các cổ phiếu có cổ tức cao.

Lập mô hình mất bao lâu để một quả bóng rơi từ tháp rơi xuống đất.

Mặc dù bước này có vẻ hiển nhiên, nhưng nó cũng rất quan trọng. Điều tồi tệ nhất bạn có thể làm là viết một chương trình không thực sự làm những gì bạn (hoặc sếp của bạn) muốn!

2. Bước thiết kế 2: Xác định yêu cầu

Mặc dù việc xác định vấn đề giúp bạn xác định được kết quả mong muốn nhưng nó vẫn còn mơ hồ. Bước tiếp theo là suy nghĩ về các yêu cầu.

Yêu cầu là một từ ngữ hoa mỹ cho cả những ràng buộc và giải pháp của bạn cần tuân theo (ví dụ: ngân sách, dòng thời gian, không gian, bộ nhớ, v.v.), cũng như các khả năng mà chương trình phải thể hiện để đáp ứng nhu cầu của người dùng. Lưu ý rằng các yêu cầu của bạn cũng nên tập trung vào “cái gì”, không phải “cách thức”.

Ví dụ:

Số điện thoại nên được lưu lại để có thể gọi lại sau này.

Hầm ngục ngẫu nhiên phải luôn có một con đường để đi từ lối vào đến lối ra.

Các khuyến nghị cổ phiếu nên tận dụng dữ liệu giá lịch sử.

Người dùng có thể nhập chiều cao của tháp.

Chúng tôi cần một phiên bản có thể kiểm tra trong vòng 7 ngày.

Chương trình sẽ đưa ra kết quả trong vòng 10 giây kể từ khi người dùng gửi yêu cầu của họ.

Chương trình sẽ gặp sự cố trong ít hơn 0,1% phiên người dùng.

Một vấn đề đơn lẻ có thể mang lại nhiều yêu cầu và giải pháp sẽ không được “hoàn thành” cho đến khi nó đáp ứng tất cả chúng.

3. Thiết kế bước 3: Xác định các công cụ, mục tiêu và kế hoạch dự phòng của bạn

Khi bạn là một lập trình viên có kinh nghiệm, có nhiều bước khác thường diễn ra tại thời điểm này, bao gồm:

  • Xác định kiến ​​trúc mục tiêu và / hoặc hệ điều hành mà chương trình của bạn sẽ chạy.
  • Xác định bộ công cụ bạn sẽ sử dụng.
  • Xác định xem bạn sẽ viết chương trình của mình một mình hay là một phần của nhóm.
  • Xác định chiến lược thử nghiệm / phản hồi / phát hành của bạn.
  • Xác định cách bạn sẽ sao lưu code của mình.

Tuy nhiên, là một lập trình viên mới, câu trả lời cho những câu hỏi này thường rất đơn giản: Bạn đang viết một chương trình để sử dụng riêng, một mình, trên hệ thống của riêng bạn, sử dụng IDE bạn đã mua hoặc tải xuống và code của bạn có thể không được ai sử dụng nhưng bạn. Điều này làm cho mọi thứ trở nên dễ dàng.

Điều đó nói rằng, nếu bạn định làm việc trên bất kỳ thứ gì có độ phức tạp không tầm thường, bạn nên có kế hoạch sao lưu code của mình. Chỉ nén hoặc sao chép thư mục vào một vị trí khác trên máy của bạn là không đủ (mặc dù điều này tốt hơn là không có gì). Nếu hệ thống của bạn gặp sự cố, bạn sẽ mất mọi thứ. Một chiến lược sao lưu tốt bao gồm việc xóa hoàn toàn bản sao code khỏi hệ thống của bạn. Có rất nhiều cách dễ dàng để thực hiện việc này: Zip nó và gửi nó qua email cho chính bạn, sao chép nó vào Dropbox hoặc một dịch vụ đám mây khác, FTP nó vào một máy khác, sao chép nó vào một máy khác trên mạng cục bộ của bạn hoặc sử dụng hệ thống kiểm soát phiên bản ở trên một máy khác hoặc trong đám mây (ví dụ: github). Hệ thống kiểm soát phiên bản có thêm lợi thế là không chỉ có thể khôi phục các tệp của bạn mà còn có thể khôi phục chúng về phiên bản trước đó.

4. Bước thiết kế 4: Chia các bài toán khó thành các bài toán dễ

Trong cuộc sống thực, chúng ta thường phải thực hiện những công việc rất phức tạp. Cố gắng tìm ra cách thực hiện những nhiệm vụ này có thể rất khó khăn. Trong những trường hợp như vậy, chúng tôi thường sử dụng phương pháp giải quyết vấn đề từ trên xuống. Nghĩa là, thay vì giải quyết một nhiệm vụ phức tạp, chúng ta chia nhiệm vụ đó thành nhiều nhiệm vụ con, mỗi nhiệm vụ riêng lẻ sẽ dễ giải quyết hơn. Nếu những nhiệm vụ phụ đó vẫn quá khó giải quyết, chúng có thể được chia nhỏ hơn nữa. Bằng cách liên tục chia các nhiệm vụ phức tạp thành các nhiệm vụ đơn giản hơn, cuối cùng bạn có thể đạt được điểm mà mỗi nhiệm vụ riêng lẻ có thể quản lý được, nếu không muốn nói là nhỏ nhặt.

Hãy xem một ví dụ về điều này. Giả sử chúng ta muốn dọn dẹp nhà cửa. Hệ thống phân cấp nhiệm vụ của chúng ta hiện trông như thế này:

Dọn Dẹp Nhà Cửa

Dọn dẹp toàn bộ ngôi nhà là một nhiệm vụ khá lớn phải làm chỉ trong một lần, vì vậy hãy chia nó thành các nhiệm vụ phụ:

  • Dọn Dẹp Nhà Cửa
  • Hút bụi thảm lót sàn
  • Dọn dẹp phòng tắm
  • Dọn bếp

Điều đó dễ quản lý hơn vì chúng ta hiện có các nhiệm vụ phụ mà chúng ta có thể tập trung vào từng công việc. Tuy nhiên, chúng ta có thể phân nhỏ một số trong số này hơn nữa:

  • Dọn Dẹp Nhà Cửa
  • Hút bụi thảm lót sàn
  • Dọn dẹp phòng tắm
  • Cọ rửa nhà vệ sinh (yuck!)
  • Rửa bồn rửa
  • Dọn bếp
  • Dọn dẹp mặt bàn
  • Làm sạch mặt bàn
  • Cọ rửa bồn rửa
  • Đổ rác

Bây giờ chúng ta có một hệ thống phân cấp nhiệm vụ, không có nhiệm vụ nào đặc biệt khó. Bằng cách hoàn thành từng hạng mục phụ tương đối dễ quản lý này, chúng ta có thể hoàn thành nhiệm vụ tổng thể khó khăn hơn là dọn dẹp nhà cửa.

Một cách khác để tạo hệ thống phân cấp nhiệm vụ là thực hiện từ dưới lên. Trong phương pháp này, chúng ta sẽ bắt đầu từ một danh sách các nhiệm vụ đơn giản và xây dựng hệ thống phân cấp bằng cách nhóm chúng.

Ví dụ: nhiều người phải đi làm hoặc đi học vào các ngày trong tuần, vì vậy, giả sử chúng ta muốn giải quyết vấn đề “đi làm”. Nếu được hỏi bạn đã làm những công việc gì vào buổi sáng để đi ngủ đi làm, bạn có thể đưa ra danh sách sau:

  • Chọn quần áo
  • Mặc quần áo
  • Ă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
  • Đi tắm

Sử dụng phương pháp từ dưới lên, chúng ta có thể sắp xếp chúng thành một hệ thống phân cấp các mục bằng cách tìm cách nhóm các mục có điểm tương đồng với nhau:

  • Đi từ giường đi làm
  • Đồ trong phòng ngủ
  • Ra khỏi giường
  • Chọn quần áo
  • Mặc quần áo
  • Đồ trong phòng tắm
  • Đi tắm
  • Đánh răng
  • Đồ ăn sáng
  • Chuẩn bị ngũ cốc
  • Ăn ngũ cốc
  • Vận chuyển những thứ
  • Lên xe
  • Lái xe đi làm

Hóa ra, các cấu trúc phân cấp nhiệm vụ này cực kỳ hữu ích trong lập trình, bởi vì một khi bạn có hệ thống phân cấp nhiệm vụ, về cơ bản bạn đã xác định cấu trúc của chương trình tổng thể của mình. Nhiệm vụ cấp cao nhất (trong trường hợp này là “Dọn dẹp nhà cửa” hoặc “Đi làm”) trở thành main() (vì nó là vấn đề chính mà bạn đang cố gắng giải quyết). Các nhiệm vụ nhỏ trở thành các hàm trong chương trình.

Nếu hóa ra một trong các mục (hàm) quá khó thực hiện, bạn chỉ cần chia mục đó thành nhiều mục / hàm con. Cuối cùng, bạn sẽ đạt đến điểm mà mỗi hàm trong chương trình của bạn không thể thực hiện được.

5. Bước thiết kế 5: Tìm ra chuỗi sự kiện

Bây giờ chương trình của bạn đã có cấu trúc, đã đến lúc xác định cách liên kết tất cả các nhiệm vụ với nhau. Bước đầu tiên là xác định chuỗi sự kiện sẽ được thực hiện. Ví dụ, khi bạn ngủ dậy vào buổi sáng, bạn thực hiện các công việc trên theo trình tự nào? Nó có thể trông như thế này:

  • Đồ trong phòng ngủ
  • Đồ trong phòng tắm
  • Đồ ăn sáng
  • Vận chuyển những thứ

Nếu chúng ta đang viết một máy tính, chúng ta có thể làm những việc theo thứ tự sau:

  • Nhận số đầu tiên từ người dùng
  • Nhận phép toán từ người dùng
  • Nhận số thứ hai từ người dùng
  • Tính toán kết quả
  • In kết quả

Tại thời điểm này, chúng ta đã sẵn sàng triển khai code.

6. Bước 1: Phác thảo chức năng chính của bạn

Bây giờ chúng ta đã sẵn sàng để bắt đầu triển khai. Các trình tự trên có thể được sử dụng để phác thảo chương trình chính của bạn. Đừng lo lắng về đầu vào và đầu ra trong thời gian này.

int main()
{
//    doBedroomThings();
//    doBathroomThings();
//    doBreakfastThings();
//    doTransportationThings();
 
    return 0;
}

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();
 
    return 0;
}

Lưu ý rằng nếu bạn định sử dụng phương pháp “phác thảo” này để xây dựng chương trình của mình, thì các hàm của bạn sẽ không biên dịch bởi vì các định nghĩa chưa tồn tại. Comment về các lệnh gọi hàm cho đến khi bạn sẵn sàng triển khai các định nghĩa hàm là một cách để giải quyết vấn đề này (và cách chúng ta sẽ trình bày ở đây). Ngoài ra, bạn có thể khai báo các hàm của mình (tạo các hàm giữ chỗ với các phần thân trống) để chương trình của bạn sẽ biên dịch.

7. Bước thực hiện 2: Thực hiện từng hàm

Trong bước này, đối với mỗi hàm, bạn sẽ thực hiện ba việc:

  • Xác định nguyên mẫu hàm (đầu vào và đầu ra)
  • Viết hàm
  • Kiểm tra hàm

Nếu các hàm của bạn đủ chi tiết, mỗi hàm phải khá đơn giản và dễ hiểu. Nếu một hàm đã cho vẫn có vẻ quá phức tạp để triển khai, có lẽ nó cần được chia nhỏ thành các hàm con để có thể dễ dàng triển khai hơn (hoặc có thể bạn đã làm điều gì đó không đúng thứ tự và cần xem lại trình tự các sự kiện của mình).

Hãy thực hiện hàm đầu tiên từ ví dụ máy tính:

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Group: https://www.facebook.com/groups/cafedev.vn/
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
Pinterest: https://www.pinterest.com/cafedevvn/
YouTube: https://www.youtube.com/channel/UCE7zpY_SlHGEgo67pHxqIoA/
*/
#include <iostream>
 
// Full implementation of the getUserInput function
int getUserInput()
{
    std::cout << "Enter an integer ";
    int input{};
    std::cin >> input;
 
    return input;
}
 
int main()
{
    // Get first number from user
    int value{ getUserInput() }; // Note we've included code here to test the return value!
    std::cout << value;
 
    // Get mathematical operation from user
//    getMathematicalOperation();
 
    // Get second number from user
//    getUserInput();
 
    // Calculate result
//    calculateResult();
 
    // Print result
//    printResult();
 
    return 0;
}

Đầu tiên, chúng ta đã xác định rằng hàm getUserInput không có đối số và sẽ trả về một giá trị int trở lại trình gọi. Điều đó được phản ánh trong nguyên mẫu hàm có giá trị trả về là int và không có tham số. Tiếp theo, chúng ta đã viết phần nội dung của hàm, đó là 4 câu lệnh đơn giản. Cuối cùng, chúng ta đã triển khai một số code tạm thời trong hàm main để kiểm tra xem hàm getUserInput (bao gồm cả giá trị trả về của nó) có hoạt động chính xác hay không.

Chúng ta có thể chạy chương trình này nhiều lần với các giá trị đầu vào khác nhau và đảm bảo rằng chương trình đang hoạt động như chúng ta mong đợi tại thời điểm này. Nếu chúng ta tìm thấy thứ gì đó không hoạt động, chúng ta biết rằng vấn đề nằm ở đoạn code chúng ta vừa viết. Khi chúng ta tin rằng chương trình đang hoạt động như dự kiến ​​cho đến thời điểm này, chúng ta có thể xóa code thử nghiệm tạm thời và tiến hành triển khai hàm tiếp theo (chức năng getMathelogicalOperation).

Hãy nhớ rằng: Không triển khai toàn bộ chương trình của bạn trong một lần. Làm việc theo từng bước, kiểm tra từng bước trước khi tiếp tục.

8. Bước thực hiện 3: Thử nghiệm cuối cùng

Khi chương trình của bạn đã “hoàn thành”, bước cuối cùng là kiểm tra toàn bộ chương trình và đảm bảo nó hoạt động như dự kiến. Nếu nó không hoạt động, hãy sửa nó.

9. Lời khuyên khi viết chương trình

Giữ cho các chương trình của bạn bắt đầu đơn giản. Thường thì các lập trình viên mới có tầm nhìn rộng lớn về tất cả những điều 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 nhập vai với đồ họa và âm thanh cùng những quái vật và ngục tối ngẫu nhiên, với một thị trấn bạn có thể ghé thăm để bán những vật phẩm bạn tìm thấy trong ngục tối” Nếu bạn cố gắng viết một thứ gì đó quá phức tạp để bắt đầu, bạn sẽ trở nên choáng ngợp và chán nản vì sự thiếu tiến bộ của bạn. Thay vào đó, hãy làm cho mục tiêu đầu tiên của bạn càng đơn giản càng tốt, một cái gì đó chắc chắn nằm trong tầm tay của bạn. Ví dụ: “Tôi muốn có thể hiển thị trường 2 chiều trên màn hình”.

Thêm các tính năng theo thời gian. Khi bạn đã có chương trình đơn giản của mình hoạt động tốt, thì bạn có thể thêm các tính năng cho nó. Ví dụ: khi bạn có thể hiển thị trường của mình, hãy thêm một nhân vật có thể đi lại. Khi bạn có thể đi lại, hãy thêm những bức tường có thể cản trở tiến trình của bạn. Khi bạn có tường, hãy xây một thị trấn đơn giản từ chúng. Khi bạn có một thị trấn, hãy thêm thương nhân. Bằng cách thêm dần từng tính năng, chương trình của bạn sẽ ngày càng phức tạp hơn mà không làm bạn choáng ngợp trong quá trình này.

Tập trung vào một khu vực tại một thời điểm. Đừng cố gắng viết code mọi thứ cùng một lúc và đừng phân chia sự chú ý của bạn cho nhiều nhiệm vụ. Tập trung vào một nhiệm vụ tại một thời điểm. Sẽ tốt hơn nhiều nếu có một nhiệm vụ đang làm việc và năm nhiệm vụ chưa được bắt đầu hơn là sáu nhiệm vụ đang hoạt động một phần. Nếu bạn chia nhỏ sự chú ý của mình, bạn sẽ dễ mắc lỗi và quên những chi tiết quan trọng.

Kiểm tra từng đoạn code khi bạn thực hiện. Các lập trình viên mới thường sẽ viết toàn bộ chương trình trong một lần. Sau đó, khi họ biên dịch nó lần đầu tiên, trình biên dịch báo cáo hàng trăm lỗi. Điều này có thể không chỉ đáng sợ, nếu code của bạn không hoạt động, có thể khó tìm ra lý do tại sao. Thay vào đó, hãy viết một đoạn code, sau đó biên dịch và kiểm tra nó ngay lập tức. Nếu nó không hoạt động, bạn sẽ biết chính xác vấn đề nằm ở đâu và sẽ dễ dàng khắc phục. Khi bạn chắc chắn rằng code hoạt động, hãy chuyển sang phần tiếp theo và lặp lại. Có thể mất nhiều thời gian hơn để viết xong code của bạn, nhưng khi bạn hoàn thành, toàn bộ công việc sẽ hoạt động và bạn sẽ không phải mất gấp đôi thời gian để cố gắng tìm ra lý do tại sao không.

Đừng đầu tư vào việc hoàn thiện code sớm. Bản thảo đầu tiên của một tính năng (hoặc chương trình) hiếm khi tốt. Hơn nữa, các chương trình có xu hướng phát triển theo thời gian, khi bạn thêm các khả năng và tìm ra những cách tốt hơn để cấu trúc mọi thứ. Nếu bạn đầu tư quá sớm vào việc đánh bóng code của mình (thêm nhiều tài liệu, tuân thủ đầy đủ các phương pháp hay nhất, thực hiện tối ưu hóa), bạn có nguy cơ mất tất cả khoản đầu tư đó khi cần thay đổi code. Thay vào đó, hãy làm cho các tính năng của bạn hoạt động ở mức tối thiểu và sau đó tiếp tục. Khi bạn tự tin vào giải pháp của mình, hãy thoa các lớp sơn bóng liên tiếp. Đừng nhắm đến mục tiêu hoàn hảo – các chương trình không tầm thường không bao giờ hoàn hảo và luôn có điều gì đó khác có thể được thực hiện để cải thiện chúng. Đến mức đủ tốt và tiếp tục.

Hầu hết các lập trình viên mới sẽ viết tắt nhiều bước và đề xuất này (bởi vì nó có vẻ như rất nhiều công việc và / hoặc nó không thú vị bằng viết code). Tuy nhiên, đối với bất kỳ dự án không tầm thường nào, việc làm theo các bước sau chắc chắn sẽ giúp bạn tiết kiệm rất nhiều thời gian về lâu dài. Lập kế hoạch trước một chút sẽ tiết kiệm rất nhiều việc gỡ lỗi ở cuối.

Tin tốt là một khi bạn cảm thấy thoải mái với tất cả những khái niệm này, chúng sẽ bắt đầu đến với bạn một cách tự nhiên hơn. Cuối cùng, bạn sẽ đạt đến điểm mà bạn có thể viết toàn bộ các hàm mà không cần lập kế hoạch trước.

Cài ứng dụng cafedev để dễ dàng cập nhật tin và học lập trình mọi lúc mọi nơi tại đây.

Nguồn và Tài liệu tiếng anh tham khảo:

Tài liệu từ cafedev:

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!