صفحه 1 از 2 12 آخرآخر
نمایش نتایج 1 تا 40 از 44

نام تاپیک: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

Hybrid View

پست قبلی پست قبلی   پست بعدی پست بعدی
  1. #1

    گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    سلام
    همونطور که آقای موسوی اعلام کردن ، اولین سری گفتگو های فنی رو با عنوان "اصول و قواعد کد نویسی" شروع می کنیم. میهمانان این گفتگو آقایان مداح و موسوی هستند. دوستانی که در مورد این بحث سوال، پیشنهاد یا انتقادی دارن می تونن در تاپیک "بحث دربارۀ گفتوی فنی شماره یک - اصول و قواعد کدنویسی" اون رو مطرح کنن (تاپیک فعلی فقط در دسترس مجری و مهمانان برنامه است)
    برای شروع از میهمانان برنامه تقاضا دارم خودشون رو معرفی کنن تا حاضرین بیشتر با اونها آشنا بشن. آقای مداح، بفرمایید...
    آخرین ویرایش به وسیله Mehdi Asgari : سه شنبه 11 خرداد 1389 در 18:12 عصر
    We work in the dark, we do what we can, we give what we have.
    Our doubt is our passion and our passion is our task.
    The rest is the madness of art

  2. #2

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    سلام خدمت دوستان،
    بنده علیرضا مداح هستم، توسعه گر، تحلیل گر و طراح سیستم، و هم اکنون عمده تمرکز بنده بر روی فناوری های ارائه شده از سوی مایکروسافت که "چارجوب کاری دات نت" محوریت اصلی آن را تشکیل می دهد، می باشد و عموما" به عنوان مشاور، توسعه گر نرم افزارهای مبتنی بر وب/ویندوز، محقق(گاها" در حوزه های دیگر IT) و در مواقعی هم به عنوان مدرس، مشغول فعالیت می باشم، تخقیق و پژوهش و در کنار آن کسب تجربه از اهمیت فوق العاده ای برای بنده برخوردار است،
    در طی این گفتگوها و در آینده بیشتر با هم آشنا خواهیم شد،
    امیدوارم که این سری گفتگوها برای دوستان مفید واقع گردد...
    خوب برای شروع کافیست...
    ،/
    آخرین ویرایش به وسیله علیرضا مداح : سه شنبه 11 خرداد 1389 در 19:05 عصر
    I've just started tweeting!
    @Alireza_Maddah

  3. #3

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    ممنون
    و شما آقای موسوی:
    ...
    We work in the dark, we do what we can, we give what we have.
    Our doubt is our passion and our passion is our task.
    The rest is the madness of art

  4. #4

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    سلام.
    مهدی موسوی هستم، طراح نرم افزار (بر اساس تکنولوژیهای مایکروسافت).
    علاقه وافری به C++‎‎ دارم و سالهای مدیدی از عمرم رو صرف نوشتن برنامه های متعدد و بعضا بزرگی با این زبان و تحت سیستم عامل ویندوز کردم.
    با ظهور .NET Framework به این Framework علاقمند شدم و به اون گرایش پیدا کردم.

    در حال حاضر هم در خدمت شما و سایر دوستان هستم.

  5. #5

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    خب ، خیلی ممنون از دو بزرگوار
    بریم سراغ سوال اول.
    اجازه بدید از صفر شروع کنیم. به نظر شما چه چیزهایی در برنامه نویسی "اصل" هستند؟ به عبارت دیگه چه فاکتور هایی در "اصولی" بودن یک برنامه (و روند ایجاد اون) موثر هستن و این که آیا استاندارد های تدوین شده ای برای این اصول وجود دارن ؟
    We work in the dark, we do what we can, we give what we have.
    Our doubt is our passion and our passion is our task.
    The rest is the madness of art

  6. #6

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    Robert C. Martin این سوال رو خیلی قشنگ پاسخ میده. اون معتقده که یکی از روش های تمیز دادن یک کد خوب از کد بد، معیاری به اسم WTF/Minute هستش. اجازه بدید قدری مساله رو روشن کنم. فرض کنید تعدادی مهندس دو کد رو در دو اتاق مختلف بررسی میکنن. یکی از کدها خوب نوشته شده و دیگری بد. ما هم بیرون اون اتاق ایستاده ایم و این صداها از اتاق به گوشمون میرسه:

    از اتاق اول:

    • این دیگه چه آشغالیه؟ (مهندس بعدی) اوه، این آشغالو نگاه کن.

    و از اتاق دوم:

    • این دیگه چه آشغالیه؟ (نفر بعدی) این دیگه چه آشغالیه؟ (نفر بعدی) اوه اوه! نگاه کن اینجا چه آت آشغالایی نوشته شده. (صدای بعدی) اوه! این دیگه چه آشغالیه.

    به بیان دیگه اون تکرار تعداد کلمات "آشغال" در "یک دقیقه" رو معیار خوب بودن یا بد بودن کد معرفی میکنه. هر چه این نرخ، کمتر باشه، کد کد بهتری هستش.

    بطور کلی، دو معیار برای نوشتن یک کد خوب (یا بقول شما اصولی) وجود داره: کار و دانش. شما باید دانش رو با بررسی بهترین الگوها کسب کنید، در این کار ممارست و ابتکار از خودتون به خرج بدید و دانش کسب شده رو در عمل بکار بگیرید.

    فرض کنید من میخوام به شما دوچرخه سواری یاد بدم. خوب، من میتونم قوانین فیزیک مرتبط با این مساله رو روی یک کاغد A4 برای شما بنویسم (بخش دانش)، شتاب، نیروی جاذبه، نیروی گریز از مرکز، اصطکاک و ... اما بازهم، وقتی شما برای اولین بار سوار دوچرخه بشید، زمین میخورید (کار).

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

    اما چه قواعدی در نوشتن یک کد خوب دخیل هستن؟ برای پاسخ به این سوال، من باید به تشریح مطالب زیر بپردازم:

    • استفاده از اسامی معنادار
    • سپردن هر Functionality به یک تابع خاص
    • Comment های نوشته شده در برنامه
    • Object ها و قوانین Data Structure
    • تعامل با خطاها
    • و ...

    اما قبل از اینکار، خوشحال میشم آقای مداح نیز نظرشون رو در این مورد بفرمایند.

  7. #7

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

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

    • "بدون فکر" شروع کنید و هرجا که لازم شد کدتان را اصلاح کنید،
    • ابتدا "به میزان کافی" در مورد کاری که می خواهید انجام دهید "فکر کنید"،

    در هر دو روش، پس از مدتی به مقصود خواهید رسید، اما در برنامه نویسی "چطور به هدف رسیدن" بسیار مهم است، کدنویسی یک فرآیند مستمر می باشد و برنامه در طول زمان تغییر می کند، افرادی که بر روی یک قطعه کد کار می کنند، جایگزین می شوند و این از خصوصیات یک قطعه کد است،
    اگر کار را بدون فکر شروع کنید، کد نوشته شده توسط شما بارها و بارها تغییر می کند و در آخر چیزی که برای شما باقی می ماند یک قطعه کد Messy و Dirty بوده که اگر چند ماه بعد به آن کد رجوع کنید، خودتان هم کد خودتان را درک نمی کنید! و گاهی شما را وادار می کند که کد را بازنویسی کنید، آن هم به کرات!
    قبل از کد نویسی باید از خود بپرسید که "هدف این قطعه کد چیست؟ این متد از چه ساب متد هایی نشکیل می شود؟ این کد را چطور بنوسم که قابل Test کردن باشد؟ و ..." و بعد برپایه ی اصول و قوائدی شروع به کدنویسی کنید،
    داسنتن اصول و قوائد کدنویسی و به خصوص "الگوهای طراحی(Design Patterns)" بسیار مهم است، اما خیالتان را راحت کنم، با داستن صرف این مطالب کسی برنامه نویس "خوب" نمی شود، برنامه نویس "خوب" شدن نیاز به استمرار و کسب تجربه دارد،
    شما هر زمان دیدید که یک عملیات خاص و مشابه در قسمت های مختلف برنامه "تکرار" می شود و برای هر "تغییر" مجبور به اصلاح قسمت های مختلف از برنامه هستید، از خود بپرسید که: "آیا امکان دارد که این عملیات ها در قالب یک متد قابل استفاده ی مجدد(Reusable) انجام شود؟"، چون یکی از اهداف یادگیری اصول و قوائد کدنویسی این است که از "تکرار بیهوده" پرهیز کنیم و در نهایت کدی داشته باشیم که ساده، خوانا، قابل استفاده ی مجدد و ... باشد،
    در ادامه قصد داریم تا بررسی کنیم که "چطور" کد "خوب" بنویسیم و اصلا" معیارهای یک کد "خوب" چیست؟
    (حتما" می پرسید چرا واژه "خوب" به دفعات در این پست تکرار شده است، در ادامه ی گفتگو به دلیل این موضوع پی خواهید برد)
    ...،/
    آخرین ویرایش به وسیله علیرضا مداح : سه شنبه 11 خرداد 1389 در 21:47 عصر
    I've just started tweeting!
    @Alireza_Maddah

  8. #8

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    بسیار خب
    بحثی که آقای مداح راه انداختن من رو یاد کتاب Coder To Developer انداخت که دقیقا تفاوت های "کدنویس" و "توسعه گر" رو مشخص می کنه. سوالی که برای من به وجود اومد ، اینه که خوندن کد دیگر برنامه نویسا چقدر می تونه در حرکت ما برنامه نویسا به سمت نوشتن "کد خوب" موثر باشه ؟
    از این بابت این سوال رو می پرسم که مثلا کتاب هایی مثل Beautiful Code رو دیدم. آیا مطالعۀ کد خوب نوشته شده توسط بزرگان تاثیری بر روی کد ما داره ؟ (مثلا اگه خودم رو جای یه نویسندۀ رمان های دلهره آور یا جنایی-درام بذارم ، نمی تونم تصور کنم که کتابای Stephen King یا آگاتا کریستی رو نخونده باشم. فکر می کنم که میشه این قیاس رو در مورد رشتۀ ما هم آورد)
    از آقای مداح خواهش می کنم جواب سوالات فوق رو بدن ، تا بعد برسیم به جواب آقای موسوی (البته ناگفته نماند که منتظر ادامۀ بحث قبلی هر دو بزرگوار هم هستیم)
    We work in the dark, we do what we can, we give what we have.
    Our doubt is our passion and our passion is our task.
    The rest is the madness of art

  9. #9

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

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

    ما توی کد با توابع، کلاسها، Property ها، Attribute ها، لایه ها و ... سر و کار داریم که باید برای هر یک از اونها نامی در نظر بگیریم. در نتیجه اسامی در جای جای کد ما قرار دارن و چون بیش از هر چیز دیگه ای در کد تکرار میشن، از اهمیت بخصوصی برخوردارن.

    لطفا این کدها رو نگاه کنید:


    private void button1_Click(object sender, EventArgs e)
    {
    ClearControls(textBox1, textBox2, comboBox1);
    }

    کد اول

    int x = (int)Days.Mon;

    کد دوم

    string text = "", source = "";
    int l = 0;

    using (FileStream FS = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.Read))
    {
    using (StreamReader SR = new StreamReader(FS))
    {
    while (text != null)
    {
    text = SR.ReadLine();
    if (text != null)
    {
    ++l;
    }
    }
    }
    }

    کد سوم

    (این سه تکه کد رو از بخش C#‎ برداشتم، و فقط تو یکی از اونها، نام متغیر رو تغییر دادم که بتونم ازش بعنوان مثال استفاده کنم).

    کد اول رو نگاه کنید. button1 یعنی چی؟ button2، button3 و ... این چه اطلاعاتی به ما میده؟ در حقیقت این اسامی باید مقصود استفاده از خودشون رو به خواننده کد برسونن. button1 به من هیچگونه اطلاعاتی نمیده! آیا این دکمه قراره رکوردی رو حذف کنه؟ یا رکوردی به بانک اضافه کنه؟ یا بخشی از صفحه رو Refresh کنه؟ همونطوریکه مشخصه، این اسم هیچگونه اطلاعاتی به ما نمیده.

    کد دوم رو نگاه کنید. اسم متغیر x انتخاب شده، در حالیکه قراره یک integer که معادل Days.Mon هستش رو در خودش نگه داره. در واقع، برنامه نویس هدفش این بوده که index روز دوشنبه رو در این متغیر نگهداری کنه، اما از اسم x برای این متغیر استفاده کرده. من وقتی بعنوان خواننده کد، کد رو میبینم، بدون بررسی جزییات نمیتونم به هدف استفاده از این متغیر پی ببرم.

    در کد سوم (که من در اون متغیر numberOfLines رو با l عوض کردم)، SR، FS و l اسامی ای هستن که در این Code Snippet استفاده شدن. کدامیک از این اسامی مقصود استفاده رو به ما نشون میدن؟ هیچکدوم (در مورد این کد بعدا بیشتر صحبت خواهم کرد).

    از همه بدتر، از حرف l که بسیار شبیه عدد 1 هستش استفاده شده. شما هرگز نباید از حرف l یا O استفاده کنید، چون با عدد صفر و یک اشتباه گرفته میشه و به سختی قابل تشخیصه.

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

    بسیار خوب. حالا که متوجه شدیم اسامی باید معنادار انتخاب بشن و مقصود استفاده رو برسونن، قدم بعدی اینه که نام انتخابی ما اطلاعات نادرست به خواننده کد نده. (دقت کنید. من مدام تکرار می کنم خواننده کد. اینو همواره به یاد داشته باشید که شما کد رو برای خودتون نمی نویسید، بلکه اونو برای دیگران می نویسید).

    فرض کنید آرایه ای از کلمات داریم. واژه wordsList اصلا واژه مناسبی برای ناگذاری این آرایه نیست چون باعث میشه خواننده کد، کلمه List رو ببینه و گمان کنه که این یک Linked List (یا انواع دیگه لیست) هستش. اگر Data Type مزبور واقعا یک لیست باشه، اونوقت اسم مزبور اسم خوبی برای اون لیست محسوب میشه.

    در مرحله بعد، نام انتخابی ما باید قابل جستجو باشه. حتما تا امروز، کدهای زیادی رو دیده اید که در اون اعداد 3.14، 7، 6، 12 و ... به ترتیب برای بیان عدد پی، تعداد روزهای هفته، نیمه اول سال و تعداد ماههای سال استفاده میشه. اونوقت برنامه نویس، هر جا رسیده عدد مورد نظر رو تکرار کرده و توی یک کلاس ساده، انبوهی از این اعداد رو در حلقه های تو در تو میبینید، که حقیقتا راهی برای تشخیص و یافتن اونها در کد (بدون مطالعه و تحلیل دقیق کد) وجود نداره.

    این روش کد نویسی خطرناک هستش و باید جدا از اون دوری بشه. در عوض، میتونید چند تا const تعریف کنید و هر یک از این مقادیر رو جداگانه یکبار در کد تعریف کنید، اونوقت هرجا نیاز بود از اون نام بهره ببرید.

    مساله بعدی Encode کردن اسامی هستش. سالها پیش، وقتی Chrales Simonyi مجارستانی (مجاری) که در اون زمان در تیم Word و Excel مایکروسافت مشغول به کار بود، در ورقهاش چیزی شبیه این جمله نوشته بود که Hungarian Naming (نامگذاری مجاری) به حروف مختصر اول موجود در نام متغیر اشاره میکنه که در حقیقت بیانگر Type اون متغیره. (این مساله رو از Joel Spolsky نقل میکنم و من خودم از این مساله تا 10-12 روز پیش خبر نداشتم).

    منظور Charles از Type، در واقع Kind یا همون "نوع" متغیر بوده. اما کسی توی مایکروسافت تصور کرد که منظور از Type در واقع Data Type اون فیلد هستش و Charles Petzold با انتشار کتابش به این مساله دامن زد و امروزه معنای Hungarian Naming Convention به کل متفاوت از اون چیزی هستش که Chrales Simonyi در ذهن داشته.

    اجازه بدید بخاطر اهمیت این موضوع اندکی در این مورد صحبت کنم (در صورت تمایل میتونید مقاله Joel در این مورد رو بخونید، اما اینجا من خلاصه ای از اون متن رو برای شما بازگو میکنم، تا اصل ماجرا و دلیل بوجود اومدن "نامگذاری مجاری" رو متوجه بشید).

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


    s = Request("name")

    ...pages later...
    name = s

    ...pages later...
    recordset("name") = name // store name in db in a column "name"

    ...days later...
    theName = recordset("name")

    ...pages or even months later...
    Write theName


    در ورودی، متغیری رو میگیریم، و با سپری شدن ایام، همینطور که کدمون رو تکمیل میکنیم، در نهایت اونو رو صفحه می نویسیم (خط آخر کد).

    این کد، مشکل ساز هستش و در برابر حملات XSS نفوذپذیر. چرا که من قبل از Encode کردن رشته وارد شده توسط کاربر اونو مستقیما دارم روی صفحه می نویسم. پس متغیر theName برای Render شدن به صفحه Client ایمن نیست. شاید هم باشه، شاید در بین خط اول و آخر، جایی در کد من اونو Encode کرده باشم و در نتیجه اجرای خط آخر کد، ایمن باشه. برای مطلع شدن از این مساله، باید کد مزبور رو حتما با دقت بررسی کنیم.

    Charles در اون زمان، برای اینکه بشه با یک نگاه به این مساله پی برد طرحی رو ارائه داد و در این طرح از برنامه نویسها خواست تا با آوردن حروف مختصری در ابتدای نام متغیر، نوع اون متغیر رو مشخص کنن (منظور Data Type نیست!). بطور نمونه در کد فوق، ما میتونستیم از دو حرف us به معنای Unsafe String (رشته ناامن) جای حرف s (خط اول کد فوق) سود ببریم. در نتیجه کدمون به این کد تبدیل میشد:


    us = Request("name")

    ...pages later...
    usName = us

    ...pages later...
    recordset("usName") = usName

    ...days later...
    sName = Encode(recordset("usName"))

    ...pages or even months later...
    Write sName


    همونطوریکه میبینید، با یک نگاه به خط آخر، متوجه میشیم که رشته نوشته شده Safe یا ایمن هستش (بخاطر وجود حرف s در ابتدای نام اون متغیر). در حقیقت ورودی us بود (ناایمن) و خروجی s (ایمن) پس این کد در برابر حملات XSS نفوذ ناپذیره.

    اما در حقیقت چه اتفاق افتاد؟ یکی دست نوشته های Charles رو پیدا کرد و فکر کرد منظور Charles از نوع، نوع داده ای متغیر بوده. بطور مثال اگر متغیری که قراره اونو تعریف کنیم، integer هستش، حرف اول رو n بذاریم، اگر قراره یه Null Terminated String رو نشون بدیم از دو حرف sz استفاده کنیم، برای نشون دادن Pointer ها از p استفاده کنیم، و الی آخر.

    اون روزها این نوع اسم گذاری (با توجه به ضعف IDE های موجود) کمک شایانی به برنامه نویس می کرد، اما خوب، درد سرهای خودش رو هم داشت. بعنوان مثال، برنامه نویس برای تعریف یک Null-Terminated String بمنظور نگهداری یه شماره تلفن اینطور عمل میکرد:

    TCHAR szPhone[12] = {NULL};


    اما بعدا در طول پروژه وقتی نیاز بود تا Data Type این متغیر رو تغییر بده، اونوقت باید اسم متغیر رو هم (علیرغم اینکه باز میخواست شماره تلفن رو نگه داره) تغییر میداد و این کار در IDE های اون زمان کابوس بود.

    در مورد نامگذاری مجاری در ادامه گفتگو بیشتر توضیح خواهم داد...

  10. #10

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    از آقای مداح خواهش می کنم جواب سوالات فوق رو بدن
    ببینید شما برای کسب تجربه در یک زمینه ی خاص، 2 انتخاب دارید:

    • شخصا" تجربه کنید
    • از تجربیات دیگر بزرگان استفاده کنید

    مسلما" بخش عظیمی از دانشی که ما به عنوان توسعه گر بدست می آوریم با نگاه به تجربیات دیگران است، البته دقت کنید تجربه ی شخصی که چندین و چندین سال در موارد گوناگون برنامه نویسی به دست می آورد با خواندن چند جمله از یک کتاب و در یک لحظه به شما منتقل نمی گردد، بلکه راه را برای فراگیری شما هموار می کند،
    بنده خواندن کتاب هایی همچون Beautiful Code را قطعا" پیشنهاد می کنم، اما توجه کنید، تنها "نگاه کردن" به کد نوشته ی شده ی یک شخص بزرگ کافی نیست، وقتی مشغول خواندن کتابی هستید و قطعه کدی در آن می بینید، معطل نکنید، همراه با نویسنده، "خط به خط" کدها را بنویسید، کامپایل کنید و نتیجه را "بررسی کنید" و بعد از اینکه "مفهوم" را درک کردید، سعی کنید بدون نگاه کردن به کتاب، آنچه از قطعه کد درک کرده اید را بنویسید، حتما" می پرسید چه کار سختی! بله، کسب علم و دانش و تجربه ی دیگران نیازمند "ممارست" و "تلاش" است، خواندن یک کتاب برنامه نویسی با خواندن رمان های وحشتناک تفاوت دارد، وقتی رمان می خوانید، همسوی با نویسنده غرق در داستان می شوید، اما وقتی کتاب برنامه نویسی می خوانید برای اینکه با نویسنده "همسو" شوید، باید سعی کنید "تفکرات" او را تا جایی که ممکن است برداشت کنید و "به عمل برسانید"، پس نتیجه می گیریم کتاب برنامه نویسی چیزی نیست که شما "در هنگام خواب" آن را مطالعه نمایید. کتاب را بردارید، کامپیوتر را روشن کنید، Visual Studio را باز کنید و قدم به قدم با نویسنده پیش بروید،
    در ضمن توجه کنید، کدی که در یک کتاب می بینید را تنها به عنوان یک "راهنما"، یک "ایده" و یک Guideline در نظر بگیرید، این بدان معنی نیست که اگر شخص بزرگی قطعه کدی را نوشت، شما می توانید در تمامی موقعیت ها از آن استفاده کنید، با نگاه کردن و نوشتن این کد ها، شما "درک" خود را افزایش می دهید، وقتی درک شما افزایش پیدا کرد، به مرور این توانایی را پیدا می کنید که کد خوب را از کد بد تمیز دهید، فراموش نکنید که "Practice Makes Perfect"،

    در ادامه راجع به دیگر مباحث توضیح خواهم داد...،/
    آخرین ویرایش به وسیله علیرضا مداح : چهارشنبه 12 خرداد 1389 در 08:16 صبح
    I've just started tweeting!
    @Alireza_Maddah

  11. #11

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    بسیار خوب. اولین گام برای نوشتن یک برنامه خوب، استفاده از اسامی معنادار هستش. در برنامه های C++‎، استفاده از نامگذاری مجاری، یا دقیقتر بخوام بگم، System Hungarian Notation (منظور استفاده از حروف اختصاری در ابتدای نام متغیر برای نشون دادن Data Type اون متغیر هستش) بسیار رواج داره. بعنوان نمونه،

    • حرف l نشاندهنده long integer
    • حروف sz نشون دهنده Null-Terminated String
    • حروف dw نشاندهنده یک DWORD
    • حرف f نشاندهنده یک flag (یا همون true، false)
    • حروف ch نشاندهنده یک Character
    • حرف p نشاندهنده Pointer و ...

    هستش. لیست مزبور رو میتونید تو کتاب Windows Programming نوشته Charles Petzold (که اونو پدر ویندوز میدونن) پیدا کنید.

    اما Apps Hungarian Notation در واقع به گونه، یا بهتره بگم هدف یک متغیر اشاره میکنه و نه Data Type اون. در نسخه اصلی Simonyi این حروف ذکر شده بود:

    • p برای pointer ها
    • d برای نمایش تفاوت، بعنوان نمونه dx یعنی تفاوت x2 و x1 روی محور x ها
    • rw نشاندهنده یک row
    • us نمایانگر Unsafe String
    • str نمایانگر string
    • w به معنای word
    • b به معنای byte

    اما با گذشت زمان و پیشرفت چشمگیری که در IDE ها رخ داد، استفاده از این علامات رمزی در زبانهایی مثل C#‎ و Java دیگه رایج نیست. همه این مطالب رو گفتم، تا نکته بعدی رو در اسم گذاری متغیرها عنوان کنم: اسامی Encode شده استفاده نکنید.

    این بدین معناست که دیگه امروزه نیازی نیست تا Member Variable های یک کلاس رو با m_ شروع کنید. یا برخی این اسامی رو با _ شروع میکنن. این گونه اسم گذاری دیگه امروزه به ما هیچگونه کمکی نمیکنه و بیشتر دست و پامون رو میبنده. همینطور دیگه امروزه نام کلاسها با حرف C شروع نمیشه، چون اونم کمکی به ما نمیکنه.

    گفتم کلاس، اجازه بدید این نکته رو عنوان کنم که برای نامگذاری کلاسها باید "اسم" بکار برد و نه "فعل". بعنوان نمونه Customer نام خوبی برای نشون دادن یک مشتری هستش.

    مساله بعدی که خودم بارها در کارم بهش برخوردم، استفاده از اسامی برای "جذابیت" هستش. کسی کد شما رو برای جذابیت اسامی مورد استفاده در کد شما نمیخونه، و اینگونه اسامی هیچ کمکی به خواننده کد نمیکنه.

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

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

    مساله بعدی در نامگذاری متغیرها، توجه به Context هاست. فرض کنید کلاسی داریم به اسم Customer و در اون FirstName، LastName و ... رو نگهداری میکنیم. اینجا FirstName با توجه به Context کلاس Customer در ذهن ما پردازش میشه، در نتیجه استفاده از CustomerFirstName جای FirstName هرگز کار شایسته ای نیست و باعث خستگی خواننده کد میشه. در واقع، هر اسمی رو با توجه به Context استفاده از اون اسم باید انتخاب کرد.

    بنابراین اگر بطور مثال روی پروژه SMS کار می کنید، از آوردن کلمه SMS در ابتدای نام متغیرها، کلاسها و ... جدا پرهیز کنید. چون اینگونه اسامی هیچ اطلاعاتی افزون بر داشته هامون به ما (یا خواننده کد) نمیده و باعث بغرنج تر شدن اوضاع میشه.

    از آقای مداح خواهش میکنم تا در مورد Camel Case، Pascal Case و ... بمنظور نحوه نگارش اسامی نیز توضیحاتی ارائه بدن.

  12. #12

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    خوب،
    به زبان خیلی ساده بیان می کنم،
    Pascal Casing به شیوه ی نگارشی گفته می شود که بنا بر آن، هنگام نامگذاری شناسه ها، حروف ابتدایی هر کلمه با حروف بزرگ مشخص می شوند، به طور مثال به شناسه های زیر در دات نت توجه کنید:


    • SqlDataAdapter(Class)
    • DialogResult(Enum type)
    • YesNo(Enum value)
    • GotFocus(Event)
    • ...



    شیوه ی Camel Casing هم مانند این شیوه بوده و تنها فرق آن این است هر شناسه با حروف کوچک شروع می شود،
    اگر کمی با .Net Framework Class Library آشنا باشید، متوجه شده اید که اعضای public به شیوه ی Pascal Casing نامگذاری شده اند و اهمیت این موضوع به قدری است که در Visual Studio 2010، شیوه ی کار Intellisense تغییر کرده و هم اکنون بر اساس Regular Expressions فعالیت می کند و قابلیت یافتن اعضا با دانستن فرم Pascal آن را فراهم می نماید، فرض کنید شما می خواهید یک نمونه از کلاس RunWorkerCompletedEventArgs ایجاد نمایید، خوب تنها با تایپ عبارت RWCEA که متشکل از حروف ابتدایی هر کلمه می باشد، به راحتی این کلاس را خواهید یافت(توجه کنید که در این مثال فضای نام System.ComponentModel را using کرده باشید)،
    با استناد به این لینک، تمامی اعضاء(به غیر از Instance Fieldها)، نوع ها و فضای نام ها که public هستند، باید به شیوه ی Pascal Casing نامگذاری شوند،
    گاهی ممکن است که شناسه ی شما از دو حرف یا یک حرف تشکیل شده باشد و public هم باشد، در این زمان می توانید از شیوه ی upper-case برای نامگذاری بهره بگیرید،
    به طور مثال در پروژه ای که بنده در در زمان نوشتن این مطلب بر روی آن کار می کنم، دارای یک فضای نام دو حرفی هستم که نشان دهنده لایه ی User Interface برنامه هست و آن را UI نامگذاری کردiه ام،
    همانطوری که آقای موسوی هم اشاره کردند، امروزه برای نامگذاری متغیرها از پیشوندهایی مانند _ استفاده نمی شود، هنگامی که می خواهید یک نام برای پارامتر انتخاب کنید از شیوه ی Camel Casing بهره بگیرید و هیچ گاه هم کلمات یک شناسه را با این نوع کاراکترها از هم جدا نکنید و به جای آن حرف ابتدای هر کلمه را با حروف بزرگ آغاز کنید(به غیر از کلمه اول)،
    برای کسب اطلاعات بیشتر پیشنهاد می کنم به Guidelines for Names مراجعه نمایید،
    مورد بعدی که باز در لینک فوق به آن اشاره شده است، چگونگی استفاده از Acronym ها می باشد، هر Acronym در صورتیکه دقیقا" از دو حرف تشکیل شده باشد یک Acronym کوتاه محسوب می شود و باید هر دو حرف از حروف بزرگ باشند، البته به غیر از کلمه ی اول در شیوه ی نگارش Camel Casing، حال اگر Acronym بیش از دو حرف باشد، تنها حرف اول آن، بزرگ نوشته می شود، حتما" با کلاس HtmlElement در دات نت آشنا هستید، HTML یک Acronym محسوب می شود، اما چون بیش از 2 حرف است، یک Acronym طولانی محسوب شده و در نتیجه تنها حرف ابتدایی آن بزرگ می باشد، حالا همین کلاس را اگر بخواهیم به شیوی Camel-Casing نامگذاری کنیم به صورت htmlElement خواهد بود،...
    منتظر مطالب بعدی باشید...،/
    I've just started tweeting!
    @Alireza_Maddah

  13. #13

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    در بحث آقای موسوی ، به IDE هم اشاره شده بود. ابتدا خواهش می کنم در مورد Refactoring توضیحی بدید و بعد ، این که کلا ظهور IDE های پیشرفته و مدرنی مثل Visual Studio با قابلیت هایی مثل Refactoring چقدر در تغییر سلیقه و عادت برنامه نویسا در نام گذاری ، تاثیر داشته.
    سوال دیگری که برای خواننده ها ممکنه به وجود بیاد ، اینه که باید در چه کدی و در چه شرایطی از انواع روش های نامگذاری استفاده کرد ؟ (مثلا مایکروسافت معمولا نام گذاری مجاری رو پیشنهاد و استفاده می کرد؛ در دات نت رو آورد به Pascal Case؛ جاوا از camelCase استفاده می کنه.)
    آیا واقعا تفاوتی بین روش های نام گذاری هست یا همین روح نام گذاری ثابت و منظمه که در کد های ما تاثیر می ذاره ؟ (و ربطی به روش استفاده شده نداره ؟)
    We work in the dark, we do what we can, we give what we have.
    Our doubt is our passion and our passion is our task.
    The rest is the madness of art

  14. #14

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    با تشکر از آقای مداح، فقط این نکته رو به صحبتهای ایشون اضافه کنم که در هر Context ای باید قوانین حاکم بر اون Context رو مد نظر قرار بدیم. به بیان دیگه، وقتی در JavaScript کد می نویسیم، طبیعتا باید از قوانین حاکم بر نامگذاری موجود در اون Context پیروی کنیم. من بعضا برنامه نویسهای خوبی رو می بینم، که توان جداسازی Context کاری که انجام میدن رو ندارن.

    اجازه بدید تا مجددا مثالی از برنامه های تحت وب بزنم. همونطور که مطلعید، برنامه های تحت وب به دو بخش Client-Side و Server-Side تقسیم میشن. با فرض به اینکه کدهای Client-Side شما به JavaScript نوشته شده باشه و کدهای سمت سرور با C#‎‎‎ نوشته شده باشن، باید این دو رو از هم تمیز بدید و در هر یک از این دو محیط قوانین حاکم بر اون محیط رو در نظر بگیرید. توی JavaScript عموما برنامه نویسها علاقمند هستن تا {} ها رو در انتها، یا ابتدای همون خطی که کد رو می نویسن قرار بدن، اما در C#‎‎‎، شروع یک Block (یا خاتمه اونو) در یک خط مجزا قرار میدن. شما هرگز نباید قوانین حاکم بر دو محیط مجرا رو با یکدیگر ترکیب کنید (حتی در یک پروژه).

    بسیار خوب. حالا برگردیم سراغ سوال آقای عسگری. Refactoring چیه؟

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

    کد خوب، کدی هستش که در فرآیند توسعه مدام مورد بازبینی قرار بگیره و حتی الامکان خوندن کد مزبور "احساس بد بودن" رو در خواننده کد بوجود نیاره. من میگم احساس بد، اما Martin Fowler و Kent Beck در کتاب Refactoring: Improving the Design of Existing Code از این احساس بد با اسم کد بد بو یاد کردن. از دید اونها، کدی که بوی بدی میده، نیاز به Refactoring داره (حتما بهتون توصیه میکنم این کتاب رو بخونید).

    اجازه بدید خیلی گذرا، من مطالب مهم ذکر شده در کتاب فوق رو برای چگونگی تشخیص کدی هایی که نیاز به Refactoring داره، عنوان کنم:

    • کد تکراری. این کد تکراری میتونه به دلیل Copy/Paste شدن یک تابع بوجود اومده باشه، یا استفاده از یک رشته مشخص در بیش از یک (یا بهتره بگم دو) نقطه از برنامه. برای رفع اون کافیه تا متغیری رو در جایی مشخص تعریف کنید و از این تعریف در نقاط دیگه سود ببرید.


    • متودهای طولانی. هنگامیکه یک متود طولانی میشه، صدای بررسی کننده کد در میاد و فریاد میزنه: "این دیگه چه آشغالیه!؟" (قانون WTF/Minute که در بخش ابتدایی صحبتهام بهش اشاره کردم). اولین قانون مرتبط با نوشتن یک تابع خوب این هستش که تابع نوشته شده باید کوچک باشه. دومین قانون چی میگه؟ میگه از اون هم باید کوچکتر باشه. من کدهای زیادی رو در دوران کاری خودم مرور کرده ام که برخی از اونها واقعا بوی بد میدادن. تصور کنید سه حلقه تو در تو، که سه الی چهار وجب کد داخل درونی ترین حلقه نوشته شده، اسامی متغیرهای حلقه i، j و k بوده و ... دقیقا اولین حرفی که من هنگام دیدن چنین کدی زدم این بود: "این دیگه چه آشغالیه؟" متاسفانه یکی از بدترین انتخاب های مایکروسافت، معرفی region directive توی .NET بود. این directive باعث شد تا برنامه نویسها انبوهی از کدهای Boilerplate رو توی یک Region Block قرار بدن و با امکانات IDE، اونها رو از دید خودشون (و دیگران) مخفی کنن. بدین ترتیب، ممکن بود، هزاران خط کد، در یک ناحیه قرار بگیرن و خواننده کد هیچ ایده ای نداشته باشه که توی اون ناحیه از کد، چه میگذره. البته خوشبختانه مایکروسافت به این مساله سریع پی برد و استفاده از این directive رو منع کرد. در اکثر مواقع، استفاده از region directive به معنای نادیده گرفتن SoC یا Separation of Concerns هستش.


    • کلاسهای طولانی. وقتی یک کلاس میخواد تمام مشکلات عالم رو حل کنه، طبیعتا باید member variable های زیادی در خودش encapsulate کنه و این، کد شما رو برای تکراری بودن کاندید میکنه (شرط اول بد بو بودن کد). کلاسها باید حتی الامکان کوچک باشن. حتما واژه SRP یا Single Responsibility Principle قبلا به گوشتون خورده. SRP چی میگه؟ میگه هر کلاس، فقط باید یک وظیفه داشته باشه و برای تغییرات آتی برنامه ریزی شده باشه. حتما دیده اید برنامه نویسانی رو که وقتی ازشون میخواهیم فلان رفتار کلاس رو تغییر بدن، دو دستی توی سر خودشون میزنن و میگن "اینطوری باید 4 تا کلاس دیگه رو هم تغییر بدم". حتما این مساله رو مد نظر داشته باشید که هر چه کلاس سبکتر باشه و از ابتدا برای تغییرات آتی برنامه ریزی شده باشه، نگهداری کد ساده تر هستش.


    • Formal Parameter های متعدد. حتما دیده اید برخی از متودهایی که تعداد پارامترهای اونها اینقدر زیاده که از عرض صفحه نمایش بیشتر میشه. حتی الامکان باید از چنین شرایطی پرهیز کرد، اما گاهی اوقات باید تعداد پارامترهای زیادی رو به یک متود ارسال کنید و این اجتناب ناپذیره. تو این شرایط، می تونید کلاس جدیدی بسازید و پارامترهای مزبور رو در اون کلاس قرار بدید، و تنها با یک پارامتر Object مورد نظر رو به متود مربوطه ارسال کنید.


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


    • Shotgun Surgery. هر وقت برای اعمال یک تغییر، مجبور شدید تغییرات گسترده و کوچکی در سطح سیستم نرم افزاری خودتون بدید، باید بوی بد کدتون شما رو ناراحت کنه! چرا که اولا ممکنه یکی از بخشها رو از قلم بندازید، ثانیا پیدا کردن این بخشها بسیار دشواره. Divergent Change (تغییر واگرا) یعنی یک کلاس از تغییرات زیادی رنج میبره و Shotgun Surgery یعنی یک تغییر، باعث دستکاری تعداد زیادی کلاس میشه. در هر دو حالت، بهترین عملکرد اینه که برای هر تغییر، با یک کلاس مواجه باشید، و تغییر در هر کلاس، منجر به تغییر در یک رفتار بشه.


    • Feature Envy. شرایطی که در اون یک کلاس، خروارها متود از یک Object دیگه رو فراخوانی میکنه تا به یک خروجی برسه. خوب، مشخصه که این متودها جایگاهشون اونجایی نیستش که الان هست و باید جایگاه اونها تغییر پیدا کنه. به بیان دیگه، در Object دیگه، باید متودی تعریف بشه که وظیفه مربوطه به دوش اون متود گذاشته بشه.


    • خوشه داده ها. داده ها، مثل بچه ها هستن و از اینکه در کنار همدیگه باشن، لذت میبرن. اغلب توابعی رو میبینیم که داده های یکسانی رو در ورودی خودشون میگیرن. این توابع بوی بد میدن. باید این داده ها رو بصورت خوشه در یک کلاس قرار داد و به چشم یک Object به اونها نگریست.


    • switch ها. در بسیاری از کدها میبینیم که اضافه کردن یک آیتم جدید به برنامه، باعث میشه تا چند switch در نقاط مختلف برنامه دستکاری بشه. چنین کدی، باید شما رو نگران کنه. Polymorphism در object oriented راه حل مناسبی برای حل این مشکل هستش.


    • Parallel Inheritance Hierarchies. این مورد هم مثل Shotgun Surgery میمونه، با این تفاوت که Subclass کردن یک کلاس (یعنی مشتق کردن یک کلاس جدید از کلاس دیگه) باعث میشه تا مجبور بشید کلاسهای دیگه ای رو نیز subclass کنید.


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


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


    • Refused Bequest. گاهی اوقات کلاسهایی رو از کلاسهای دیگه مشتق میکنیم، اما تو کلاس جدید، نیازی به برخی از متودها و ... به ارث رسیده شده نداریم. چنین شرایطی اغلب نشون میده که سلسله مراتب Derive کردن کلاسها بدرستی رعابت نشده و باید تغییر کنه.


    • Comment. هر وقت احساس کردید که کدتون نیاز به نوشتن Comment داره، اول به این فکر کنید که کدتون رو چطوری میتونید Refactor کنید تا خود کد گویای مطلبی که شما میخواهید اونو در Comment بنویسید باشه. البته این به این معنا نیستش که شما نباید کدهاتون رو Comment کنید. در ادامه گفتگو در این مورد توضیح خواهم...

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

  15. #15

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    بسیار ممنون
    پست جامع و آموزنده ای بود.
    اجازه بدید قبل از ادامۀ بحث ، روی موارد مطرح شده توسط شما بیشتر بحث کنیم. شما دو نفر (همون طور که خودتون هم در ابتدای بحث اشاره کردید) بیشتر در دنیاهای مدیریت/طراحی/تحلیل و نیز مهندسی/شی گرایی کار کردید (عجب دنیاهایی!) ؛ در نقطۀ مقابل شما من خودم رو یک برنامه نویس پرگماتیک (یا هکر) می دونم تا یک مهندس (به دلیل نوع خاص کارم. در واقع خیلی از اوقات انجام دادن کار برای من خیلی مهمه. من احساس می کنم این درجه از رسمیت برای کد های من و امثال من خیلی سنگین و کشنده است. بذارید دو نمونه ذکر کنم:
    1- نوشتن پروتوتایپ برنامه ها توسط زبان هایی مثل پایتون (نیاز مند انجام در کم ترین زمان و هزینۀ ممکن) (همینطور کد ها و اسکریپت های دور انداختنی که معمولا برای انجام یک تسک خاص یا استفاده در مدت کوتاهی نوشته میشن)
    2- نوشتن کد های امنیتی و سیستمی که در اون ها کوچک و سریع بودن (و در بعضی مواقع کثیف بودن!) مزیت محسوب میشه (مسلما در این موارد من به انجام سریع تر کار و درست کار کردن اون فکر می کنم تا تمیز بودن و درست بودن طراحیش از نظر مهندسی نرم افزار)
    سوالم اینه که اون مرزی که مشخص می کنه باید از متد های رسمی برای ایجاد نرم افزار یا refactoring یا نام گذاری یا طراحی تمیز ... استفاده کرد کجاست ؟ آیا شما برای نوشتن برنامه ای که نوشتنش یک هفته طول می کشه باز هم همین موارد و گام ها رو طی می کنید ؟
    آیا می تونید مواردی رو ذکر کنید که نیازی به استفاده از متدهای رسمی نیست ؟
    شما جواب این سوالات رو بدید تا بعد نظر آقای مداح رو هم در این موارد بدونیم (بعدش می ریم سراغ ادامۀ بحثمون)
    We work in the dark, we do what we can, we give what we have.
    Our doubt is our passion and our passion is our task.
    The rest is the madness of art

  16. #16

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    "آیا الان فلانی، توی فلان شرکت معروف دنیا، داره به همین روش من کد می نویسه؟"
    جمله ی زیبایی بود، این جمله همیشه هنگام کدنویسی در ذهن من رژه می رود و وادارم می کند که "یک بار دیگر" کدی که نوشتم را مرور کنم و از جنبه های مختلف آن را بررسی کنم،

    آیا شما برای نوشتن برنامه ای که نوشتنش یک هفته طول می کشه باز هم همین موارد و گام ها رو طی می کنید ؟
    ببینید وقتی شما "عادت" به "کد خوب" نوشتن کنید، نمی توانید به خودتان "اجازه" نوشتن کد بد را بدهید، عادت کردن به نوشتن کد خوب در واقع ذهن شما را "تربیت" می کند، اما باید حقیقت را پذیرفت، مسلما" پروژه ای که باید در یک هفته تحویل داده شود را نمی توان با به کارگیری تمامی مسائل ذکر شده به مرحله ی اجرا رساند، گاهی شما "مجبور" می شوید که کدی را بنویسید که یک مشکل را به طور موقت برای یک شرکت حل کند، یک کارخانه بزرگ را در نظر بگیرید که با چند صد کارمند از برنامه نوشته شده توسط شرکت شما بهره می گیرد و آنقدر فعالیت های این کارخانه به نرم افزار شما وابسته است که down بودن حتی چند ساعت نرم افزار هم می تواند صدماتی را به کارخانه وارد کند، در چنین محیطی یک مشکل اساسی در نرم افزار بروز می کند و شما "مجبور" هستید که در "کمترین زمان ممکن" مشکل را برطرف کنید، اینجا عامل "زمان" بر شما پیروز شده است، اما نه به طور کامل! شما می توانید با هر روشی مشکل را به طور موقتی برطرف کنید و سپس با گذاشتن وقت کافی بر روی موضوع به بررسی آن بپردازید و قسمت درونی کد خود را هم بهبود بخشید و در نگارش بعد آن را در محل مشتری اعمال نمایید، پس نتیجه می گیریم که گاهی فاکتورهای دیگری بر کار ما تاثیرگذار هستند و ممکن است این امکان برای ما در هر زمان وجود نداشته باشد که کار را هر طور که به میل خودمان است، انجام دهیم،
    در ضمن نکته ی بسیار مهمی هم که وجود دارد این است که در هیچ کاری نباید "افراط" کنید، رعایت نکاتی که در این بحث به آن ها اشاره می شود برای هر برنامه نویسی لازم است، اما به قدر ضرورت و بنا بر موقعیت و نوع پروژه. به طور مثال میزان جداسازی، Abstraction Level، Security Level و ... در همه ی پروژه ها یکسان نیست و باید بنا بر سناریوی مورد نظر گام بردارید،/
    I've just started tweeting!
    @Alireza_Maddah

  17. #17

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

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

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

    سؤال دوم من در مورد متغیر شمارنده در حلقه ها هست. آقای موسوی از انتخاب نام هایی مثل i و j و k و ... به عنوان ویژگی های یک کد کثیف یاد کردند (البته به نقل از یک کتاب). اگر اینطور هست، پس اصلاً چطور شد که i و j و ... برای همه برنامه نویسان همیشه یادآور متغیر شمارنده هست و چرا اینقدر شیوع پیدا کرده؟
    و ztx4
    فکر نمی کنید نوشتن کد به روشی که شما می فرمایید کمی سخت و خسته کننده باشه؟
    مثلا در حلقه ی for به جای استفاده از i,j,k از متغیر های ColomNum,RowNom ئ امثال اینها که خیلی طولانی هستند استفاده کنیم!
    آیا این روش سخت تر نیست؟
    آیا شما به عنوان یک برنامه نویس با تجربه می تونید بگید که بعد از مدتی به این سختی عادت میکنیم؟
    We work in the dark, we do what we can, we give what we have.
    Our doubt is our passion and our passion is our task.
    The rest is the madness of art

  18. #18

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    چند نکته را در مورد این سوالات و البته در تایید گفته های آقای موسوی به صورت کلی عرض می کنم:
    ببینید اصولی کد نوشتن و رعایت قوائد ممکن است در اوایل کار خسته کننده به نظر برسد، ولی وقتی که یاد گرفتید چطور اصولی کار کنید، اصلا" تصور بد کد نوشتن هم به ذهنتان خطور نمی کند و کد بد نوشتن برای شما بسیار سخت خواهد بود و نوشتن کد تمیز دیگر برای شما یک "اصل" خواهد بود که ذهن شما آن را پذیرفته است، وقتی می خواهید متغیرها را نام گذاری کنید، اسامی مناسبی انتخاب می کنید، وقتی کد بدی می بینید ناخوداگاه این جمله در ذهن شما می آید که: "That's Rubbish" و در زمانی که به این حد از پختگی برسید، اتفاقا" تشخیص کدهای بد بو برای شما بسیار آسان خواهد شد، چون یکسری قوانین در ذهن شما شکل گرفته است و گاها" بدون آنکه متوجه شوید هنگام کد نویسی آن قوانین در کد شما Enforce می شوند، باید سعی کنید به "درک" برسید، حاضرم این جمله را N بار تکرار کنم که برای برنامه نویس خوب شدن، کد نویس خوب شدن، توسعه گر خوب شدن، باید "درک" خود را بالا ببرید و این چیزی نیست که یک شبه به دست بیاید و کار و تلاش و علم آموزی می طلبد،
    از کاربران هم به خاطر اینکه ما را در این بحث همراهی می کنند واقعا" سپاسگزارم،/
    I've just started tweeting!
    @Alireza_Maddah

  19. #19

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    من یک مبحث هم مد نظرم هست برای این گفتگو: مستند سازی کد
    منتها قبل از ورود به این مبحث ، شما اگه حرفی در ادامۀ مبحث نام گذاری و کد اصولی و Refactoring دارید ، بگید تا اون مبحث رو تموم کرده و سراغ بحث جدید بریم.
    We work in the dark, we do what we can, we give what we have.
    Our doubt is our passion and our passion is our task.
    The rest is the madness of art

  20. #20

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

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

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


    • یکی از مسائلی که در صحبتهای قبلی به اون اشاره کردم، نیاز به تقسیم کدهای طولانی به بخشهای کوچکتر قابل مدیریت بودش. این اولین و مهمترین کاری هستش که باید در Refactoring بهش توجه کنید و اونو تحت عنوان Extract Methods می شناسن. در Extract Method، کد مورد نظر از جایگاه فعلی حذف و به یک تابع جدید منتقل شده و تابع جدید در جای قبلی کد، فراخوانی میشه. طبیعی هستش که بر اساس نیاز، ممکنه Formal Parameter هایی برای تابع جدید در نظر بگیریم، یا به یک Return Value تو Signature اون متود نیاز داشته باشیم. این مساله ارتباط مستقیم به کدی که در دست داریم داره.


    • برخی اوقات، ما توابع inline ای داریم، که حقیقتا نیازی به استفاده از اونها نیست و میشه اونها رو حذف کرد. تابع inline در C++‎ تابعی هستش که کد نوشته شده در این تابع، مستقیما در جاییکه تابع فراخوانی شده قرار میگیره (توسط کامپایلر) تا سرعت اجرای کد افزایش پیدا کنه. البته این مساله باعث افزایش سایز برنامه نیز میشه و باید هنگام لزوم و بدقت از اون استفاده بشه. این توضیحات رو دادم تا به روش بعدی Refactoring تحت عنوان Inline Method اشاره کنم. Inline Method دقیقا عمل عکس Extract Method هستش، به این بیان که ما یک تابع کوچک (عموما یک خطی) رو حذف میکنیم، و Body تابع رو جایی که تابع رو فراخوانی کرده ایم قرار میدیم(همون کاری که کامپایلر با کدهای Inline در C++‎ انجام میده). اما این سوال پیش میاد که چطوری متوجه بشیم که کی باید اینکارو کنیم؟ یکی از جاهایی که Inline Method زیاد استفاده میشه، کدهایی هستش که در اون یک تابع، تابع دیگه ای رو فراخوانی میکنه و اون تابع نیز به نوبه خودش، تابع دیگه ای رو فراخوانی کرده و این زنجیره چند بار تکرار شده. روش Inline Method باید روی چنین کدی اعمال بشه. در حقیقت، باید این Indirection ها با دقت بررسی بشن، اونهایی که قابل حذف هستن حذف شده و کد مورد نظر مستقیما فراخوانی بشه.


    • فرض کنید که در ابتدای تابعی، متغیری رو به Return Value تابع دیگه ای Assign کرده اید اما از این متغیر، تنها در یک نقطه از تابع استفاده می کنید و نه بیشتر! اینجا میتونید فراخوانی تابع رو به نقطه ای که از اون متغیر استفاده کرده اید منتقل کنید، و متغیر مزبور رو از بین ببرید (البته عموما اینکارو هنگامی انجام میدن که این متغیر موقت دست و پای ما رو برای اعمال روشهای دیگه Refactoring بسته باشه). به این روش، Inline Temp گفته میشه.


    • Replace Temp with Query هنگامی استفاده میشه که شما در تابعی، متغیری دارید که حاصل یک عبارت هستش. تو این شرایط، محاسبه عبارت از روی Member Variable ها رو باید به یک تابع دیگه منتقل کنید، و سپس اونو جای متغیر مربوطه استفاده کنید.


    • Explaining Variable روشی است که در اون، یک عبارت پیچیده، به بخشهای قابل فهم توصیف پذیر شکسته میشن و هر بخش به یه متغیر موقت Assign میشه. اجازه بدید مجددا مثالی از یک Web App بزنم. فرض کنید توی کد نیاز داریم که در صورتیکه browser کاربر IE و سیستم عاملش Windows بود، فلان محصول رو بهش پیشنهاد بدیم. برای اینکار، میتونیم در یک شرط، نوع سیستم عامل و همینطور نوع مرورگر رو بررسی کنیم و در صورتیکه Windows و IE بود، HTML مورد نظر رو Render کنیم. اما به جای اینکه چنین if "سخت فهمی" بنویسیم، بهتره یه متغیر boolean به اسم isIE داشته باشیم که نشون دهنده IE بودن یا نبودن Browser هستش، و یک متغیر به اسم isWin که نشوندهنده ویندوز بودن یا نبودن سیستم نگاه میشه متوجه شد که هدف از اون if چی بوده. عامل هستش. سپس در if مورد نظر، از این دو متغیر استفاده کنیم. در این صورت، if مزبور توصیف پذیر هستش.


    • روش بعدی هنگامی کاربرد داره که در یک تابع طولانی از local variable ها بگونه ای استفاده میکنه که نمیشه Extract Method رو روی اون اعمال کرد. تو چنین شرایطی، کلیه اون local variable ها رو به کلاس جدیدی باید منتقل کنید و توابع جدیدی برای اون در نظر بگیرید تا با فراخوانی اون توابع روی object مزبور به هدفتون برسید.


    • روش بعدی در Refactoring، عدم Assign کردن Formal Paramter های یک تابع، به مقادیر بازگشتی از توابع دیگه هستش. جای این کار، باید از متغیر موقت دیگه ای استفاده کرد.


    • روش بعدی در Refactoring یک کد، جایگزینی الگوریتم با یک الگوریتم بهتر هستش. توی این روش، شما منطق دستیابی به هدف رو با منطق بهتری جایگزین می کنید.


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

    به مجموعه روشهایی که تا کنون اشاره کردم، Composing Methods یا متودهای ترکیبی میگن. اعمال برخی از این روشها روی کد توسط Visual Studio به سادگی میسر هستش که در ادامه گفتگو در مورد اون صحبت خواهیم کرد.

    از دیگر روشهای Refactoring میشه به موارد زیر اشاره کرد:

    • Move Method
    • Move Field
    • Hide Delegate
    • Inline Class
    • Extract Class
    • Foreign Method
    • Local Extensions
    • Pull up Constructor
    • Push Down Method
    • Replace Constructor with Factory Method
    • Extract Subclass
    • Extract Superclass

    اینها روشهای عجیب و غریبی نیستن و در خیلی از موارد از همون ابتدای کار میشه بخش عمده ای از اونها رو با ارائه یک معماری خوب برای نرم افزار پوشش داد. لطفا دقت کنید که این روشها هرگز مختص یک زبان خاص نیستن و میشه اونها رو در Platform های متفاوت اعمال کرد. اگر چه در تکنولوژیهای متفاوت، روشهای فوق، نسبتا رنگ و بوی دیگه ای به خودشون میگیرن، اما مسائلی که نام بردم، سرچشمه کلیه متودهای موجود در Refactoring هستش و در کتاب Refactoring: Improving the Design of Existing Code در مورد اونها به تفصیل توضیح داده شده.

    گمان میکنم با ذکر چند مثال عملی در Visual Studio و کدهای C#‎، بحث Refactoring قدری ملموس تر بشه و این مساله نیز باب آشنایی شما با ابزارهای Refactoring در Visual Studio رو نیز باز کنه. اگر آقای عسگری اجازه بدن، چند مثال در این زمینه بزنم (من هنوز در مورد کدهای بودار، حرفهای ناگفته بسیاری دارم).

  21. #21

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    بعنوان اولین مثال، کد زیر رو در نظر بگیرید. این کد قراره مشخصات یک فاکتور رو چاپ کنه:


    public void PrintInvoice()
    {
    //Print invoice header...
    Console.WriteLine(this.invoiceId);
    Console.WriteLine(this.customerName);

    decimal grandTotal = 0;

    //Print invoice items and calculate the grand total...
    foreach (Item item in this.items)
    {
    Console.WriteLine("Id: " + item.Id);
    Console.WriteLine("Name: " + item.Name);

    grandTotal += item.Price;
    }

    //Print the grand total
    Console.WriteLine("Grand Total: " + grandTotal);
    }


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

    اکنون قصد داریم تا این کد رو Refactor کنیم. برای این منظور، میتونیم با Extract کردن بخش "چاپ سربرگ" شروع کنیم. ابتدا سه خط مربوط به چاپ سربرگ رو انتخاب میکنم، سپس روی ناحیه انتخاب شده، کلید سمت راست Mouse رو فشار میدم:



    اکنون در پنجره مربوطه، نام متود مورد نظر خودم رو وارد میکنم: "چاپ سربرگ" یا PrintInvoiceHeader و کلید OK رو فشار میدم. بدین ترتیب، متود جدیدی تحت عنوان PrintInvoiceHeader ایجاد میشه و خطوط انتخاب شده به درون این تابع جدید منتقل میشه. جای خطوط انتخاب شده نیز این تابع فراخوانی میشه. (روش اول، Extract Method در Refactoring).

    اکنون همین کار رو برای بخش چاپ جزییات فاکتور نیز انجام میدم. البته این بار، نام تابع رو PrintInvoiceDetails میذارم. دقت کنید که اینبار از خط 9 تا 19 در کد فوق رو انتخاب میکنم، سپس گزینه Extract Method رو انتخاب میکنم. بدین ترتیب کد ما به این شکل تغییر خواهد کرد:

    public void PrintInvoice()
    {
    PrintInvoiceHeader();

    decimal grandTotal = 0;

    grandTotal = PrintInvoiceDetails(grandTotal);
    }

    private decimal PrintInvoiceDetails(decimal grandTotal)
    {
    //Print invoice items and calculate the grand total...
    foreach (Item item in this.items)
    {
    Console.WriteLine("Id: " + item.Id);
    Console.WriteLine("Name: " + item.Name);

    grandTotal += item.Price;
    }

    //Print the grand total
    Console.WriteLine("Grand Total: " + grandTotal);
    return grandTotal;
    }

    private void PrintInvoiceHeader()
    {
    //Print invoice header...
    Console.WriteLine(this.invoiceId);
    Console.WriteLine(this.customerName);
    }


    همونطوریکه می بینید، تعریف متغیر grandTotal بیرون موند و تابع جدیدی با Prototype ای که می بینید ایجاد شده که grandTotal رو در Formal Parameter خودش میگیره، و پس از محاسبه جمع کل فاکتور، اونو برمیگردونه.

    این کد هنوز بودار هستش چرا که اولین قانون در مورد توابع رو رعایت نکرده و اون چی بود؟ هر تابع باید یک و فقط یک کار رو انجام بده. در صورتیکه کد فوق، علاوه بر محاسبه grandTotal در تابع PrintInvoiceDetails، داره جزییات فاکتور رو نیز چاپ میکنه.

    پر واضح هستش که محاسبه grandTotal به این تابع تعلقی نداره. به این منظور تابع جدیدی تحت عنوان GetGrandTotal ایجاد می کنیم و کدمون رو به شکل زیر تغییر میدیم:

    public void PrintInvoice()
    {
    PrintInvoiceHeader();

    decimal grandTotal = GetGrandTotal();
    PrintInvoiceDetails(grandTotal);
    }

    private void PrintInvoiceDetails(decimal grandTotal)
    {
    //Print invoice items and calculate the grand total...
    foreach (Item item in this.items)
    {
    Console.WriteLine("Id: " + item.Id);
    Console.WriteLine("Name: " + item.Name);
    }

    //Print the grand total
    Console.WriteLine("Grand Total: " + grandTotal);
    }

    private decimal GetGrandTotal()
    {
    decimal result = 0;
    foreach (Item item in this.items)
    result += item.Price;

    return result;
    }

    private void PrintInvoiceHeader()
    {
    //Print invoice header...
    Console.WriteLine(this.invoiceId);
    Console.WriteLine(this.customerName);
    }


    این یک کد خوبه که اجزای اون به بخشهای کوچک قابل مدیریت تقسیم شدن. ممکنه این سوال مطرح بشه که Performance کد قبلی بالاتر از کد فعلی بوده، آیا با توجه به این مساله هنوز هم Refactoring برای کد اولیه، توصیه میشه؟ پاسخ به این سوال، مثبت هستش. در واقع کد فعلی خواناتر و مدیریت پذیر هستش. اگر یک فاکتور واقعی رو در نظر بگیریم که در اون باید جزییات متفاوت فاکتور نمایش داده بشه، اونوق پی به این مساله میبرید که قربانی کردن میزان اندکی Performance، باعث چند برابر شدن خوانایی کد میشه.
    عکس های ضمیمه عکس های ضمیمه
    • نوع فایل: jpg 01.jpg‏ (74.0 کیلوبایت, 1440 دیدار)

  22. #22

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    بعنوان مثال دوم، به کد زیر رو که رفتار دو اتومبیل پورشه و مرسدس رو شبیه سازی میکنه، دقت کنید:


    public class Porsche
    {
    public string Brand;
    public System.Drawing.Color Color;

    public Porsche(string brand, System.Drawing.Color color)
    {
    Brand = brand;
    Color = color;
    }

    public Porsche(string brand)
    {
    Brand = brand;
    }

    public void Start()
    {
    }

    public void Brake()
    {
    }
    }

    public class Mercedes
    {
    public string Brand;
    public System.Drawing.Color Color;

    public Mercedes(string brand, System.Drawing.Color color)
    {
    Brand = brand;
    Color = color;
    }

    public Mercedes(string brand)
    {
    Brand = brand;
    }

    public Mercedes()
    {
    }

    public void Start()
    {
    }

    public void Brake()
    {
    }
    }


    بوی بد این کد رو می تونید استشمام کنید؟ این کد حاکی از نادیده گرفته شدن Object Oriented توسط برنامه نویس هستش. آستینها رو بالا بزنید و مرحله به مرحله کد رو پالایش کنید.

    در مرحله اول، خصیصه های یک اتومبیل رو از دو کد فوق جدا می کنیم. برای اینکار، داخل یکی از دو کلاس فوق، Right Click کرده، گزینه Refactor رو انتخاب کنید. سپس Extract Interface رو انتخاب کنید. نام Interface رو به ICar تغییر بدید و مطمئن بشید که دو متود Start و Brake رو انتخاب کرده اید. حالا کلید OK رو بزنید. بدین ترتیب، Interface ای با دو متود فوق ایجاد میشه:


    interface ICar
    {
    void Brake();
    void Start();
    }


    و کلاس Porsche (با فرض اینکه در این کلاس Right-Click کرده ایم) از ICar مشتق میشه. حالا کلاس Mercedes رو بصورت دستی از همون ICar درایو کنید. از نظر منطقی اوضاع بسیار بهتر شد، اما هنوز بوی بد این کد آزار دهنده هستش.

    در مرحله بعد، ابتدا ICar رو بدین شکل تغییر میدیم، زیرا مدل و رنگ، دو خصیصه یک اتومبیل هستن:


    interface ICar
    {
    void Brake();
    void Start();

    string Brand { get; set; }
    System.Drawing.Color Color { get; set; }
    }


    سپس دو کلاس فوق رو بگونه ای تغییر میدیم که دسترسی به دو متغیر عمومی فوق، کنترل شده باشه (در واقع از طریق Accessor ها در دسترس باشن). بدین ترتیب به کد زیر میرسیم:

    interface ICar
    {
    void Brake();
    void Start();

    string Brand { get; set; }
    System.Drawing.Color Color { get; set; }
    }

    public class Porsche : ICar
    {
    public string brand;
    public System.Drawing.Color color;

    public string Brand
    {
    get { return this.brand; }
    set { this.brand = value; }
    }

    public System.Drawing.Color Color
    {
    get { return this.color; }
    set { this.color = value; }
    }

    public Porsche(string brand, System.Drawing.Color color)
    {
    Brand = brand;
    Color = color;
    }

    public Porsche(string brand)
    {
    Brand = brand;
    }

    public void Start()
    {
    }

    public void Brake()
    {
    }
    }

    public class Mercedes : ICar
    {
    private string brand;
    private System.Drawing.Color color;

    public string Brand
    {
    get { return this.brand; }
    set { this.brand = value; }
    }

    public System.Drawing.Color Color
    {
    get { return this.color; }
    set { this.color = value; }
    }

    public Mercedes(string brand, System.Drawing.Color color)
    {
    Brand = brand;
    Color = color;
    }

    public Mercedes(string brand)
    {
    Brand = brand;
    }

    public Mercedes()
    {
    }

    public void Start()
    {
    }

    public void Brake()
    {
    }
    }


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

    public class Car : ICar
    {
    private string brand;
    private System.Drawing.Color color;

    public Car(string brand, System.Drawing.Color color)
    {
    Brand = brand;
    Color = color;
    }

    public Car()
    {
    }

    public string Brand
    {
    get { return this.brand; }
    set { this.brand = value; }
    }

    public System.Drawing.Color Color
    {
    get { return this.color; }
    set { this.color = value; }
    }

    public virtual void Brake()
    {
    }

    public virtual void Start()
    {
    }
    }


    و در نتیجه دو کلاس پورشه و مرسدس رو به این شکل تغییر میدم:

    public class Porsche : Car
    {
    public Porsche(string brand, System.Drawing.Color color)
    {
    base.Brand = brand;
    base.Color = color;
    }

    public Porsche(string brand)
    {
    base.Brand = brand;
    }

    public Porsche()
    {
    }
    }

    public class Mercedes : Car
    {
    public Mercedes(string brand, System.Drawing.Color color)
    {
    base.Brand = brand;
    base.Color = color;
    }

    public Mercedes(string brand)
    {
    base.Brand = brand;
    }

    public Mercedes()
    {
    }
    }


    بسیار خوب. باز هم بهتر شد. آیا احساس می کنید که هنوز هم میشه این دو کلاس رو بهبود داد؟ لطفا به این کد دقت کنید:

    public class Porsche : Car
    {
    public Porsche(string brand, System.Drawing.Color color)
    : base(brand, color)
    {
    }

    public Porsche(string brand)
    : this(brand, System.Drawing.Color.Empty)
    {
    }

    public Porsche()
    {
    }
    }

    public class Mercedes : Car
    {
    public Mercedes(string brand, System.Drawing.Color color)
    : base(brand, color)
    {
    }

    public Mercedes(string brand)
    : this(brand, System.Drawing.Color.Empty)
    {
    }

    public Mercedes()
    {
    }
    }


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

    اکنون به Class Diagram این کد نگاه کنید و از اون لذت ببرید (سمت چپ قبل از انجام عمل Refactoring و سمت راست Class Diagram مربوطه پس از انجام عمل Refactoring را نمایش می دهد).


    عکس های ضمیمه عکس های ضمیمه
    آخرین ویرایش به وسیله mehdi.mousavi : سه شنبه 18 خرداد 1389 در 00:18 صبح دلیل: تصحیح Class Diagram ها، تعریف متودهای Brake و Start بصورت Virtual

  23. #23

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    قصد دارم بر روی مبحث Code Resusability کمی صحبت کنم،
    گاها" با کدهایی مواجه می شوم که اصطلاح "کاغذ دیواری" را برای آن ها به کار می برم، حتما" می پرسید چرا کاغذ دیواری؟! به دلیل اینکه یک قطعه کد خاص "مدام" تکرار می شود و تنها مقادیر بعضی از متغیرها تغییر پیدا می کند و در عملکرد تفاوتی ندارد، خوب هدف ما همیشه باید این باشد که Redundancy کد را کاهش دهیم، باید سعی کنیم کدی بنویسیم که با حداقل تعداد خط کد ممکن ما را به مقصود برساند و تمام عملکردهایی که قرار است برنامه انجام دهد داخل متد ها و کلاس های مرتبط که هر کدام وظیفه ای را بر عهده دارند Encapsulate شود، راجع به "جادوی Copy & Paste چیزی شنیدید؟ این اصطلاح را برای اولین بار از زبان یکی از مهندسین مایکروسافت در ویدوهای سایت ASP.NET شنیدم، یعنی هنگامی که قصد انجام یک عملیات مشابه را دارید یک قطعه کد را مرتب در قسمت های مختلف برنامه کپی می کنید و فقط جای یکسری متغیر را عوض می کنید، حالا تصورش را بکنید که فراموش کنید بعد از کپی کردن کد، یکی از متغیرها را تغییر دهید، چه اتفاقی می افتد؟ بله، یک نیم ساعتی احتمالا" با کد کلنجار می روید که ببینید اشکال کار کجاست، این موارد را به تجربه مشاهده کرده ام، برای اصلاح چنین کدی به روش های Refactoring که آقای موسوی توضیح دادند باید رجوع نمود،
    یک کد Reusable یا قابل استفاده مجدد باید چه ویژگی هایی داشته باشد؟ البته برخی از این ویژگی ها پیش از این در گفته های آقای موسوی آورده شده است:

    1. از "تکرار" خودداری کنید، هرگاه دیید یک کد مشابه در چندجا تکرار می شود، بدانید که یک جای کار ایراد دارد، کد باید طوری باشد که بتوانید "با حداقل تغییرات" و "حداقل تاثیر بر روی دیگر قسمت ها"، به اصلاخ یک قسمت از برنامه بپردازید،
    2. چرا برنامه نویسان ما اغلب از Unit Test استفاده نمی کنند؟ سعی کنید با روش های Unit Testing آشنا شوید و متدهای قابل Test شدن همراه با Unit Test بنویسید تا در مواقع لزوم بتوانید عملکرد برنامه خود را به خوبی بررسی نمایید، قصد ندارید که بنشینید و تک تک متدهای برنامه را به طور دستی تست کنید؟!!
    3. عملکرد تمامی متدها و کلاس های نوشته شده باید "مشخص" باشد و هر کلاس یا متد باید تنها یک "وظیفه" را بر عهده داشته باشد، الان به ذهنم رسید که در کنار جمله ی Practice Makes Perfect ما برنامه نویسان باید بگوییم Refactoring Makes Perfect!
    4. سعی کنید با الگو های جداسازی کد آشنا شوید، یکی از این بهترین الگوها که حتما" یک بار اسم آن به گوشتان خورده است و امروزه سر زبان هاست، "معماری چند لایه" می باشد، چرا این همه اصرار می کنیم که از این گونه معماری ها بهره بگیریم؟ چرا الگو هایی مانند MVC یا MVVM را به کار می گیریم؟ شاید می پرسید خوب چرا همه ی کد را به طور متمرکز در یک جا نمی نویسیم؟ چه نیازی به این روش ها هست؟ حقیقت این است که یکی از مهمترین دلایل این است که می خواهیم مفهومی به نام Separation Of Concerns را پیاده سازی نماییم، تمامی اجزا باید در عین "مجزا" بودن از یکدیگر با یکدیگر تعامل مناسبی داشته باشند، باید مشخص باشد که هر بخش چه وظیفه ای را بر عهده دارد، باید معلوم باشد که در یک برنامه عملیات مربوط به Business Logic کجا انجام می شود، متد تبدیل تاریخ شمسی به میلادی در کدام کلاس است، فرم های برنامه من همگی توسط چه متدی و در چه کلاسی لود می شوند، خویب این کمک می کند هر زمان که قصد دارید کد خود را ارتقا دهید یا یک باگ را برطرف کنید، "دقیقا بدانید به کجا باید بروید" و داخل چندین هزار خط کد "سردرگم" نمی شوید،
    5. همواره کدهایی که نیازی در برنامه به آن ها ندارید را حذف کنید، چون فقط باعث سردرگم شدن شما می شود،
    6. طوری کد بنوسید که قابل Extend شدن باشد و بتوان در آینده آن را گسترش داد، قابلیت های جدید به آن اضافه نمود یا برخی قابلیت ها را override کرد، کدنویسی یک فرآیند مستمر است، جمله ی جالبی از جناب Scott Guthrie در یکی از ویدئوهای بررسی قابلیت های VS 2010 شنیدم که "بیشتر وقت برنامه نویسان بر روی مرور کدهای نوشته شده صرف می شود تا نوشتن کدهای جدید" (نقل به مضمون)، برای رسیدن به این مقصود هم باید سعی کنید مفاهیم شیء گرایی را به خوبی درک نمایید،
    7. ...

    این بحث ادامه دارد...،/
    I've just started tweeting!
    @Alireza_Maddah

  24. #24

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    خب ، ابتدا آقای مداح لطفا یک توضیح کوتاهی در مورد Unit Testing و نیز MVC بدید تا کسانی که با این اصطلاح ها آشنا نیستند ، قادر به درک کامل مطلب باشند (در ضمن تست نرم افزار جزو مواردی هست که قصد داریم در آینده در موردشون بحث کنیم)
    و برای سوق دادن گفتگو به سمت مطالب دیگر، لطفا در مورد نقش مستند کردن کد صحبت کنید.
    سوالم اینه که آیا مستند کردن کد (اون قسمتی که به برنامه نویس مربوطه) فقط شامل کامنت گذاری روی کد میشه یا انواع دیگری هم داره ؟
    و این که چرا توصیه میشه در همون ابتدای برنامه نویسی (و به صورت موازی با کد زدن) عمل مستند سازی رو انجام بدیم ؟
    بعدش از نظر آقای موسوی در این زمینه بهره مند می شیم
    We work in the dark, we do what we can, we give what we have.
    Our doubt is our passion and our passion is our task.
    The rest is the madness of art

  25. #25

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    همانطور که می دانید هر Project به بخش های مختلفی تقسیم بندی می شود، دارای فایل های متفاوتی است، از کلاس های متفاوتی تشکیل می شود که هر یک دارای متدهای خاصی است که وظیفه ی معینی را بر عهده دارند، به ببان بسیار ساده، Test کردن هر یک از این قسمت ها به صورت مجزا به عنوان عملیات Unit Testing شناخته می شود، چرا که هر واحد/فسمت یا Unit از Application مورد آزمایش قرار می گیرد، تصور کنید کلاسی دارید که وظیفه ی ارتباط با دیتابیس و فراخوانی یکسری Stored Procedure برای انجام عملیات بر روی یکی از جداول دیتابیس را بر عهده دارد، در طول زمان دیتابیس دستخوش تغییرات می شود و ممکن است که عملکرد این متدها تحت تاثیر قرار بگیرد و متدهای قراخوانی SP ها باید مورد Test مجدد قرار بگیرند، شما یکسری متد Test تولید می کنید(یا به طور دستی و یا به طور خودکار و توسط یکسری ابزار) و هربار این Test ها را اجرا می نمایید تا از صحت عملکرد این متدها آگاه شوید،
    اگر در یک پروژه چندین Developer مشغول کار باشند، ممکن است هر کدام در حوزه ی مشخص خود، یکسری Unit Test را برای اطمینان از صحت عملکرد کد خود، تهیه کند، اما توجه کنید که کار یک Tester حرفه ای بیش از این هاست، او سعی می کند علاوه بر اینکه از عملکرد یک Unit مطمئن شود، کاری کند که آن کد درست عمل نکند و از کار بیفتد! چرا که اگر او اینکار را نکند، برنامه هنگامی که به دست کاربر نهایی(End-User) برسد دارای مشکلاتی خواهد بود، برای شروع در این زمینه به کتاب Amazon.com: A Tester's Guide to .NET Programmingمراجعه نمایید،
    در صورت صلاحدید مجری محترم، مثال کوچکی در این زمینه ارائه خواهم داد،

    اجازه دهید کمی هم در مورد MVC صحبت کنم؛ اگر کمی با ASP.NET آشنا باشید، حتما" نام این الگو یا معماری را شنیده اید، اما ASP.NET MVC یکی از "پیاده سازی" های این معماری بوده و این معماری چیزی نیست که به تازگی ظهور کرده باشد،
    این معماری روشی برای "جداسازی" بخش های مختلف یک پروژه به ما ارائه می دهد که در آن به بیان خیلی ساده، "منطق برنامه" مانند پردازش ورودی گرفته شده توسط کاربر و ارسال آن به دیتابیس از "واسط کاربری" که در آن ورودی از کاربر گرفته می شود به طور مجزا عمل می کند، در نتیجه به طور "مجزا" توسعه داده می شوند و به طور "مجزا" Test می شوند، به طور مثال شما در همان جایی که ورودی را از کاربر می گیرید، عملیات ارسال به دیتابیس و بررسی منطق آن را انجام نمی دهید و این ها به صورت مجزا از یکدیگر هستند،
    در این معماری، هر قطعه از واسط کاربری شما(مانند یک صفحه ی HTML) به عنوان View شاخته می شود، در Model، شما ساختار دیتابیس خود را در Application تولید می نمایید و عملیات Business Logic(مانند چک کردن اینکه در فرم ورود اطلاعات کاربر، نام کاربری وارد شده است یا نه) در آن صورت می پذیرد، حالا Controller تعامل هایی که توسط کاربر در UI انجام می شود(مانند کلیک کردن یک دکمه) را به عملیات قابل انجام توسط Model تبدیل می کند، خوب پس تا اینجا ورودی از کاربر در یک View گرفته می شود، بعد این ورودی به Controller مربوطه فرستاده می شود، این Controller عملیاتی را بر روی یک Model خاص و با توجه به وضعیت کنونی آن، انجام داده و مجددا" پاسخی را به View ی مربوطه ارسال می نماید، به تصویر زیر دقت کنید:


    خطهای نقطه چین نمایانگر رابطه غیرمستقیم و خط های توپر نمایانگر رابطه ی مستقیم هستند، هر Controller به طور مستقیم به View و Model در ارتباط است و هر View نیز به طور مستقیم Model مربوطه را می شناسد(توجه کنید این روابط یکطرفه هستند)،

    این توضیحات در زمینه ی هر دو مبحث فقط جهت آشنایی بود و به زبان خیلی ساده بیان شد،

    آقای عسگری کافیست یا نیاز به توضیحات بیشتری است؟

    ،/
    آخرین ویرایش به وسیله علیرضا مداح : پنج شنبه 20 خرداد 1389 در 17:05 عصر
    I've just started tweeting!
    @Alireza_Maddah

  26. #26

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    ممنون بابت توضیح
    البته بعد ها گفتگو های فنی تری در این زمینه ها خواهیم داشت ولی فکر کنم فعلا کافیه.
    لطفا به سوال هام در مورد مستندات و کامنت گذاری جواب بدید تا بریم و نظر آقای موسوی رو در این زمینه ها جویا بشیم
    We work in the dark, we do what we can, we give what we have.
    Our doubt is our passion and our passion is our task.
    The rest is the madness of art

  27. #27

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی



    When all else failed, read the documentation!


    خوب به بحث شیرین مستندسازی می پردازیم،
    هر چقدر که در توسعه نرم افزار تجارب بیشتری کسب می کنید، اهمیت بعضی مسائل برای شما بسیار پررنگ تر می شود، مستندسازی کد هم از آن دسته مسائل است،
    باید به خاطر داشته باشید که "برنامه نویسی" و "توسعه" یک فرآیند "مستمر" و "گروهی" است، تصور کنید که شما در یک تیم برنامه نویسی چند ده نفره کار می کنید که تعداد مشخصی از این اعضاء مشعول توسعه ی لایه ی Business Logic هستند، خوب در این Assembly کلاس ها، متدها، اینترفیس ها، فضای نام ها و ... گوناگونی وجود دارد که اعضای دیگر پروژه قرار است از آن ها استفاده کنند، پس باید راجع به تمامی آن ها اطلاعات "دقیق" و "مستندی" داشته باشند، همچنین کدی که شما می نویسید باید "قابل نگهداری" یا "Maintainable" باشد که اگر در آینده، "خود شما" یا "برنامه نویس دیگری" خواست تغییرات یا بهبودهایی را بر روی آن اعمال کند، دچار سردرگمی نشود،
    سوال اساسی اینجا مطرح می شود که "چرا باید عملیات مستندسازی کد همگام با نوشتن آن صورت گیرد؟"
    در زمانی که مشغول نوشتن کد هستید، به طور کامل از عملکرد آن آگاهید و معز شما به شدت با آن درگیر است، اگر بعدا" قصد داشته باشید که کد خود را مستندسازی کنید، "شک نکنید" که حتی ممکن است مجبور شوید 2 برابر زمان عادی مستندسازی کد در روش موازی، زمان صرف کنید و این صرف زمان هم ذهن شما را خسته می کند در حالی که می توانستید انرژی خود را در کار مفیدتری صرف کنید، در ضمن ممکن است همزمان با اتمام نوشتن یک مثلا متد یا کلاس، تیم Test بخواهد شروع به نوشتن Unit Test کند، پس نیاز دارد که از کارکرد آن آگاه باشد، در ضمن اینکه آگر در زمان کدنویسی، عملیات مستندسازی را انجام ندهید، ممکن است بعدا" وظیفه ی یک خط کد به کلی از یادتان برود، حتما" برای خیلی از شما پیش آمده است که بعد از گذشت چند ماه از نوشتن یک متد مستند نشده، وقتی به خط معینی از آن مراجعه می کنید از خودتان می پرسید "من جرا این خط کد را نوشته ام؟، دلیل آن چه بوده است؟"، بعد ممکن است به خود بگویید: "نه، فکر کنم این خط زیادی باشد و کاربردی نداشته باشد، پس آن را حذف می کنم." و بعد فاجعه رخ می دهد، چون حذف آن خط کد ممکن است در ظاهر و هنگام کامپایل خود شما را با خطا مواجه نکند، اما ممکن است تاثیر آن در زمان اجرا و بخشی از پروژه، نمایان شود،
    البته توجه کنید که مستندسازی نرم افزار یا Software Documentation مفهوم وسیع تری را پوشش می دهد که شمامل Requirement Documentation, Architectural/Design Documentation, User Manual و "Technical Documentation" می باشد، مستندسازی کد که ما قصد داریم به عنوان برنامه نویس در ادامه به آن بپردازیم، جزو Technical Documentation می باشد،
    پس تا بدین جا، اهمیت مستندسازی کد برای ما روشن شد،
    مستندسازی کد تنها به گذاشتن کامنت بر روی بدنه ی یک متد یا یک خط خاص محدود نمی شود و مفهوم وسیع تری را که شامل مستندسازی تمامی اجزای یک پروژه می باشد را دربر می گیرد،
    در ادامه قصد داریم راجع به موارد زیر صحبت کنیم:

    • نکات مهم در مستندسازی کد(در هنگام مستندسازی کد به چه نکاتی دقت کنیم؟)
    • آشنایی با تکنیک XML Documentation و Tag های مربوط به آن در .Net
    • معرفی ابزارهایی جهت تولید خودکار مستندات نهایی(مثلا چیزی شبیه به MSDN) از روی XML Documentation های موجود در Code
    • آشنایی با تگ های TODO, FIXME و ...

    ،/
    I've just started tweeting!
    @Alireza_Maddah

  28. #28

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    آقای موسوی ، خواهش می کنم شما هم در این مورد صحبت کنید تا وارد جزییات بیشتر این بحث بشیم
    We work in the dark, we do what we can, we give what we have.
    Our doubt is our passion and our passion is our task.
    The rest is the madness of art

  29. #29

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

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


    • Requirements Documentation که به مستند سازی شناخت نیازها، تواناییها و مشخصات یک سیستم می پردازه.
    • Architecture Decumentation که به مستندسازی معماری و طراحی سیستم نرم افزاری می پردازه و درباره کد نوشته شده صحبتی نمیکنه (یا بسیار کم به این مساله اشاره میشه).
    • Technical Documentation که در مورد مسائل فنی مرتبط با کد بطور صریح صحبت میکنه که بخشی از اون رو Comment های موجود در کد ما تشکیل میده.
    • User Documentation که در واقع نکات آموزشی و چگونگی کار با سیستم نرم افزاری رو برای کاربر نهایی سیستم تشریح میکنه.

    قاعدتا منظور شما از واژه "مستند سازی"، نوع سوم اون یعنی "مستند سازی فنی" هستش که بخش اعظمی از اون رو Comment های همراه کد تشکیل میده. Comment چیه و چرا میگن که میتونه خیلی مفید، یا خیلی مضر باشه؟ چه Comment ای خوبه و چه کدی باید Comment بشه؟

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


    • آیا نمیتونم با اضافه کردن تابعی، کدم رو خوانا کنم؟
    • آیا نمیتونم با نوشتن فلان عبارت به یک شکل دیگه، نیاز برای وجود Comment رو از بین ببرم؟"
    • آیا Refactoring به من کمک میکنه تا از نوشتن Comment برای فلان بخش از برنامه منصرف بشم؟ و ...


    Robert Martin بشدت در مورد Comment کردن کدها بد بین هستش و معتقده در بیشتر مواقع این Comment ها به ما دروغ میگن، البته نه از روی قصد و نیت قبلی! اون معتقده که هر چه Comment های یک کد از محل اصلی خودشون دورتر میشن (بواسطه Update های بعدی کد) و فاصله زمانی بین Update کردن Comment ها بیشتر میشه، اونها تبدیل به متونی بی محتوا و گمراه کننده میشن که فقط خواننده کد رو به درد سر میندازه. و این یک حقیقت هستش. ما بعنوان توسعه دهنده ذهنمون برای Update نگه داشتن این متون برنامه ریزی نشده و این کار برای ما دشوار هستش.

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

    اینها رو گفتم تا بگم که گمان نکنید کد بد خودتون رو می تونید با نوشتن یک Comment زیبا، خوب و "بی بو" جلوه بدید. جای اینکه وقتتون رو صرف نوشتن یک Comment خوب برای اشتباهی که در کد کرده اید بکنید، اون وقت رو صرف بهینه تر کردن خود کد کرده و خوانایی کد رو بالا ببرید. به عنوان مثال به این کد جاوا نوشته Robert Martin دقت کنید:

    // Check to see if the employee is eligible for full benefits
    if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))


    در واقع شرط "ساعتی پول گرفتن کارمند در صورتیکه وی بیش از 65 سال سن داشته باشه"، شرط "گرفتن دستمزد کامل" تعیین شده. آیا بهتر نیست اونو بدین شکل بنویسیم؟

    if (employee.isEligibleForFullBenefits())


    کد دوم، خودش داره با خواننده صحبت میکنه و کاملا واضح هستش که هدف از کد چیست. پس دقت کنید، هر وقت خواستید Comment ای بنویسید، حتما قبلش از خودتون سوالاتی رو که گفتم بپرسید و سعی نکنید ضعف کد خودتون رو با نوشتن یک Comment بپوشونید! خوب، اگر اینطور باشه پس من دیگه کی Comment بنویسم؟ این سوالی هستش که در حال حاضر در ذهن شما نقش بسته...

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

    از طرف دیگه، Comment هایی که اطلاعات مفیدی بهمون میدن نیز در بخش Comment های خوب قرار میگیرن. بعنوان مثال، Comment کردن یک Regular Expression خیلی میتونه مفید باشه (اگر چه نام متغیر مورد استفاده باید با دقت انتخاب بشه که مبین دلیل استفاده از Regular Expression مورد نظر باشه - منظورم همون استفاده از اسامی معنادار هستش که در ابتدای صحبتهام بهش اشاره کردم).

    Comment هایی که هدف نوشته شدن یک کد رو نیز بیان میکنن، میتونه مفید باشه. فرضا من کدی میبینم که در اون 30 تا Thread در یک حلقه ایجاد شده. اینجا اگر بدونم که دلیل ایجاد این Thread ها از منظر برنامه نویس چی بوده، خیلی به من کمک میشه، اگرچه ممکنه من با الگوریتم مورد استفاده در کد موافق نباشم.

    کسانیکه با C++‎‎‎ از روزگاران قدیم آشنا هستن قاعدتا با واژه Precondition و Postcondition بیگانه نیستن. در روزگاران دور، هر تابعی که نوشته میشد، باید شرایط قبل و پس از اجرای خودش رو بصورت Comment در ابتدای تابع عنوان می کرد تا مرورکننده کد می دونست که:

    1. تحت چه شرایطی اجازه داره این تابع رو فراخوانی کنه.
    2. پس از فراخوانی تابع، وضعیت جدید Object چی هستش.


    مدتها گذشت و برنامه نویسهای C++‎‎‎ دیدن که عموما این Comment ها رو نمیخونن و در نتیجه نمیدونن فلان تابع در چه شرایطی باید فراخوانی بشه و همین مساله آغازگر مشکلات بعدی بودش. به بیان دیگه، تابعی در شرایطی فراخوانی می شد که برای اون شرایط برنامه ریزی نشده بود. در حقیقت برنامه نویس باید قبل از فراخوانی تابع، مستندات اون تابع رو مطالعه میکرد تا ببینه اجازه داره اون تابع رو در اون State خاص Object فراخوانی کنه یا خیر.

    برای روشن شدن مطلب، به این کد دقت کنید:

    private string[] courseNames = new string[10];

    //Precondition:
    // This property returns the course name based on a zero-based index of the course in the courseNames.
    // Please note that the valid range for the index parameter is 0-9.
    // Also note that you should not pass a null or empty string to this property when setting the course name.
    //Postcondition:
    // The set accessor will update the courseNames array.
    private string this[int index]
    {
    get
    {
    return this.courseNames[index];
    }

    set
    {
    this.courseNames[index] = value;
    }
    }


    در این کد، برنامه نویس توضیح میده که فقط در شرایطی میتونید این property رو فراخوانی کنید که index ای که به این property میدید، بین 0 تا 9 باشه. ضمنا، اگر میخواهید property رو set کنید، حواستون باشه که اجازه ارسال empty یا null به این property رو ندارید. بدین ترتیب، هنگامیکه میخواستیم این property رو فراخوانی کنیم، باید حواسمون می بود که همچین کدی ننویسیم:

    this[10] = null;

    چون اولا Index اش بیشتر از 9 هستش، ثانیا داریم null رو بعنوان نام یک درس assign میکنیم... اما این حالت فقط هنگامی میسر بود که من اون Comment ها رو میخوندم و میدونستم تحت چه شرایطی اجازه دارم این property رو call کنم. از اونجاییکه عموما Comment ها در این بخش نادیده گرفته میشدن، امکانی تحت عنوان ASSERT معرفی شد. ASSERT در لغت به معنای "تایید شرایط" هستش. اما منظور چیه؟

    در واقع با Assert من دارم بخش اعظمی از اون Comment ها رو که در بخش precondition قرار میگرفتن رو به درون کد میارم. برای روشن شدن موضوع، به همون property که به گونه دیگه ای نوشته شده توجه کنید:

    private string[] courseNames = new string[10];

    /// <summary>
    /// Gets or sets the course name using a zero-based index of the course.
    /// </summary>
    /// <param name="index">The zero-based index of the course.</param>
    /// <returns>The course name.</returns>
    private string this[int index]
    {
    get
    {
    System.Diagnostics.Debug.Assert(index >= 0 && index <= 9);
    return this.courseNames[index];
    }

    set
    {
    System.Diagnostics.Debug.Assert(index >= 0 && index <= 9);
    System.Diagnostics.Debug.Assert(!string.IsNullOrEm pty(value));

    this.courseNames[index] = value;
    }
    }



    در واقع اینجا دارم میگم اگر توی runtime و در حالت debug، خصیصه index با پارامتری خارج از بازه 0-9 فراخوانی شد، runtime جلوی منو بگیر و بهم اخطار بده تا ببینم چرا به شرایط فراخوانی تابع دقت نکرده ام!

    همینطور در set، علاوه بر چک کردن بازه index، دارم میگم "انتظار دارم که courseName ای که میخواد set بشه، null یا empty نباشه" که اگر باشه، بهم در runtime اخطار بده که "ای برنامه نویس! حواست کجاست؟ گفته بودی این Property رو فقط وقتی فراخوانی میکنی که courseName ای بهش بدی، نه اینکه empty یا null ردش کنی. حالا برو ببین کجا رو اشتباه کردی، درستش کن".

    به این مساله بسیار مهم دقت کنید. من modifier این property رو private گذاشتم تا بهتون نشون بدم که این indexer توسط برنامه نویسی که به این private property دسترسی داره میتونه فراخوانی بشه. فرض کنید شما رشته ای بعنوان email از کاربر میگیرید و انتظار دارید این ورودی واقعا یک email باشه. اینجا جریان کاملا متفاوت هستش. منظور من از انتظار چنین انتظاری نبود... این دو رو با هم اشتباه نکنید. اینجا، وقتی من از کاربر یک رشته میگیرم، خودم باید تصدیق کنم که این ورودی آیا ورودی معتبری هستش یا خیر که اگر نبود به کاربر بگم email اش رو درست وارد کنه. در صورتیکه هنگام استفاده از assert در واقع من هشدار باش رو به خودم دارم میدم و بر اساس وضعیت فعلی object این شرایط رو می نویسم. البته کد اخیر نیز کد بدبویی هستش و میشه اونو با یک الگوریتم به مراتب بهتر جایگزین کرد، اما الان قصد ندارم در مورد این مساله صحبت کنم و کد فوق رو فقط بعنوان یک مثال در نظر بگیرید...

    حالا برگردیم به Comment ها... پس تا الان متوجه شدیم که میشه Precondition ها رو بصورت assert نوشت (و البته، نوشتن assert فقط مختص precondition ها نیست). به این مساله نیز دقت کنید، که assert فقط در محیط debug و آزمایشگاهی کار میکنه و وقتی نسخه release برنامه رو بسازید، کلیه assert هایی که نوشته اید نادیده گرفته میشن و در کد نهایی وجود نخواهند داشت (درست مثل اینکه اصلا نوشته نشده بودن).

    برخی اوقات، کارهایی هستش که شما بعنوان برنامه نویسید دوست دارید انجام بدید، اما در حال حاضر فرصت انجام اون کارها رو ندارید و فعلا باید پیاده سازی خودتون رو سبکتر انجام بدید. اینجاست که با زدن یک TODO Comment، توضیح میدید که باید در آینده فلان تغییر رو در این کد ایجاد کنم...

    بعنوان نمونه در کد فوق، میتونم یک TODO با این مضمون بنویسم:

    //TODO: We need to replace the array with a generic list.
    private string[] courseNames = new string[10];


    در محیط Visual Studio، شما میتونید از منوی View گزینه Task List رو انتخاب کنید، Combo Box مربوطه رو روی Comments بذارید و لیست TODO ها در کل پروژه رو ببینید. به TODO میگن Token و VS یا Token های پیش فرضی Config شده. اگر مایلید میتونید Token های مورد نظرتون رو به Visual Studio اضافه کنید. برای اینکار، از منوی Tools گزینه Options رو انتخاب کنید. در درخت سمت چپ، زیر شاخه Environment گزینه Task List رو انتخاب کرده و سمت راست Token های مورد نظرتون رو اضافه کنید. بدین ترتیب از این به بعد Comment هایی که با Token مزبور شروع میشن توی Task List نمایش داده خواهند شد.

    بسیار خوب. اجازه بدید تا به برخی Comment های بد نیز اشاره کنم. Comment هایی که کد خوانایی رو توضیح میدن، Comment بدی هستن. فرض کنید یه array جدید شما new می کنید و اسم این array نیز معنادار انتخاب شده. دیگه چه نیازی هستش بالاش Comment بزنید که این array فلان کارو انجام میده؟ اون اسم خودش گویای مطلب هستش...

    گاهی اوقات برنامه نویسها هر تغییر جزیی در کلاس رو با آوردن یک Comment بالای فایل مربوطه اعلام میکنن. به این Comment ها، Journal Comment میگن. استفاده از Journal Comment ها واقعا کار نادرستی هستش و باید ازش دوری بشه. سیستم های Source Control وظیفه اشون همینه و این کار باید به اونها واگذار بشه.

    یکی دیگه از Comment های بد، Comment هایی هستش که برخی از برنامه نویسها در انتهای Block هاشون میذارن. به چنین Comment هایی Close Brace Comments میگن. فرضا بلاک while ای درست میکنن و اونجاییکه block رو با { میبندن، یه Comment مینویسن به این شکل:

    while (true)
    {
    }//while


    این Comment ها فاقد ارزش هستش.

    بسیار خوب. آقای عسگری؟
    آخرین ویرایش به وسیله mehdi.mousavi : شنبه 22 خرداد 1389 در 18:17 عصر دلیل: تصحیح Comment یک تابع برای خوانایی بیشتر

  30. #30

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    بله ، منظور من کامنت های درون کد بودن.
    آقای مداح ، نمی دونم این بحث کامنت ها رو شما چقدر جلو خواهید برد (با توجه به این که دیگر وجوه مستندات و ... را در گفتگو های بعدی بررسی خواهیم کرد) چون با ذکر ASSERT ها در صحبت های آقای موسوی ، یاد این افتادم که در آخرین نسخۀ دات نت ، Code Contract ها معرفی شدن، و می خواستم شما بحث های پایانی رو در مورد مستند سازی و کامنت بفرمایید تا سراغ مفهوم Design By Contract و علت نیاز به اون ها (و این که چرا خیلی دیر در دات نت گنجونده شدن) بریم.
    (البته از اتاق فرمان اشاره می کنن ، وقت زیاد داریم و اگه نیاز هست بحث مستندات طولانی تر باشه ، مشکلی نیست. فقط خواستم از الان محور بحث بعدی هم مشخص باشه)
    We work in the dark, we do what we can, we give what we have.
    Our doubt is our passion and our passion is our task.
    The rest is the madness of art

  31. #31

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    خوب، قبل از اینکه وارد بحث XML Documentation شوم، قصد دارم تا چند نکنه را "تاکید" کنم:

    1. هنگام نوشتن کامنت به "شعور" خواننده توهین نکنید و تنها برای قسمت هایی از کد که نیاز به کامنت دارند، کامنت بنویسید، مثلا" به کد زیر دقت کنید:

    if (a > b) // If a is greater than b
    {
    }

    2.پس دقت کنید که ممکن است یک قطعه کد "Self-Explanatory" باشد و اصلا" نیازی به کامنت نداشته باشد، پس بی جهت کد خود را شلوغ نکنید،

    3.کامنت زیر را نگاه کنید و بعد قضاوت کنید.

    // You might be crazy if you use this obsolete method!
    public void Foo()
    {
    }

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

    4. تا حالا حتما" متوجه شدید که مستندات MSDN از یک قالب و شیوه ی نگارش خاص بهره می برند، مثلا" اگر به مستندات یک پروپرتی خاص نگاه کنید، متوجه می شوید که توضیح آن با عبارت "Gets or sets" آغاز می شود و در صورتی که این پروپرتی فقط دارای بخش Getter باشد، مستندات آن با واژه "Gets" آغاز می شود، این مسئله به ما می آموزد که در نوشتن کامنت ها و مستندات از یک شیوه ی نگارش مشخص بهره بگیریم و در طول پروژه آن را رعایت نماییم.

    5. اگر قصد نوشتن کامنت دارید، پس معطل نکنید، همزمان با نوشتن کد یا حتی پیش از آن اینکار را انجام دهید و اگر قصد ندارید که بعدا" کامنت خود را همزمان با تغییر کد، به روزرسانی کنید، پیشنهاد می کنم از خیر کامنت گذاری بگذرید، همانگونه که آقای موسوی هم به خوبی به این نکته اشاره کردند.

    6....

    ،/
    I've just started tweeting!
    @Alireza_Maddah

  32. #32

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    با تشکر از توضیحات آقای مداح...

    احتمالا در حین این گفتگو، این سوال در ذهن شما نقش بسته که "حالا که من تو یک تیم 3 نفره کار میکنم، چطور این قوانین رو در تیمم حکمفرما کنم تا کلیه اعضای تیم از یک سبک و سیاق واحد در طول پروژه پیروی کنن؟ آیا ابزاری برای Enforce کردن این قوانین در برنامه های C#‎‎‎‎‎ وجود داره؟"

    پاسخ به این سوال مثبت هستش (اگرچه حتی اگر بصورت انفرادی نیز مشغول بکار هستید، این ابزار میتونه کمک شایانی در حفظ خوانایی کد شما کنه). دو سال پیش، مایکروسافت تصمیم گرفت تا یکی از ابزارهای درونی شرکت رو تحت عنوان StyleCop (استایل کاپ تلفظ میشه، متاسفانه برخی اونو استایل کوپ تلفظ میکنن) در اختیار عموم قرار بده تا تیم های برنامه نویسی بتونن بصورت یکپارچه قوانین مورد نظر رو روی نحوه نگارش کد اعمال کنن. افرادیکه با .NET 1.0 کار کرده بودن حتما FxCop رو بخاطر میارن. StyleCop از برخی جهات شبیه FxCop هستش، با این تفاوت که FxCop تحلیل خودش رو روی فایلهای کامپایل شده انجام میداد در حالیکه StyleCop مستقیما Source Code ها رو بررسی میکنه.

    نسخه اولیه StyleCop هنگام تحلیل Source Code ها بالغ بر 200 قانون رو در نظر میگرفت. به بیان دیگه، اگر هر یک از این 200 مورد در Source برنامه رعایت نمیشد، برنامه نویس از این مساله مطلع میشد و باید نسبت به رفع اون اقدام می کرد. این قوانین به دسته های زیر تقسیم شده اند:

    • جاگیری المان ها، عبارات و دستورات
    • نحوه قرارگیری bracket ها، پرانتز ها، آکولاد ها و ...
    • فضای خالی پس از keyword ها و operator ها
    • فضای خالی بین خطوط
    • نامگذاری کلاسها و متغیرها
    • استفاده از تایپ های built-in
    • بررسی Comment های توابع، Property ها و کلاسها
    • و ...


    دو ماه پیش، مایکروسافت تصمیم گرفت تا این پروژه رو بصورت Open Source و تحت گواهینامه MS-PL در اختیار عموم قرار بده. به زودی نسخه جدید این نرم افزار (که روی Visual Studio نصب و فعال میشه) از C#‎‎‎‎‎ 4.0 نیز پشتیبانی خواهد کرد. دوستانی که از Visual Studio 2005 یا 2008 استفاده میکنن، میتونن همین حالا نسخه 4.3.3 رو بصورت رایگان از این سایت دریافت کنن.

    نسخه 4.4 که در حال حاضر بصورت بتا عرضه میشه و قابل نصب روی VS2010 هستش رو نیز می تونید از اینجا دریافت کنید. آخرین اخبار و اطلاعات در مورد StyleCop رو نیز می تونید در اینجا مشاهده کنید.

    اگر هنوز در نصب این برنامه 1.2MB تردید دارید، اجازه بدید تا با آوردن یک مثال عملی طرز کارش رو بهتون نشون بدم و شما رو به استفاده از اون ترغیب کنم.

    کلاسی رو که پیش تر مثال زده بودم، در نظر بگیرید:

    using System;
    using System.Text;
    using System.Collections.Generic;
    using System.Linq;

    public class Porsche : Car
    {
    public Porsche(string brand, System.Drawing.Color color)
    : base(brand, color)
    {
    }

    public Porsche(string brand)
    : this(brand, System.Drawing.Color.Empty)
    {
    }

    public Porsche()
    {
    }
    }


    برنامه رو ابتدا کامپایل کنید تا مطمئن بشید مشکلی وجود نداره (CTRL-SHIFT-B). وقتی از صحت Compile شدن برنامه اطمینان حاصل کردید، در وسط صفحه ادیتور Right-Click کنید و گزینه Run StyleCop رو انتخاب کنید. وقتی اینکارو انجام بدید، با 6 اخطار مواجه میشید:




    این خطاها بر اساس تنظیمات پیش فرض StyleCop تولید شده اند. شما می تونید هر کدوم رو که مایل باشید، برای پروژه خودتون غیر فعال کنید. اکنون روی اولین خطا Double-Click کنید تا به رفع اون اقدام کنیم. خطای اول، نشون میده که ما برای کلاس Porsche هیچ توضیحی نداریم. در نتیجه، استفاده کننده از این کلاس، نمیدونه که این کلاس به چه درد میخوره.

    بالای تعریف کلاس، سه بار کلید / رو فشار بدید تا یک Summary Block خالی ایجاد بشه. سپس توضیح مورد نظر رو در درون Tag مزبور بنویسید. بهمین ترتیب، توضیحات Constructor های کلاس مزبور رو نیز بنویسید تا کلاس Porsche به این کلاس تبدیل بشه:

    using System;
    using System.Text;
    using System.Collections.Generic;
    using System.Linq;

    /// <summary>
    /// Represents a Porsche car.
    /// </summary>
    public class Porsche : Car
    {
    /// <summary>
    /// Initializes a new instance of the Porsche class.
    /// </summary>
    /// <param name="brand">Indicates the Porsche brand including but not limited to 911, Boxster and Cayman.</param>
    /// <param name="color">The body color of the car.</param>
    public Porsche(string brand, System.Drawing.Color color)
    : base(brand, color)
    {
    }

    /// <summary>
    /// Initializes a new instance of the Porsche class.
    /// </summary>
    /// <param name="brand">Indicates the Porsche brand including but not limited to 911, Boxster and Cayman.</param>
    public Porsche(string brand)
    : this(brand, System.Drawing.Color.Empty)
    {
    }

    /// <summary>
    /// Initializes a new instance of the Porsche class.
    /// </summary>
    public Porsche()
    {
    }
    }


    اکنون مجدد StyleCop رو اجرا کنید:



    همونطور که میبینید، تعداد اخطارها به عدد 2 تقلیل پیدا کرد. ایراد بعدی، عدم Sort بودن using ها بر اساس حروف الفبا هستش. نگران نباشید. نیازی نیست دستی این کارو انجام بدید. روی صفحه Right-Click کنید، گزینه Organize Using و سپس Remove and Sort رو انتخاب کنید. اگر دقت کنید، کلیه using ها از بین میرن، چون این کلاس از هیچ یک از Namespace های مزبور استفاده نکرده. خوب! بهتر از این نمیشه.

    اخطار بعدی نشون میده که برای فایل Source امون، Header ای در نظر نگرفته ایم. برای این بخش، ابتدا روی نام پروژه در Solution Explorer کلید سمت راست Mouse رو بزنید و گزینه StyleCop Settings رو انتخاب کنید. سپس، پنجره Company Information رو انتخاب کنید و نام شرکت و Copyright مورد نظر رو اونجا وارد کنید:



    اکنون این سه خط رو بالای کلاس Porsche قرار بدید:

    // <copyright file="Porsche.cs" company="Mehdi's Company">
    // Copyright (c) Mehdi's Company. All rights reserved.
    // </copyright>


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



    حالا از هم تیمی هاتون بخواهید تا StyleCop رو روی ماشین هاشون نصب کنن و از این پس، بر اساس Microsoft StyleCop قبل از اینکه تغییراتشون رو روی Source Control قرار بدن، از درست بودن دست خطشون اطمینان حاصل کنن.

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



    البته این Comment ها کاربرد دیگه ای نیز دارن، که در ادامه بحث به اونها اشاره خواهم کرد...
    عکس های ضمیمه عکس های ضمیمه
    • نوع فایل: jpg 1.jpg‏ (53.8 کیلوبایت, 1296 دیدار)
    • نوع فایل: jpg 2.jpg‏ (34.2 کیلوبایت, 1287 دیدار)
    • نوع فایل: jpg 3.jpg‏ (39.3 کیلوبایت, 1284 دیدار)
    • نوع فایل: jpg 4.jpg‏ (28.2 کیلوبایت, 1280 دیدار)
    • نوع فایل: jpg 6.jpg‏ (31.5 کیلوبایت, 1274 دیدار)

  33. #33

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    حوب ابتدا از تاخیر به وجود آمده پوزش می خواهم، سعی می کنیم که از این به بعد بحث با سرعت مناسبی پیگیری شود،
    شروع می کنیم،
    بهره گیری از ابزار ارزشمند StylCop را به تمامی برنامه نویسان پیشنهاد می کنم، مطمئن باشید به محض شروع کار با آن، انقلابی در کدهای شما رخ خواهد داد، همه چیز مرتب، منظم، سر جای خود!
    یکی از بخش هایی هم که StyleCop بر روی آن تمرکز دارد، همین مستندات است،
    در پست قبلی آقای موسوی اشاره ای به XML Documentation داشتند،
    شما می توانید هنگام مستندسازی کدهای خود، از ویژگی Inline XML Code Documentation بهره بگیرید، به زبان ساده تر، داخل کد، مستندان آن را به زبان XML و با یک ساختار مشخص می نویسید، این شاختار مشخص از یکسری Tag هایی تشکیل می شود، پس از اینکه کد خود را مستند نمودید، هر موقع که توسعه گر دیگر قصد استفاده از آن را داشته باشد، Intellisense به کمک او خواهد آمد و بنا بر ساختاری که شما برای آن تعریف نمودید، آن شخص می تواند از عملکرد آن آگاه شود، همچنین ابزارهایی وجود دارد که مستندات XML موجود در کد را در یکسری قالب های مشخص برای شما اسخراج می نماید تا به طور مثال بتوانید مستندات Class Library که تهیه نمودید را به صورت مجزا توزیع نمایید،
    در مثال آقای موسوی شما متوجه شدید که هر زمان، قبل از یک متد، کلاس، پروپرتی یا هر عضو دیگری. سه بار کلید / را بزنید، یک قالب از پیش تعیین شده همراه با یکسری Tag ها برای شما تولید خواهد شد، این قالب بستگی به عضوی دارد که قصد مستندسازی آن را دارید، طبیعتا" هر کدام از این Tag ها ممکن است برای عضو های مشخص قابل استفاده باشد،
    خوب فرض کنید می خواهیم یک متد را مستند کنیم، به چه اطلاعاتی در رابطه با آن نیاز داریم؟ یا به عبارتی کدام یک از بخش های آن را می توانیم مستند کنیم؟


    • خلاصه ای از عملکرد آن(summary Tag)
    • توضیح پارامترهای آن(param Tag)
    • Exception هایی که ممکن است هنگام استفاده از این مند رخ دهد(exception Tag)
    • توضیح مقدار بازگشتی متد(returns Tag)
    • توضیحات بیشتر در مورد نحوه عملکرد و نکاتی که در استفاده از آن باید مد نظر قرار داد(remarks Tag)
    • ارائه ی مثالی جهت کار با آن متد(example Tag)
    • معرفی مطلب مرتبط با مستندات این متد که دیدن آن مفید است(seaalso tag)
    • ...
    • تمامی Tag ها در MSDN به تفصیل بررسی شده اند،

    می خواهیم بخشی از مستنداتی که برای متد System.Decimal.Multiply(Decimal, Decimal) نوشته شده است را بررسی نماییم:

    /// <summary>
    /// Multiplies two specified System.Decimal values.
    /// </summary>
    /// <param name="d1"> A System.Decimal (the multiplicand).</param>
    /// <param name="d2"> A System.Decimal (the multiplier).</param>
    /// <returns>A System.Decimal that is the result of multiplying d1 and d2.</returns>
    /// <exception cref="System.OverflowException">The return value is less than System.Decimal.MinValue or greater than System.Decimal.MaxValue.</exception>
    public static int Multiply(decimal d1, decimal d2)
    {
    // Implementaion goes here...
    }


    در اینجا جزئیات پیاده سازی مد نظر ما نیست،
    همانطور که مشاهده می کنید در داخل تگ <summary>عملکرد متد به طور خلاصه تشریح شده است، هر یک از پارامترها نیز توسط تگ <param> توصیف شده اند و در داخل تگ <returns> نیز در مورد مقدار بازگشتی متد اطلاعاتی به ما داده شده است، به کاربرد تگ <exception> دقت کنید، در element این تگ که cref نام دارد، Exception آورده شده است و در قسمت توضیحات هم دلیل وقوع آن توضیح داده شده است، همانطور که مشاهده می کنید این Exception زمانی رخ می دهد که "مقدار بازگشتی کمتر از System.Decimal.MinValue یا بیشتر از System.Decimal.MaxValue باشد."
    پس تا اینجا کمی با XML Documentation آشنا شدیم، خوشحال خواهم شد که توضیحات آقای موسوی را هم در این مورد بشنویم تا سپس به بررسی چگونگی استخراج مستندات XML از داخل کد بپردازیم،/
    آخرین ویرایش به وسیله علیرضا مداح : چهارشنبه 02 تیر 1389 در 12:34 عصر
    I've just started tweeting!
    @Alireza_Maddah

  34. #34

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    واقعیت اینه که در این زمینه حرف قابل عرضی باقی نمونده... البته فقط یک نکته به ذهنم میرسه و اونم تولید ماشینی این Comment هاست. تصور کنید ابزاری داشته باشید که توسط اون بتونید کلاس، متود، Property و ... رو در برنامه Comment کنید. طبیعتا این Comment کردن نمیتونه بسیار هوشمندانه باشه، چون ماشین حقیقتا نمیدونه کارکرد یک متود چیست و چه هدفی رو دنبال میکنه که بخواد توضیح مناسبی برای اون ارائه کنه. اما میتونه بنیان مورد نیاز شما رو برای نوشتن Comment ها محیا کنه.

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

    پس از نصب GhostDoc (که حدودا 930KB حجم داره)، آیتم جدیدی تحت عنوان GhostDoc به منوی Tools اضافه میشه. پس از نصب برنامه و هنگامیکه Visual Studio رو باز میکنید، میتونید برای اولین بار اونو Config کنید (بطور مثال کلیدی تعیین کنید که هنگام زدن اون کلید، Comment مورد نظر روی تابع انتخاب شده نوشته بشه).

    کلاس Porsche رو مجددا مثال میزنم، اما اینبار Comment هایی که در این کلاس میبینید توسط GhostDoc ایجاد شده و من توش دست نبردم:

    /// <summary>
    ///
    /// </summary>
    public class Porsche : Car
    {
    /// <summary>
    /// Initializes a new instance of the <see cref="Porsche"/> class.
    /// </summary>
    /// <param name="brand">The brand.</param>
    /// <param name="color">The color.</param>
    public Porsche(string brand, System.Drawing.Color color)
    : base(brand, color)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="Porsche"/> class.
    /// </summary>
    /// <param name="brand">The brand.</param>
    public Porsche(string brand)
    : this(brand, System.Drawing.Color.Empty)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="Porsche"/> class.
    /// </summary>
    public Porsche()
    {
    }
    }


    البته شما باید حتما Comment نوشته شده رو بدقت بررسی کنید و تغییرات مقتضی رو در اون اعمال کنید، چون همونطور که عنوان کردم، این Comment ها توسط ماشین تولید میشه و طبیعتا دقیق نیست؛ اما میتونه کمک بسزایی در سرعت بخشیدن به نوشتن Comment ها داشته باشه.

    با این توضیحات، اکنون میتونیم به کاربرد دیگه این Comment ها اشاره کنیم. جناب مداح؟

  35. #35

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    ممنون از دوستان.
    به نظر من بهتره یه roadmap از بحث فعلی که "مستند سازی در سطح کد" هست ، ارائه بشه (تا بنده هم بتونم سوالای بحث بعدی رو از الان آماده کنم)
    در ضمن یکی از بیننده ها در تماسی که با اتاق فرمان داشتن ، خواستار بحث در مورد MVVM بودن، که نمی دونم آیا در قالب گفتگوی فنی شمارۀ 1 میشه در این مورد بحث کرد یا نه ؟
    و یک سوال هم دربارۀ این بحث: مستند سازی برنامه (نه در سطح کد ، بلکه همون چیزی که بهش راهنمای برنامه گفته میشه) تا چه حدی وظیفۀ برنامه نویس هست ؟ و این که آیا تیم تولید کنندۀ مستندات راهنما ، حتما باید با تیم برنامه نویسی در ارتباط باشند ؟ یا این که باید خودشون هم از برنامه نویسی سر در بیارن ؟
    We work in the dark, we do what we can, we give what we have.
    Our doubt is our passion and our passion is our task.
    The rest is the madness of art

  36. #36

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

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

    بطور مثال، در چرخه تولید نرم افزار (SDLC) به روش Lean (یا Agile)، برنامه نویس عملیات مستند سازی رو باید به تعویق بندازه، چون هنوز هیچ اطلاعاتی Fix نشده و سیستم مدام در حال تغییره. مهیا کردن مستندات بر اساس روش Agile باعث کاهش هزینه ها میشه چون دیگه برنامه نویس وقتش رو صرف نوشتن مستنداتی که در آینده ای بدلیل تغییرات در برنامه، تغییر خواهند کرد، نمیکنه. ثانیا خطر تغییر "مستندات طراحی" بسیار کاهش پیدا میکنه. شما ممکنه یک طرح رو بارها تغییر بدید چون ایده هاتون به مرور زمان تغییر میکنه. وقتی این اتفاق بیفته، خطر دوباره کاری نوشتن مستندات از بین میره.

    به همین دلیل، بهترین زمان نوشتن مستندات هنگامی هستش که پروژه پایان پذیرفته و شما کاملا از کارکرد سیستمی که در دست دارید، مطلع هستید. طبیعتا تهیه مستندات در انتهای پروژه معایبی نیز در پی داره:

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

    بنابراین بهتره توازنی بین زمان نوشتن مستندات و انجام پروژه برقرار کنیم تا به مشکلات فوق برنخوریم.

    آقای عسگری، گمان میکنم بهتره تا بحث مستند سازی رو در اینجا تموم کنیم، چون حقیقتا این مساله داره به خارج از حوزه ای که در اون صحبت می کردیم منتقل میشه. تنها نکته ای که در بحث مستند سازی باقی مونده، و با اجازه شما و آقای مداح میخوام اونو مطرح کنم، Extract کردن XML Document ها از درون کد و تولید فایل مستندات هستش. برای این منظور نرم افزارهای زیادی وجود داره که یکی از اونها NDoc هستش. متاسفانه این پروژه Open Source بالغ بر 5 سال هستش که دست نخورده رها شده و در نتیجه دیگه از اون استفاده نمیشه.

    در عوض، توسعه گران دسترسی به ابزاری معادل NDoc دارن، البته این بار این ابزار توسط مایکروسافت پشتیبانی میشه. نام این ابزار Sandcastle هستش. خوشبختانه این ابزار در ماه جاری میلادی (دقیقا 6 روز پیش و پس از دو سال از آخرین نسخه) به روز شده و از این آدرس در دسترسه.

    Sandcastle از .NET 4.0 پشتیبانی میکنه و بالغ بر 100 خطایی که در نسخه آخر اون بود، در نسخه جدید رفع شده.

    برای شروع، ابتدا Sandcastle رو دریافت و نصب کنید. پس از اتمام عملیات نصب، Visual Studio رو باز کرده و پروژه مورد نظرتون رو باز کنید. سپس روی نام پروژه Right-Click کرده و گزینه Properties رو انتخاب کنید. سمت چپ، آیتم Build رو انتخاب کرده و سمت راست، گزینه XML documentation file رو انتخاب کنید. با این تغییر، Comment هایی که در برنامه نوشته بودیم، تحت قالب یک XML در خروجی قرار میگیره (پس از اینکه پروژه رو مجددا Compile کردید).

    اکنون به شاخه ای که Sandcastle رو نصب کرده اید برید و از شاخه Examples، زیر شاخه Generic رو انتخاب کنید. اونجا فایلی تحت نام SandcastleGui.exe وجود داره. این برنامه اجرا کنید...

    نام Assembly مورد نظرتون رو در قسمت Assemblies پنجره باز شده وارد کنید. همینطور، نام فایل XML تولید شده رو (که بصورت پیش فرض در شاخه Debug برنامه قرار میگیره)، در بخش Comments اضافه کنید. در قسمت Options (سمت راست صفحه) نام فایل CHM مورد نظرتون رو انتخاب کنید. فایل CHM مورد نظر با این اسم ذخیره خواهد شد.

    اکنون کلید Build رو فشار داده و کمی منتظر بمونید.

    در نهایت، وقتی کار این Utility تموم بشه، با پیامی شبیه این مواجه خواهید شد:

    Created c:\Program Files\Sandcastle\Examples\App1Doc\vs2005\chm\App1D oc.chm, 107,986 bytes


    (من اسم App1Doc رو برای فایل CHM خودم انتخاب کرده بودم). اکنون به مسیر فوق برید و فایل CHM مزبور رو باز کنید. من این کار رو برای کلاس Porsche که در موردش صحبت کردیم انجام دادم و Snapshot ای از اون رو برای مشاهده شما اینجا قرار میدم:



    دقت کنید، چون این ابزار 6 روز پیش Update شده و در دسترس عموم قرار گرفته، هنوز Documentation های شسته و رفته ای نداره، بنابراین باید خودتون زمان بذارید و fبا جزییاتش آشنا بشید. تا هنگامی که مستندات این ابزار توسط مایکروسافت به روز نشده، می تونید از این آدرس برای کسب اطلاعات بیشتر استفاده کنید.

    در ضمن همونطوریکه شما فرمودید، بحث MVVM در قالب این گفتگو قرار نمیگیره، اگرچه استفاده از الگوهای طراحی همواره به نوشتن کد بهتر و خواناتر، کمک شایانی میکنه. بسیار خوب، آقای عسگری؟

    (توضیح اینکه هر چقدر معطل کردم تا پاسخ آقای عسگری رو دیرتر بدم تا بلکه پروژه Sandcastle Help File Builder (بر اساس اعلان قبلی) به روز بشه، این اتفاق نیفتاد و تصمیم گرفتم تا حضار رو بیشتر از این معطل نذارم. این پروژه وقتی به روز بشه، GUI ای مطابق NDoc به شما ارائه میکنه، که دقیق بوده و قابل استفاده هستش. GUI ای که من در فوق به اون اشاره کردم، فقط نمونه ای بود برای نمایش توانایی های Utility مزبور و هرگز جایگزینی برای NDoc محسوب نمیشه. تیم مربوطه مجددا پیش بینی کردن که این ابزار فردا برای Download محیا هستش).
    عکس های ضمیمه عکس های ضمیمه
    • نوع فایل: jpg 01.jpg‏ (83.6 کیلوبایت, 704 دیدار)
    آخرین ویرایش به وسیله mehdi.mousavi : سه شنبه 08 تیر 1389 در 23:57 عصر دلیل: اضافه کردن توضیحی در مورد MVVM

  37. #37

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    خب ، ممنون از آقای موسوی.
    آقای مداح ، لطفا اگر بحثی در زمینۀ مورد بحث جاری باقی مونده بفرمایید ، تا بعد از شما تریبون رو به دست آقای موسوی سپرده و گفتگوی فنی شمارۀ یک رو به اتمام برسونیم
    We work in the dark, we do what we can, we give what we have.
    Our doubt is our passion and our passion is our task.
    The rest is the madness of art

  38. #38

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

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

    اشتباه 1 - Indentation نامناسب ممکن است شما را فریب دهد!
    قطعه کد زیر را در نظر بگیرید:

    if (condition == true)
    doSomething();

    حالا فرض کنید من می خواهم در صورت برقراری شرط متد دیگری هم فراخوانی شود:

    if (condition == true)
    doSomething();
    doSomethingElse();

    Oops! ، چی شد؟ ممکن است یک برنامه نویس یک چند دقیقه ای با این کد کلنجار برود و فکر کند که در صورت بر قراری شرط هر دو متد اجرا می شوند، اگر من این کد را با ابزار StyleCop که آقای موسوی معرفی کردند، آنالیز کنم، با این Warning مواجه خواهم شد،

    The body of the if statement must be wrapped in opening and closing curly brackets.

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

    if (condition == true)
    {
    doSomething();
    }
    doSomethingElse();

    خوب به نظر شما چرا این مطلب عنوان شد؟ با اینکه خیلی ساده به نظر می رسد؟
    قصدم تکیه بر این موضوع بود که همیشه سعی کنید "دقت" خود را بالا ببرید، همچنین طوری کد بنویسید که وقتی به آن نگاه می کنید، نیاز به فکر کردن کمتری داشته باشید، خوب اینطور کد نوشتن هم نیازمند عمل به روش هایی است که سعی کردیم در این گفتگو به آن ها بپردازیم،

    اشتباه 2 - جادوی Copy & Paste کار دست آدم می دهد
    "شما نباید تنها به این دلیل که یک فطعه کد بر روی اینترنت است، آن را در سیستم تولید خود Cut & Paste کنید، آیا آدامسی که در خیابان پیدا کنید را می جوید؟"
    جمله ی بالا از جناب Scott Hanselman نقل شده است که متن کامل آن را در اینجا قرار می دهم:

    Scott's Rule of Programming - Rule# 0x3eA
    Just because code is on the Internet doesn't mean you should cut and paste it into your production system. Do you chew gum you find on the street? Give code you find on the 'NET the same amount of attention you'd give advice scrawled on a public bathroom wall.
    بارها در همین انجمن برنامه نویس مشاهده کردیم که شخصی کدی را از یک سایت معتبر دریافت کرده و به دلیل اینکه نتیجه ی دلخواه خود را از آن کد نگرفته است، اقدام به طرح سوال می کند، کمی که موضوع را بررسی می کنید، متوجه می شوید که این شخص اصلا" دقتی در کد مربوطه نداشته و فقط زحمت Copy & Paste را کشیده است، هر قطعه کدی هر چقدر هم که به نیازمندی های شما نزدیک باشد، باز هم باید برای قرار گرفتن در محیط تولید و پروژه ی شما مورد بازبینی قرار گیرد و در صورت لزوم، برای تطابق آن با شرایط موجود، اصلاح گردد، قبلا" هم گفته شد که برنامه نویسی یک فعالیت "مستمر" است، قطعه کدی که الان و فقط در زمان حاضر نیاز شما را برطرف کند، لزوما" فطعه کد خوبی نیست و ممکن است در آینده شما را با مشکلاتی روبرو کند، پس باید همیشه پیش از قرار دادن یک قطعه کد در پروژه خود، به دقت آن را بررسی کرده و با اطمینان کامل از آن استفاده کنید،
    گاهی اوقات هم به دلیل رعایت نکردن Code Reusability، برنامه نویس، یک قطعه کدی را که در جایی از برنامه نوشته بوده، به محل جدیدی کپی کرده و قصد دارد که با تغییر دادن مقدار تنها چند متغیر، همان عملکرد را پیاده سازی کند، خوب این کار را انجام می دهد و می بیند که کد در محل جدید و با مقادیر جدید نتیجه دلخواه را نمی دهد، بعد از مدت زیادی بالا پایین کردن کد، متوجه می شود که مقدار یکی از متغیرها را تغییر نداده است و اینجاست که برنامه نویس "خطا" کرده است، تنها به خاطر اینکه تصور می کرد با کمی زرنگی می تواند در وقت صرفه جویی کند، اما با اینکار هم وقت و انرژی که می شد صرف انجام فعالیت مفیدتری شود به هدر رفت و هم اینکه یکی از اصول اساسی در کد نویسی که به آن اشاره کردم را نقض کرده است که مشکلاتی را به همراه دارد، پس یکی از جاهایی که نیاز مبرم به Code Refactoring احساس می شود، همین جاست،

    اشتباه 3 - Overuse نکنید
    یکی از مزایای قوائد و اصولی که در برنامه نویسی وجود دارد که ما به بخشی از آن ها اشاره کردیم، برای نظم بخشیدن بیشتر می باشد، گاهی اوقات اصل پروژه و الگوریتم به کل از یادمان می رود، از بس که غرق در این اصول می شویم و فقط بیشتر به "پیچیدگی" کار اضافه می کنیم تا اینکه بخواهیم آن را سازماندهی نماییم، این الگوها زمانی مفید خواهند بود که "در جای خود و متناسب با نیاز" از آن ها استفاده کنیم،

    راستش را بخواهید، از این اشتباهات، زیاد وجود دارد، حتما" سعی خواهم کرد که در آینده بیشتر به آن ها بپردازم،

    خوب، منتظر شنیدن صحبت های پایانی آقای موسوی هستیم،

    اوقات خوشی را برای شما آرزو می کنم و منتظر شنیدن نظرات شما هستم،

    امیدوارم که دوستان از این گفتگو لذت برده باشند، از آقای موسوی و آقای عسگری هم تشکر و قدردانی می کنم،

    ،/
    آخرین ویرایش به وسیله علیرضا مداح : جمعه 25 تیر 1389 در 17:55 عصر
    I've just started tweeting!
    @Alireza_Maddah

  39. #39

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

    ممنون آقای مداح. آقای موسوی شما آخرین پست این بحث رو هم بنویسید تا گفتگوی فنی شمارۀ یک تموم بشه.
    از هر دو مهمان عزیز ممنونم.
    We work in the dark, we do what we can, we give what we have.
    Our doubt is our passion and our passion is our task.
    The rest is the madness of art

  40. #40

    نقل قول: گفتگوی فنی شماره یک - اصول و قواعد کد نویسی

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

    نقل قول نوشته شده توسط Saeed_m_Farid مشاهده تاپیک
    2) Divergent Change (تغییر واگرا) و Shotgun Surgery رو میشه بیشتر توضیح بدین؟ من نوعی تو برخی کدهام، فکر میکنم که درست تر از این دیگه نمیشه و از زمان و وقتم هم هزینه میکنم و دنبال کدهای تمیز مشابه و قابل استفاده برای نمونه می گردم و ... ولی باز هم - علیرغم دقت اولیه- طی زمان همچین مشکلاتی شبیه اون چیزی که آقای موسوی فرمودن برام پیش میاد. اینا دیگه Refactoring نیستند که تو توسعه نهایی سیستم تاثیری نداشته باشند؛ باید هزاران خط کد از اول بهینه سازی بشه یا دید توسعه دهندگان سیستم عوض بشه و بردارن تمام اجداد کلاسها رو بهینه نویسی کنند که اونهم معلوم نیست بازم Refused Bequest بشن یا نه! اینا واقعاً تو پروژه هایی که پرونده شون مثلاً بسته شده و تغییرات جزئی میخوان هزینه بر و اعصاب خوردکن هست؛ چه راه حلی پیشنهاد می کنید؟ یا بنظرتون از این به بعد چی کار کنیم بهتره؟
    ببینید، ما کدمون رو همواره طوری می نویسیم (یا باید بنویسیم) که بشه براحتی تغییرات رو روش اعمال کرد. وقتی می خواهیم تغییری در سیستم بدیم، حالت بهینه اینه که به سرعت بتونیم نقطه ای در برنامه رو نشون بدیم که نیاز به تغییر داره. منظورم تنها یک نقطه هستش. وقتی قادر نباشیم اینکارو کنیم، باید نگران طرح پیاده سازی شده باشیم. Divergent Change شرایطی هستش که شما برای اعمال یک تغییر باید تو چندین نقطه از برنامه دست ببرید (و این خطرناکه، چرا که ممکنه یکی از نقاط مورد نظر که نیاز به تغییر داشته رو از قلم بندازیم). در واقع چنین کدی کد Error-Prone ای هستش (یعنی کد به نحوی نوشته شده که بالقوه خطر آفرین هستش). در این شرایط بهترین کار این هستش که اون کلاس رو به کلاسهای دیگه تقسیم کنیم به نوعی که هر کلاس فقط و فقط یک کار رو انجام بده و هر تغییر فقط در یک نقطه انعکاس پیدا کنه.

    در مقابل، تصور کنید شرایطی رو که تغییر در یک کلاس منجر به تغییر در چندین کلاس متعدد بشه (Shotgun Surgery). خوب، این وضعیت نشون میده قانون SRP در نوشتن برنامه نادیده گرفته شده و کلاسها به همدیگه وابسته هستن. در ادامه در مورد SRP صحبت خواهم کرد. اینکه چطور می تونیم از این شرایط دوری کنیم، بخشیش به داشتن این اطلاعات بر میگرده و بخشیش به تجربه. در طول فرآیند توسعه نرم افزار، کدها باید مدام Refactor بشن تا در نهایت به نقطه Stable ای برسن. چون توسعه در طی زمان رخ میده، طی این زمان (و اضافه شدن قابلیتهای جدید) هستش که برنامه نویس متوجه میشه کدوم بخش از کد رو باید چگونه Refactor کنه. اگر عمل Refactoring زود زود انجام بشه، دیگه با انبوهی کد مواجه نخواهیم بود که یک تغییر ساده، نیاز به زیر و رو کردن کل سیستم داشته باشه.

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

    بطور کلی، نوشتن یک کد خوب هرگز محدود به رعایت قوانین نام گذاری، سپردن هر Functionality به بک تابع خاص یا خوب نوشتن Comment ها در برنامه نمیشه. برای اینکه یک کد خوب بنویسیم، باید به اصول شیء گرایی، بهترین الگوها و ... اشراف داشته باشیم. این اشراف بعلاوه مطالبی که تا به حال ذکر شد، می تونه تواما ما رو به نوشتن یک کد خوب رهنمون بشه.

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

    احتمالا تا به حال با برخی از این واژه ها در دوران کاری خودتون، روبرو شده اید: SRP، OCP، LSP، ISP و DIP. اینها بخشی از قوانینی هستن که شما رو در نوشتن یک کد خوب، یاری میکنن. اجازه بدید تا اندکی در مورد هر یک از این مفاهیم توضیح بدم.

    • واژه SRP یا Single Responsibility Principle به این معناست که فقط و فقط باید یک وظیفه رو دوش کلاس گذاشته بشه. وقتی شما کلاسی به اسم Car ایجاد می کنید، طبیعتا باید کلیه متودها، Property ها و Member Field های کلاس مزبور همگی در یک راستا نوشته بشن و نباید آهنگ نوشتن یک کلاس رو با معرفی متودی نامتناسب در کلاس مزبور بهم بزنید. به بیان دیگه، هر کلاس باید تنها و تنها به یک دلیل تغییر کنه. نیاز به تغییر یک کلاس به دو دلیل مجزا در دو برهه زمانی متفاوت، بیانگر این مساله هستش که این کد نیاز به Refactoring داره و در واقع باید در دو ماژول یا دو کلاس جداگانه تعریف بشه.
    • واژه OCP، یا Open Closed Principle به این معناست که شما باید بدون دست بردن در یک کلاس، توان توسعه اون کلاس رو داشته باشید. در واقع کدی خوبه که توسعه پذیر باشه. به بیان دیگه، یک کلاس (یا یک ماژول) باید برای توسعه پذیر بودن دست برنامه نویس رو باز بذاره، در عین حال در برابر تغییرات بیرونی کاملا بسته و محدود باشه.
    • LSP یا Liskov Substitution Principle به این معناست که شما باید کلاسهای Derived رو طوری بنویسید که در صورت نیاز بتونید اونها رو با Base Class خودتون عوض کنید، بدون اینکه تغییری در کلاسهای دیگه بوجود بیارید! فهم عمیق این مساله، مستلزم دونستن مفاهیم Contravariance، Covariance، Invariant و ... هستش، که در حال حاضر از حوصله این گفتگو خارجه.
    • ISP یا Interface Segregation Principle به این معناست که وقتی Interface شما در طول فرآیند توسعه نرم افزار فربه شدش، باید اون Interface رو به Interface های دیگه تقسیم کنید تا Client هایی که از اون Interface اولیه استفاده می کردن، اما نیازی به بسیاری از متودهاش ندارن، مجبور به پیاده سازی اجباری اونها نباشن. اگر دقت کنید، یکی از دلائل بوجود اومدن Interface های نسخه 2 در Windows SDK همین بوده. اگر متودهای جدید، روی اینترفیس قبلی قرار میگرفتن، اونوقت (علاوه بر بوجود اومدن مشکلات عدیده مهمتری که بی ارتباط با این موضوع هستش) Client های قدیمی مجبور بودن تا متودهای جدید Interface جدید رو نیز پیاده سازی کنن، در صورتیکه به اونها نیازی ندارن.
    • DIP یا Dependency Inversion Principle عنوان میکنه که اولا ماژولهای سطح بالا نباید به ماژولهای سطح پایین گره بخورن، بلکه هر دو باید بر اساس لایه Abstraction ای بنا بشن (مثل HAL در ویندوز). ثانیا، این Abstraction ها نباید به جزییات گره بخورن، بلکه جزییات نیز باید بر اساس abstraction ها پایه ریزی بشن. دونستن این مفاهیم و بکار بردن اونها هنگام طراحی کلاسها، بدون شک به شما کمک میکنه تا کدتون در آینده کمتر نیاز به Refactoring داشته باشه و در نتیجه، خواناتر و بهتر باشه.

    مسائلی که مطرح کردم، یک کد خوب رو از دید طراحی کلاسها مد نظر قرار میده. اما کد خوب، از دید Package Cohesion (پیوستگی در سطح Package) نیز باید مورد توجه قرار بگیره.
    RREP، CCP و CRP سه تا از مهمترین قوانین درگیر با شکستن کد در سطح Package هستن:

    • CCP به این معناست که کلاسهایی که با یکدیگر تغییر می کنن، باید در یک Module قرار بگیرن. به این ترتیب هنگامیکه نیاز به اعمال تغییر در یک کلاس باشه، دیگه نیازی نیست تا Module های دیگه به روز بشن و با بهنگام سازی همون ماژولی که حاوی Class مورد نظر هستش، سیستم نرم افزاری ارتقاء پیدا خواهد کرد. به این مساله Common Closure Principal یا CCP میگن.
    • منظور از CRP یا Common Reuse Principle، بسته بندی کلاسها در یک Module هستش هنگامیکه اون کلاسها در کنار یکدیگر استفاده بشن. فرض کنید در حال نوشتن برنامه ای همانند Paint هستیم. این قانون ما رو ملزم میکنه تا کلاسهای Circle، Line، Polygon و ... رو که همگی در کنار یکدیگر استفاده خواهند شد رو در یک Package قرار بدیم.
    • RREP یا Release Reuse Equivalency Principle به ما میگه که اگر قراره یک کد بطور مناسبی مورد استفاده مجدد قرار بگیره، این کد باید بصورت یه Black Box عرضه بشه تا بشه بدون درگیر شدن با جزییات پیاده سازیش، از اون استفاده کرد. نه اینکه دل و روده کد ما در اختیار استفاده کننده باشه و اون نیز به نوبه خودش بتونه در کد دست ببره و تغییرات مورد نظرش رو ایجاد کنه. در واقع Component مورد نظر باید بصورت یک Library در اختیار استفاده کننده قرار بگیره و تغییرات درونی این Library از دید استفاده کننده کاملا پنهان باشه.

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

    قانون KISS، یا Keep it simple, stupid به این معناست که هر چه کد رو ساده تر نگه دارید، کدتون خواناتر و بهتره. در برابر، این قانون شما رو ترغیب به استفاده مجدد از کلاسها و کدهای موجود میکنه. بر این اساس، شما هر چه "داده های کمتری" رو در برنامه خودتون لمس کنید، Performance برنامه شما بالاتر خواهد رفت. این مساله فقط محدود به کد نویسی نمیشه! بطور مثال هنگامیکه دارید جدولی رو برای بانک RDBMS خودتون طراحی می کنید، این سوال رو از خودتون بپرسید که "آیا میتونم مساله رو خیلی متناسب بدون طراحی این جدول، حل کنم؟" و اگر پاسخ مثبت بود، حتما اینکارو کنید. جدول جدید، یعنی نیروی کار اضافی. یعنی پیچیدگی در لایه های دیگه نرم افزار و ... بنابراین KISS!

    یکی دیگه از مسائلی که اینجا باید بهش اشاره کرد، تحلیل و یافتن منشاء اصلی مشکل هنگامی هستش که با یک خطا مواجه میشیم. همونطور که مستحضرید، در سایت برنامه نویس عموما سوالاتی مطرح میشه که فرسنگها از سوال اصلی فاصله گرفته. من وقتی تو برنامه خودم بواسطه دسترسی غیر مجاز از یک Thread به UI Control ای که در Thread دیگه ای ایجاد شده، خطای InvalidOperationException رو میگیرم، باید ببینم این خطا به چه معناست و چرا بوجود اومده نه اینکه با Set کردن یه Flag در کلاسم روی این خطا در پوش بذارم. وقتی شما این بدنبال منشاء خطا نمیرید، اون خطا جای دیگه و به طریق دیگه یقه شما رو میگیره. اینها رو گفتم تا با Root Cause Analysis آشنا بشید و در ادامه صحبتهام، مسیری به Exception ها باز کرده باشم.

    در ابتدای صحبتهام در این گفتگو، خدمتتون عرض کردم که تعامل با خطاها بخشی از نگارش یک کد خوب رو تشکیل میده. بعنوان یک قانون کلی، شما باید Exception های خاص رو بگیرید. علاوه بر اینکه نوشتن catch(Exception) بدلیل Corrupted State Exceptions بیشتر از اونی که تصور کنید خطرناکه، بلکه اینکار نیز گذاشتن در پوش روی خطایی هستش که رخ داده و همونطور که گفتم بعدا تو یه بازه زمانی دیگه (در runtime) به سراغتون میاد و شما رو به زانو در میاره.

    شما از Exception ها نباید برای کنترل جریان برنامه استفاده کنید. به عنوان نمونه جای اینکه از DateTime.TryParse برای Parse کردن یک رشته استفاده کنید، بیایید و رشته مورد نظر خودتون رو Parse کنید، بعد یه catch بذارید که اگر throw کردش متوجه بشید که رشته مزبور، تاریخ نبوده! این کار بر اساس الگوها، نه تنها اشتباه هستش، بلکه لطمه شدیدی به Performance برنامه میزنه.

    بسیار خوب. با در دست داشتن این اطلاعات، اجازه بدید تا بصورت خیلی گذرا نیز نگاهی به Unit Testing و تاثیر اون روی نوشتن یک کد خوب داشته باشیم. چون هیچ گفتاری در مورد نوشتن یک کد خوب، بدون صحبت کردن در مورد Unit Test کامل نیست.

    Unit Test چیه و هدف از نوشتن این Unit ها چی هستش؟ Unit Test به مجموعه توابعی گفته میشه که صحت اجرای بخش کوچکی از سیستم نرم افزاری در حال توسعه رو مورد آزمایش قرار میده. این توابع ممکنه توسط خود برنامه نویس نوشته بشه و میتونه در شرکتهای بزرگتر، به Tester ها سپرده بشه. اما چرا بی جهت وقت بذاریم همچین Unit هایی بنویسیم؟ چرا وقتی رو که میتونم صرف نوشتن یک Unit Test کنیم، صرف Production Code نکنیم؟

    سیستم نرم افزاری ای رو در نظر بگیرید که به مرور زمان تغییراتی در این سیستم رخ میده. کدی در Base Class ای تغییر میکنه، مقدار یک Constant کم و زیاد میشه و ... تا به سمت بهینه شدن پیش بره. هر کدوم از این تغییرات، ممکنه بخش مربوطه، یا بخشهایی دیگه از این سیستم رو از کار بندازه و مستلزم صرف ساعتها وقت برای آزمایش و بررسی کد، پس از اعمال هر تغییر کوچک در برنامه هستش. برای جلوگیری از چنین شرایطی و عدم مواجه با شرایط از پیش تعیین نشده، بهتره تا Unit Test هامون رو یک گام جلوتر از Production Code بنویسیم. بله! درست شنیدید. Test Code دقیقا به همون اندازه Production Code اهمیت داره، و باید در موردش فکر بشه و با برنامه انجام بشه.

    توسعه Unit Test ها خودش به شیوه های متفاوتی انجام میگیره: TDD، DDT، POUTing و ... توضیح هر یک از این روشها از حوصله این گفتگو خارجه. فعلا در این حد به خاطر بسپارید که کد خوب، کدی هستش که آزمون پذیر باشه و از قانون FIRST طبعیت کنه. FIRST حروف اولیه این عبارات هستن:

    • Fast: آزمایش باید سریع انجام بگیره. اگر آزمایش زمان بر باشه، احتمالا علاقه به اجرای اون کم و کمتر میشه و این خودش نوشتن Unit Test ها رو در هاله ای از ابهام فرو میبره.
    • Independent: آزمایشها نباید به یکدیگر وابسته باشن و هر آزمایشی باید بطور مجزا قابل انجام باشه.
    • Repeatable: آزمایشات باید در محیطهای اجرایی متفاوت، قابل تکرار باشن تا بشه اونها رو دائما در فواصل مختلف و در شرایط اجرایی متفاوت، اجرا کرد.
    • Self-Validating: آزمایشات باید یه مقدار Boolean برگردونن، True یا False، اولی به این معنا که آزمایش با موفقیت انجام شد و دومی یعنی اینکه آزمایش پاس نشده.
    • Timely: آزمایشات باید از نظر زمانی، حتما جلوتر از Production Code نوشته بشن، چرا که اگر این اتفاق نیفته ممکنه شما قادر به نوشتن Unit Test (بدلیل پیچیدگی Production Code) نباشید.

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

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



    (اگر منبعی رو فراموش کرده باشم، بعدا به این پست اضافه خواهم کرد).

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

    موفق باشید.

صفحه 1 از 2 12 آخرآخر

برچسب های این تاپیک

قوانین ایجاد تاپیک در تالار

  • شما نمی توانید تاپیک جدید ایجاد کنید
  • شما نمی توانید به تاپیک ها پاسخ دهید
  • شما نمی توانید ضمیمه ارسال کنید
  • شما نمی توانید پاسخ هایتان را ویرایش کنید
  •