خوش آموز درخت تو گر بار دانش بگیرد، به زیر آوری چرخ نیلوفری را


آموزش زبان ++C : چگونه اولین برنامه های خود را طراحی کنید؟

آموزش زبان ++C : چگونه اولین برنامه های خود را طراحی کنید؟
نویسنده : امیر انصاری
حالا که برخی اصول اولیه در مورد برنامه ها را یاد گرفته اید، بیایید نگاه دقیقتری به چگونگی طراحی برنامه ها بیندازیم. وقتی که می نشینید تا یک برنامه را بنویسید، معمولاً نوعی مشکل دارید که باید حلش کنید، یا وضعیتی دارید که می خواهید آن را شبیه سازی کنید. برنامه نویسان جدید معمولاً در چگونگی تبدیل ایده ها به کد واقعی مشکل دارند. اما معلوم میشود که شما بسیاری از مهارتهای حل مشکلات را از زندگی روز مره دریافت می کنید.

سیستم یکپارچۀ سازمانی راهکار



مهمترین چیز برای یادآوری (و سخت ترین کاری که باید انجام بدهید) اینست که قبل از شروع به کد نویسی برنامه خود را طراحی کنید. در بسیاری از موارد، برنامه نویسی مانند معماری می باشد. اگر شما سعی کنید تا خانه ای را بدون پیروی از یک نقشه معماری بسازید، چه اتفاقی می افتد؟ اگر شانس بیاورید، مگر اینکه خیلی با استعداد باشید، شما به خانه ای می رسید که بسیاری مشکلات خواهد داشت : دیوارها راست نخواهند بود، سقف ضعیفی خواهد داشت و ... . به طور مشابه، اگر بخواهید برنامه ای را بدون طرح قبلی بنویسید، در نهایت به یک کد بسیار مشکل دار خواهید رسید، و مجبور خواهید بود تا زمان زیادی را صرف رفع مشکلات بکنید، مشکلاتی که تنها با اندکی فکر کردن پیشاپیش، ممکن بود هرگز پیش نیایند.

کمی برنامه ریزی پیشاپیش، می تواند در زمان شما صرفه جویی کند و همینطور از سرخوردگی و نا امیدی جلوگیری نماید.

مرحله 1 : مشکل را تعریف کنید (Define the problem)


اولین چیزی که باید بدانید اینست که چه مشکلی را قرار است برنامه شما حل کند. ایده آل آنست که بتوانید مشکل را در یک یا دو جمله بیان کنید. همچنین مفید است که این را به عنوان یک نتیجه گیری (آنچیزی که قرار است به آن برسید) مطرح کنید. برای مثال :

  • من دنبال راه بهتری برای پیگیری شماره تلفن های دوستانم هستم.
  • من می خواهم تا یکسری چاله های تصادفی تولید کنم تا در نهایت غارهای جالبی ایجاد گردند.
  • من می خواهم پیشنهادهایی برای اینکه کدام سهام ها را بخرم ایجاد کنم.
  • من می خواهم افتادن یک توپ از یک برج بلند را مدل سازی کنم.

اگر چه این مرحله به نظر واضح و بدیهی می رسد، اما به شدت حائز اهمیت می باشد. بدترین کاری که می توانید انجام بدهید، اینست که برنامه ای بنویسید که آن کاری را که شما دقیقاً می خواهید (یا کارفرمای شما می خواهد)، انجام نمی دهد!

مرحله 2 : جمع آوری نیازمندی ها (Collect requirements)


در حالیکه تعریف کردن مشکل به شما کمک می کند تا نتیجه نهایی را که مد نظرتان می باشد، تعیین نمایید، اما هنوز ابهاماتی دارد. مرحله بعد اینست که در مورد نیازمندیها فکر کنید.

نیازمندی ها، یک کلمه فانتزی است که هم به محدودیت هایی که راه حل شما باید در نظر داشته باشد، اشاره می کند (برای مثال بودجه، زمان، مکان، حافظه و ...)، و هم به توانایی هایی که برنامه باید برای کاربران فراهم آورد اشاره دارد. توجه داشته باشید که نیازمندی های شما باید بر روی "چه؟" تمرکز داشته باشد و نه روی "چگونه؟".

برای مثال :

  • شماره تلفن ها باید ذخیره شوند، بنابراین باید بتوانیم آنها را دوباره فراخوانی کنیم.
  • چاله های ما باید دارای مسیرهای ورودی و خروجی باشند.
  • توصیه های خرید سهام باید از داده های تاریخی قیمت های سهام تبعیت کنند.
  • کاربر باید بتواند ارتفاع برج را به عنوان یک پارامتر وارد کند.
  • ما یک نسخه قابل تست کردن را در طی 7 روز نیاز داریم.

یک مشکل ممکن است خودش نیازمندی های زیادی را به همراه بیاورد، و تا زمانی که تمامی آنها به نتیجه نرسند راه حل نهایی نشده است.

یک مجموعه خوب تعریف شده از مشکلات، که به یک مهندس شایسته داده می شود، به آن مهندس امکان می دهد تا برنامه ای را برای شما بسازد. اما این جالب نیست!

مرحله 3 : ابزارها، اهداف و برنامه های پشتیبان گیری خود را تعریف کنید


وقتی که شما یک برنامه نویس با تجربه باشید، موارد زیادی در میان می باشد که در این مرحله قرار می گیرند، شامل :

  • مشخص کنید که برنامه شما قرار است روی چه معماری یا چه سیستم عاملی باید اجرا شود.
  • تعیین کنید که از چه مجموعه ابزارهایی باید استفاده کنید.
  • تعیین کنید که آیا قرار است این برنامه را به تنهایی بنویسید، یا بخشی از یک تیم هستید.
  • استراتژی تست، بازخورد، و نسخه نهایی ارائه کردن را تعیین کنید.
  • تعیین کنید که روش تهیه نسخه پشتیبان از کدهای شما به چه شکلی می باشد.

با این حال، به عنوان یک برنامه نویس جدید، معمولاً پاسخ این سوالات ساده است : شما برنامه ای برای استفاده شخصی خودتان می نویسید، آن را روی کامپیوتر خودتان می نویسید، از IDE که دانلود کرده اید یا خریداری کرده اید، استفاده می کنید، و احتمالاً کدهای شما قرار نیست توسط شخصی غیر از خود شما مورد استفاده قرار بگیرد. این باعث می شود تا فعلاً همه چیز ساده باشد.

گفتیم که شما باید برنامه ای برای پشتیبان گیری از کدهایی که می نویسید، داشته باشید. این کافی نیست که یک کپی از آن را روی پوشه دیگری در کامپیوتر خود نگه دارید (البته از هیچ چی بهتر است). اگر سیستم شما کرش کند، شما همه چیز را از دست خواهید داد. یک استراتژی بک آپ گیری خوب اینست که همیشه یک نسخه کامل از کدهای خود را در جایی خارج از کامپیوتر نگهداری کنید. در این زمینه روش های زیادی وجود دارند که توصیه ما استفاده از سرویس های ابری برای تهیه پشتیبان از کدها می باشد.

مرحله 4 : مشکلات سخت را بشکنید و آنها را تبدیل به چندین مشکل کوچکتر کنید


در زندگی واقعی، معمولاً نیاز داریم تا کارهای خیلی پیچیده را انجام بدهیم. تلاش برای کشف اینکه چگونه باید آن کارها را انجام بدهیم، می تواند بسیار چالش برانگیز باشد. در چنین مواردی، ما معمولاً از متد بالا به پایین (top down method) برای حل مشکلات استفاده می کنیم. به این شکل که، به جای اینکه یک مشکل پیچیده را مستقیماً حل کنیم، آن مشکل بزرگ را به چندین مشکل کوچکتر می شکنیم، حالا حل کردن جداگانه هر کدام از این مشکل ها می تواند کار آسانتری باشد. اگر احیاناً یکی از آن زیر مشکلها، خودش پیچیدگی داشته باشد، می توانیم آن را هم با همین روش مجددا به چند مشکل کوچکتر بشکنیم. با تقسیم وظایف پیچیده به وظایف کوچکتر و شدنی تر، می توان در نهایت به کارهای قابل مدیریت و شدنی رسید.

بیایید به مثالی بپردازیم. فرض کنیم که می خواهیم گزارشی در مورد "هویج ها" بنویسیم. سلسله مراتب کاری ما در حال حاضر به این شکل می باشد :

  • گزارشی در مورد هویج ها بنویسید.

نوشتن گزارش در مورد هویج ها در یک جلسه کار سختی می باشد، برای همین آن را به چند کار کوچکتر می شکنیم :

  • گزارشی در مورد هویج ها بنویسید.
    • تحقیقی در مورد هویج ها انجام بدهید.
    • یک طرح کلی بنویسید.
    • طرح کلی را با جزئیاتی در مورد هویج ها تکمیل تر کنید.
    • جدولی برای محتویات اضافه کنید.

حالا کار ما قابل مدیریت تر است، چرا که وظایفی داریم که می توانیم روی هر کدام از آنها جداگانه تمرکز کنیم. با این حال، مرحله تحقیقات در مورد هویج ها هنوز دارای ابهاماتی است، پس آن را هم به وظایف کوچکتری می شکنیم:

  • گزارشی در مورد هویج ها بنویسید.
    • تحقیقی در مورد هویج ها انجام بدهید.
      • به کتابخانه بروید و کتابهایی در مورد هویج ها بگیرید.
      • در اینترنت به دنبال اطلاعاتی در مورد هویج ها بگردید.
      • در هر یادداشت برداری که انجام می دهید، منبع تحقیق را نیز ذکر کنید.
    • یک طرح کلی بنویسید.
      • اطلاعاتی در مورد رشد
      • اطلاعاتی در مورد پردازش
      • اطلاعاتی در مورد تغذیه
    • طرح کلی را با جزئیاتی در مورد هویج ها تکمیل تر کنید.
    • جدولی برای محتویات اضافه کنید.

حالا ما یک سلسله مراتب از کارها را داریم که هیچ کدام از آنها به تنهایی سخت و پیچیده نمی باشند. هر کدام از این مراحل ساده را که انجام بدهیم، در نهایت تکمیل آنها منجر به تکمیل کل سلسله مراتب کارها خواهد شد. در پایان گزارشی از هویج ها خواهیم داشت.

راه دیگر ایجاد یک سلسله مراتب وظایف اینست که از پایین به بالا انجام شوند. در این روش، ما از یک سری کارهای ساده شروع می کنیم و با گروه بندی آنها سلسله مراتب کارها را ایجاد می کنیم.

به عنوان یک مثال، بسیاری از مردم در ایام کاری هفته به محل کارشان یا به مدرسه شان می روند، فرض کنیم می خواهیم مشکلی با نام "از بستر خواب تا محل کار" را حل کنیم. اگر بخواهید کارهایی را که از زمان بیدار شدن از خواب در بسترتان انجام می دهید تا رفتن به محل کار را لیست کنید، ممکن است لیستی مشابه لیست زیر را داشته باشید :

  • لباس ها را انتخاب کن
  • لباس بپوش
  • صبحانه بخور
  • به سمت محل کار رانندگی کن
  • مسواک بزن
  • از تخت خوابت خارج شو
  • صبحانه را آماده کن
  • داخل ماشنیت شو
  • دوش بگیر

با استفاده از متد پایین به بالا (bottom up method)، می توانیم کارهای بالا را به شکل زیر سازماندهی کنیم، به این ترتیب که کارهای مشابه را در کنار یکدیگر دسته بندی کنیم :

  • از بستر خواب تا محل کار
    • کارهای در تختخواب
      • از تخت خوابت خارج شو
      • لباس ها را انتخاب کن
      • لباس بپوش
    • کارهای حمام
      • دوش بگیر
      • مسواک بزن
    • کارهای صبحانه
      • صبحانه را آماده کن
      • صبحانه را بخور
    • کارهای حمل و نقل
      • داخل ماشنیت شو
      • به سمت محل کار رانندگی کن

اگر بخواهیم برنامه یک ماشین حساب را بنویسیم، باید کارها را به ترتیب انجام بدهیم :

  • اولین عدد را از کاربر بگیر
  • عملیات ریاضی را از کاربر بگیر
  • عدد دوم را از کاربر بگیر
  • نتایج را محاسبه کن
  • نتایج را چاپ کن

در مورد برنامه "از بستر خواب تا محل کار" لیست توابعی که در تابع main ما نوشته خواهد شد، به شکل زیر خواهد بود :

int main()
{
getOutOfBed();
pickOutClothes();
takeAShower();
getDressed();
prepareBreakfast();
eatBreakfast();
brushTeeth();
getInCar();
driveToWork();
}

با در مورد برنامه ماشین حساب خواهیم داشت :

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

مرحله 6 : داده های ورودی و خروجی هر کدام از وظایف را بیابید


وقتی که سلسله مراتب کد خود و ترتیب رویدادها را تعیین کردید، مرحله بعد اینست که تعیین کنید هر کدام از این عملیات ها چه ورودی ها و یا چه خروجی هایی می توانند داشته باشند. داده های ورودی ها را به شکل پارامتر (parameter) در توابع تعیین نمایید. همچنین خروجی های توابع را به صورت مقدار بازگشتی (return value) در توابع تعیین نمایید.

وقتی کار ما تمام شود باید نمونه اولیه توابع (prototypes) را داشته باشیم. اگر فراموش کرده اید که prototypes چیست، برای یادآوری باید بگوییم که نمونه اولیه توابع (prototypes) شامل نام تابع، پارامترهای تابع و مقدار بازگشتی تابع می باشد، اما صرفا شامل معرفی اولیه تابع است و بدنه تابع در آن وجود ندارد.

بیایید چند نمونه را با هم انجام بدهیم تا درک بهتری از مساله پیدا کنید. تابع getUserInput قرار است تا مقداری را از کاربر بگیرد. ما می دانیم که این مقدار یک عدد می باشد. همچنین این تابع قرار است تا مقدار دریافتی را به تابعی که آن را فراخوانی کرده است باز گرداند. پس نمونه اولیه توابع (prototypes) به این شکل می باشد :

int getUserInput();

در مثال برنامه ماشین حساب، تابع calculateResult احتیاج دارد تا سه مقدار ورودی را دریافت کنید، دو عدد و یک علامت ریاضی که بر اساس آن محاسبات را انجام می دهد. این سه مقدار به عنوان پارامترهای این تابع تعیین خواهند شد. تابع calculateResult محاسبات را انجام می دهد، اما قرار نیست خودش مقداری را نمایش بدهد و مقدار محاسبه شده را به تابع فراخوانی کننده اش باز خواهد گرداند. در نتیجه، ما باید یک مقدار بازگشتی در این تابع تعیین کنیم که مقدار محاسبه شده را به عنوان بازگشتی به سایر توابع باز گرداند.

با توجه به مواردی که ذکر کردیم، نمونه اولیه توابع (prototypes) مربوطه به شکل زیر خواهد بود :

int calculateResult(int input1, int op, int input2);

مرحله 7 : جزئیات کارها را بنویسید


در این مرحله، برای هر وظیفه جزئیات آن را که در واقع پیاده سازی (implementation) می باشد، بنویسید. اگر وظایف را به درستی به قطعات کوچکتر تقسیم کرده باشید در این مرحله به اندازه کافی هر وظیفه ساده و سرراست خواهد بود. اگر احساس کردید که یکی از وظایف همچنان سخت است و قابل شکستن به وظایف ریزتری نیز می باشد، می توانید این کار را انجام بدهید تا پیاده سازی آن ساده تر گردد.

به مثال زیر که در آن پیاده سازی تابع getMathematicalOperation را نوشته ایم توجه کنید :

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;
}

مرحله 8 : وردی ها (inputs) و خروجی ها (outputs) را به هم متصل کنید.


مرحله آخر اینست که ورودی ها و خروجی های هر کدام از وظایف را به شکل شایسته ای به یکدیگر متصل سازید تا در نهایت برنامه به درستی کار کند. به عنوان مثال، شما ممکن است خروجی تابع calculateResult را به عنوان ورودی تابع printResult ارسال کنید تا در نتیجه آن پاسخ محاسبه شده چاپ گردد. این کار معمولا با استفاده از متغیرهایی به عنوان واسط (intermediary variables) صورت می پذیرد که مقادیر را به صورت موقتی در خودشان ذخیره می کنند. برای مثال :

// 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);

استفاده از متغیرهای واسط (intermediary variables) نسبت به استفاده مستقیم از توابع به عنوان ورودی ها و خروجی های یکدیگر، بهتر است. هم خوانایی برنامه را بالا می برد و هم اینکه اگر در جای دیگری در همان کد نیاز به ارجاع به نتایج آن محاسبه باشد نیاز به فراخوانی مجدد تابع مربوطه نخواهید داشت. اگر نمی خواستیم از متغیرهای واسط (intermediary variables) استفاده کنیم، برنامه ما به شکل زیر نوشته می شد :

printResult( calculateResult(input1, op, input2) );

برنامه ماشین حساب به صورت کامل


در ادامه برای درک بهتر مساله، برنامه ماشین حسابی را که در این درس اشاره کردیم به صورت کاملتر برایتان نوشته ایم. فقط توجه داشته باشید که ممکن است برخی از قسمتهای کد را تا اینجای دوره آموزشی اشاره نکرده باشیم که قطعا در درس های آینده به آنها خواهیم پرداخت، هدف ما از آوردن این برنامه در این مرحله، این بوده است که چارچوب کلی کار را متوجه گردید :

// #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 x + y; // if the user passed in an invalid op, we'll do addition.

// we discuss better error handling in future chapters
}

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);

return 0;
}

توصیه های مشاوره ای در هنگام نوشتن برنامه ها


  • شروع برنامه های خود را ساده کنید. حتی برنامه نویسان تازه کار هم معمولاً چشم انداز باشکوهی از آنچه که برنامه هایشان ممکن است انجام بدهد، داشته باشند. به عنوان مثال : "من می خواهیم یک بازی با هیولاهایی در آن که به صورت تصادفی در صفحه حرکتهای مختلفی انجام می دهند، همراه با گرافیک و صدا بنویسم و ..." . اگر بخواهید از ابتدای شروع کار اینگونه برنامه هایی را بنویسید ممکن است سرخورده و نا امید شوید. در عوض می توانید به مساله کوچکتر نگاه کنید و به مرور از مسائل کوچکتر به مسائل بزرگتر دست پیدا کنید. به عنوان مثال اولین هدف شما می تواند این باشد که می خواهم یک برنامه بنویسیم که یک شکل دو بعدی را روی صفحه نمایش بدهد.

  • ویژگی های بیشتر را به مرور زمان اضافه کنید. وقتیکه برنامه کوچک و ساده شما درست کار کرد و از کارکرد آن مطمئن شدید، می توانید ویژگی های دیگری را هم به آن اضافه کنید. به عنوان مثال بعد از اینکه توانستید یک شکل دو بعدی را روی صفحه نمایش بدهید می توانید کاری کنید که آن شکل روی صفحه حرکت کند. وقتی موفق شدید آن شکل را حرکت بدهید، می توانید امکانات دیگری را هم به همین شکل به برنامه تان اضافه کنید. طبیعتاً با افرودن هر ویژگی جدید برنامه شما پیچیده تر و بزرگتر می شود، و خواهید دید بدون اینکه شما دچار سرخوردگی و نا امیدی شوید، کار دارد پیش می رود.

  • در هر زمان روی یک ناحیه تمرکز کنید. سعی نکنید تا همه کدها را همزمان پیش ببرید، تمرکزتان را روی ناحیه خاصی بگذارید و کارها را تقسیم کنید. در هر زمان بر روی یک وظیفه خاص تمرکز کنید، و آن را تکمیل کنید. این روش خیلی بهتر از اینست که همزمان چند وظیفه را آغاز کنید. یک بخشی که به درستی و کامل کار می کند بهتر از چندین بخشی است که هنوز تکمیل نشده اند و یا حتی هنوز شروع نشده اند.

  • همچنان که پیش می روید، هر قطعه از کد را تست کنید. برنامه نویسان جدید معمولاً کل برنامه را در یک مرحله می نویسند. سپس هنگامی که آن را کامپایل می کنند کامپایلر دهها خطا می دهد. این مساله نه تنها ترسناک است، بلکه پیدا کردن دلیل خطاها نیز مشکل می شود. در عوض، می توانید قطعه ای از کد را بنویسید، آن را کامپایل و تست کنید. اگر هم در این وضعیت خطایی پیش بیاید، پیدا کردن مشکل و رفع آن قطعاً ساده تر می باشد. وقتیکه مطمئن شدید کد شما به درستی و بدون خطا کار می کند، آنوقت سراغ قطعه کد بعدی بروید و مجدداً این فرآیند را تکرار کنید. هر چند نوشتن برنامه ها به این شکل زمان بیشتری می برد، اما در عوض وقتیکه کارتان تمام شد، همه چیز به خوبی کار خواهد کرد و با خطاهای احتمالی بسیار کمتری مواجه خواهید شد، در مجموع در این روش زمان کد نویسی افزایش پیدا می کند، اما زمان تست و خطایابی قطعاً کاهش چشم گیری خواهد داشت.


آموزش قبلی : آموزش زبان ++C : گارد هدر (Header guards)

آموزش بعدی : آموزش زبان ++C : اشکال زدایی (Debugging) برنامه ها



نمایش دیدگاه ها (1 دیدگاه)

دیدگاه خود را ثبت کنید:

انتخاب تصویر ویرایش حذف
توجه! حداکثر حجم مجاز برای تصویر 500 کیلوبایت می باشد.