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

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

  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

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

    من کدهای بد بوی بسیار زیادی نوشتم (عموما در C++‎) تا اینکه تصمیم گرفتم کاری که انجام میدم اصولی و قاعده مند باشه. نوشتن کدهای بد بو هستش که برنامه نویس رو به نقطه ای میرسونه که از خودش بپرسه "آیا الان فلانی، توی فلان شرکت معروف دنیا، داره به همین روش من کد می نویسه؟" و پاسخ به این سوال هستش که روند کد نویسی برنامه نویس رو تغییر میده. (قوانین فیزیک رو میدونستم، اما کم دوچرخه سواری کرده بودم).

    برنامه مورد نظر شما ممکنه یک هفته زمان ببره که نوشته و تکمیل بشه، اما مهم تر از اون اینه که من تا کی باید با اون برنامه زندگی کنم و Life Cycle اون برنامه به چه میزان هستش.

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

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

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

  17. #17

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

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

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

  18. #18

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

    نقل قول نوشته شده توسط mehdi.mousavi مشاهده تاپیک
    ممکنه من یک هفته (بطور مثال) زمان بذارم و کدی بنویسم، اما اگر قرار باشه اونو برای مدت زمان مدیدی پشتیبانی کنم، طبیعتا ترجیح میدم کد مربوطه رو Refactor کنم و اصولی بنویسم (البته این شامل بخش اول صحبتهام، یعنی استفاده از اسامی معنادار و ... نمیشه. الان فقط در مورد Refactoring صحبت میکنم).
    برخی دوستان از پشت دوربین بهم اشاره کردن که این چه حرفیه میزنی. یعنی تو یه پروژه یه هفته ای، اسم متغیرها رو هر چی خواستی میذاری؟

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

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

  19. #19

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

    اجازه بدید دو مورد از سوالات کاربران رو عینا نقل کنم تا بهشون جواب داده بشه
    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

  20. #20

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

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

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

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

    1. Greenfield Development
    2. Brownfield Development


    • Greenfield Development یعنی اینکه یا "هیچ نرم افزاری برای مکانیزه شدن فلان کار وجود نداره"، یا "برنامه ای وجود داره که خوب نوشته شده و قادر هستیم با تغییراتی جزیی، اونو نگهداری کنیم یا قابلیتهای جدیدی به اون بیفزاییم". به بیان دیگه، هر برنامه ای که Re-Modeling نخواد در این بخش قرار میگیره. این اسم از کجا انتخاب شده؟ این اسم در واقع از فیلد کشاورزی به این عرصه وارد شده. وقتی شما یه زمین سرسبزی دارید، میتونید اونو برای کشت محصول مورد نظر خودتون اونطور که مایلید تغییر بدید و شروع به ساخت و ساز در اون کنید.



    • Brownfield Development در مقابل، یعنی "پروژه ای داریم که نیاز به Re-Modeling داره"، "بد نوشته شده و تیم دیگه ای قادر نیست قابلیتهای جدیدی به اون اضافه کنه یا اضافه کردن قابلیتهای جدید، بخاطر ساختار فعلی بسیار دشواره" و ... این اسم از کجا انتخاب شده؟ توی مهندسی عمران، Brownfield Development یعنی ساخت و ساز ملکی در منطقه ای که در حال حاضر اونجا ملک وجود داره و بنای جدید باید بر اساس بناهای موجود قبلی ساخته بشه و بالا بره.


    اینطور که بنظر میرسه شما روی گونه دوم توسعه تمرکز کرده اید. برای این منظور، چند کتاب وجود داره که به روشهای توسعه Brownfield میپردازه. Kyle Baley و Donald Belcham قبلا کتابی در مورد چگونگی توسعه Brownfield در محیط .NET تحت عنوان Brownfield Application Development in .NET منتشر کردن که در اون روشهای صحیح انجام اینکار توضیح داده شده. بخش 6 و 9 کتاب بصورت رایگان در اختیار عموم قرار داره. خوندن این کتاب برای افرادیکه در حرفه اشون به Brownfield App Development مشغولن رو توصیه میکنم.

    سؤال دوم من در مورد متغیر شمارنده در حلقه ها هست. آقای موسوی از انتخاب نام هایی مثل i و j و k و ... به عنوان ویژگی های یک کد کثیف یاد کردند (البته به نقل از یک کتاب). اگر اینطور هست، پس اصلاً چطور شد که i و j و ... برای همه برنامه نویسان همیشه یادآور متغیر شمارنده هست و چرا اینقدر شیوع پیدا کرده؟
    نه. اونجا منظور من این نبود که چون اسامی متغیر های استفاده شده در حلقه i، j و k بوده کد کثیف شده بود. خیر. من عرض کردم "سه حلقه تو در تو، که سه الی چهار وجب کد داخل درونی ترین حلقه نوشته شده، اسامی متغیرهای حلقه i، j و k بوده..."... دقت کنید. سه الی چهار وجب کد، میشه چند صفحه که باید Scroll بشه. چون سه تا حلقه تو در تو نیز استفاده شده بوده، پیچیدگی کد بشدت افزایش پیدا کرده بود و خواناییش بشدت افت. استفاده از حروف تک حرفی برای حلقه ها، این پیچیدگی رو بشدت افزایش داده بود.

    استفاده از حروف تک حرفی (غیر از l و O که با 1 و صفر اشتباه گرفته میشه) توی حلقه ها هیچ مشکلی نداره، بشرطیکه Scope استفاده از اونها بسیار کوچک باشه و هیچ اسم دیگه ای با اونها تداخل نداشته باشه. اگر کد شما دارای این دو شرط بود، براحتی و بدون نگرانی میتونید از i و j و ... برای شمارنده حلقه استفاده کنید. کدی که من توصیف کردم، حقیقتا وحشتناک بود و براحتی میشد با یک Map اونو به 4، 5 خط تقلیلش داد.

    فکر نمی کنید نوشتن کد به روشی که شما می فرمایید کمی سخت و خسته کننده باشه؟ مثلا در حلقه ی for به جای استفاده از i,j,k از متغیر های ColomNum,RowNom ئ امثال اینها که خیلی طولانی هستند استفاده کنیم! آیا این روش سخت تر نیست؟ آیا شما به عنوان یک برنامه نویس با تجربه می تونید بگید که بعد از مدتی به این سختی عادت میکنیم؟
    پاسخ به این سوال رو الان دادم. ممنون از توجهتون...

  21. #21

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

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

  22. #22

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

    من یک مبحث هم مد نظرم هست برای این گفتگو: مستند سازی کد
    منتها قبل از ورود به این مبحث ، شما اگه حرفی در ادامۀ مبحث نام گذاری و کد اصولی و 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

  23. #23

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

    ممنونم از آقای عسگری و به خاطر وقفه ای که رخ داد، عذرخواهی میکنم. بسیار خوب. اجازه بدید تا به مساله 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 رو نیز باز کنه. اگر آقای عسگری اجازه بدن، چند مثال در این زمینه بزنم (من هنوز در مورد کدهای بودار، حرفهای ناگفته بسیاری دارم).

  24. #24

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

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


    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 دیدار)

  25. #25

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

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


    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

  26. #26

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

    قصد دارم بر روی مبحث 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

  27. #27

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

    خب ، ابتدا آقای مداح لطفا یک توضیح کوتاهی در مورد 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

  28. #28

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

    همانطور که می دانید هر 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

  29. #29

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

    ممنون بابت توضیح
    البته بعد ها گفتگو های فنی تری در این زمینه ها خواهیم داشت ولی فکر کنم فعلا کافیه.
    لطفا به سوال هام در مورد مستندات و کامنت گذاری جواب بدید تا بریم و نظر آقای موسوی رو در این زمینه ها جویا بشیم
    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

  30. #30

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



    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

  31. #31

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

    آقای موسوی ، خواهش می کنم شما هم در این مورد صحبت کنید تا وارد جزییات بیشتر این بحث بشیم
    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

  32. #32

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

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


    • 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 یک تابع برای خوانایی بیشتر

  33. #33

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

    بله ، منظور من کامنت های درون کد بودن.
    آقای مداح ، نمی دونم این بحث کامنت ها رو شما چقدر جلو خواهید برد (با توجه به این که دیگر وجوه مستندات و ... را در گفتگو های بعدی بررسی خواهیم کرد) چون با ذکر 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

  34. #34

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

    خوب، قبل از اینکه وارد بحث 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

  35. #35

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

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

    احتمالا در حین این گفتگو، این سوال در ذهن شما نقش بسته که "حالا که من تو یک تیم 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 دیدار)

  36. #36

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

    حوب ابتدا از تاخیر به وجود آمده پوزش می خواهم، سعی می کنیم که از این به بعد بحث با سرعت مناسبی پیگیری شود،
    شروع می کنیم،
    بهره گیری از ابزار ارزشمند 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

  37. #37

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

    واقعیت اینه که در این زمینه حرف قابل عرضی باقی نمونده... البته فقط یک نکته به ذهنم میرسه و اونم تولید ماشینی این 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 ها اشاره کنیم. جناب مداح؟

  38. #38

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

    ممنون از دوستان.
    به نظر من بهتره یه 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

  39. #39

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

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

    بطور مثال، در چرخه تولید نرم افزار (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

  40. #40

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

    خب ، ممنون از آقای موسوی.
    آقای مداح ، لطفا اگر بحثی در زمینۀ مورد بحث جاری باقی مونده بفرمایید ، تا بعد از شما تریبون رو به دست آقای موسوی سپرده و گفتگوی فنی شمارۀ یک رو به اتمام برسونیم
    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

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

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

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

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