Skip to content

ابزارهای پایه

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

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

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

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

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

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

برای چسباندن بخش زیادی از جادو به هم به چسب نیاز دارید. در دستکاری متن، برخی امکانات مثل awk، Perl و Python را بررسی می‌کنیم.

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

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


۱۴ — قدرت متن ساده

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

متن ساده چیست؟

متن ساده از نویسه‌های قابل چاپی تشکیل شده که به‌گونه‌ای است که مستقیماً توسط انسان قابل خواندن و فهم است. برای مثال، اگرچه قطعهٔ زیر از نویسه‌های قابل چاپ تشکیل شده، بی‌معناست:

Field19=467abe

خواننده هیچ ایده‌ای ندارد که 467abe چه معنایی دارد. انتخاب بهتری این است که آن را برای انسان قابل فهم کنیم:

DrawingType=UMLActivityDrawing

متن ساده به این معنا نیست که متن بدون ساختار است؛ XML، SGML و HTML نمونه‌های عالی متن ساده با ساختار مشخص‌اند. با متن ساده می‌توانید هر کاری را که با قالب دودویی می‌توانستید انجام دهید — از جمله نسخه‌بندی.

متن ساده معمولاً در سطح بالاتری از رمزگذاری دودویی خالص قرار دارد که معمولاً مستقیماً از پیاده‌سازی مشتق می‌شود. فرض کنید می‌خواهید ویژگی‌ای به نام uses_menus را ذخیره کنید که می‌تواند TRUE یا FALSE باشد. با متن ممکن است این‌طور بنویسید:

myprop.uses_menus=FALSE

این را با 0010010101110101 مقایسه کنید.

مشکل بیشتر قالب‌های دودویی این است که زمینهٔ لازم برای فهم داده از خود داده جداست. شما به‌طور مصنوعی داده را از معنایش جدا می‌کنید. داده می‌تواند به‌خوبی رمزگذاری شده باشد؛ بدون منطق برنامه برای تجزیهٔ آن کاملاً بی‌معناست. با متن ساده اما می‌توانید جریان دادهٔ خودتوصیفی به دست آورید که مستقل از برنامه‌ای است که آن را ایجاد کرده.

TIP 20

دانش را در متن ساده نگه دارید

معایب

دو معیب عمده در استفاده از متن ساده وجود دارد: (۱) ممکن است فضای بیشتری برای ذخیره نسبت به قالب دودویی فشرده بگیرد، و (۲) ممکن است از نظر محاسباتی تفسیر و پردازش فایل متن ساده گران‌تر باشد.

بسته به برنامهٔ شما، یکی یا هر دو این موارد ممکن است غیرقابل‌قبول باشند — مثلاً هنگام ذخیرهٔ داده‌های تله‌متری ماهواره، یا به‌عنوان قالب داخلی پایگاه دادهٔ رابطه‌ای.

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

برخی توسعه‌دهندگان ممکن است نگران باشند که با قرار دادن فراداده در متن ساده، آن را در معرض دید کاربران سیستم قرار می‌دهند. این ترس بی‌جا است. دادهٔ دودویی ممکن است مبهم‌تر از متن ساده باشد، اما امن‌تر نیست. اگر نگران دیده شدن رمزهای عبور توسط کاربران هستید، آن‌ها را رمزنگاری کنید. اگر نمی‌خواهید پارامترهای پیکربندی را تغییر دهند، یک هش امن¹ از تمام مقادیر پارامترها را در فایل به‌عنوان checksum قرار دهید.

قدرت متن

از آنجا که «بزرگ‌تر و کندتر» از پرتکرارترین ویژگی‌های درخواستی کاربران نیست، چرا با متن ساده درگیر شویم؟ مزایا چیست؟

  • بیمه در برابر کهنگی
  • اهرم
  • تست آسان‌تر

بیمه در برابر کهنگی

قالب‌های دادهٔ قابل‌خواندن برای انسان و دادهٔ خودتوصیفی از همهٔ قالب‌های دیگر داده و برنامه‌هایی که آن‌ها را ایجاد کرده‌اند بیشتر دوام می‌آورند. نقطه.

تا وقتی داده باقی بماند، شانس استفاده از آن را دارید — احتمالاً مدت‌ها پس از اینکه برنامهٔ اصلی که آن را نوشته منسوخ شده باشد.

می‌توانید چنین فایلی را با دانش جزئی از قالبش تجزیه کنید؛ با بیشتر فایل‌های دودویی باید تمام جزئیات کل قالب را بدانید تا با موفقیت آن را تجزیه کنید.

فایل داده‌ای از یک سیستم قدیمی² را در نظر بگیرید که به شما داده شده. دربارهٔ برنامهٔ اصلی کم می‌دانید؛ تنها چیزی که مهم است این است که لیستی از شماره‌های تأمین اجتماعی مشتریان را نگه می‌داشته و شما باید آن‌ها را پیدا و استخراج کنید. در میان داده‌ها می‌بینید:

<FIELD10>123-45-6789</FIELD10>
...
<FIELD10>567-89-0123</FIELD10>
...
<FIELD10>901-23-4567</FIELD10>

با شناخت قالب شمارهٔ تأمین اجتماعی، می‌توانید سریع برنامهٔ کوچکی بنویسید که آن داده را استخراج کند — حتی اگر هیچ اطلاعاتی دربارهٔ بقیهٔ فایل نداشته باشید.

اما تصور کنید فایل به‌جای آن این‌طور قالب‌بندی شده بود:

AC27123456789B11P
...
XY43567890123QTYL
...
6T2190123456788AM

شاید اهمیت اعداد را به‌آن‌راحتی تشخیص نمی‌دادید. این تفاوت بین قابل‌خواندن برای انسان و قابل‌فهم برای انسان است.

در ضمن، FIELD10 هم کمکی نمی‌کند. چیزی مثل:

<SSNO>123-45-6789</SSNO>

کار را بسیار ساده می‌کند — و اطمینان می‌دهد که داده از هر پروژه‌ای که آن را ایجاد کرده بیشتر دوام می‌آورد.

اهرم

تقریباً هر ابزار در جهان محاسبات — از سیستم‌های مدیریت کد منبع تا محیط‌های کامپایلر، ویرایشگرها و فیلترهای مستقل — می‌تواند روی متن ساده کار کند.

فلسفهٔ یونیکس

یونیکس به‌خاطر طراحی حول فلسفهٔ ابزارهای کوچک و تیز مشهور است؛ هر کدام برای انجام یک کار خوب ساخته شده‌اند. این فلسفه با استفاده از قالب زیربنایی مشترک — فایل متن سادهٔ خط‌محور — ممکن می‌شود. پایگاه‌های داده‌ای که برای مدیریت سیستم (کاربران و رمزها، پیکربندی شبکه و غیره) استفاده می‌شوند همگی به‌صورت فایل‌های متن ساده نگه داشته می‌شوند. (برخی سیستم‌ها مثل Solaris نسخهٔ دودویی برخی پایگاه‌ها را هم به‌عنوان بهینه‌سازی عملکرد نگه می‌دارند. نسخهٔ متن ساده به‌عنوان رابطی با نسخهٔ دودویی حفظ می‌شود.)

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

برای مثال، فرض کنید استقرار تولیدی یک برنامهٔ بزرگ با فایل پیکربندی پیچیدهٔ مختص سایت دارید (sendmail به ذهن می‌رسد). اگر این فایل متن ساده باشد، می‌توانید آن را زیر سیستم کنترل کد منبع قرار دهید (به کنترل کد منبع، صفحهٔ ۸۶ مراجعه کنید) تا به‌طور خودکار تاریخچهٔ تمام تغییرات را نگه دارید. ابزارهای مقایسهٔ فایل مثل diff و fc به شما اجازه می‌دهند یک‌نگاه ببینید چه تغییراتی انجام شده، در حالی که sum به شما امکان می‌دهد checksum تولید کنید تا فایل را برای تغییرات تصادفی (یا مخرب) پایش کنید.

تست آسان‌تر

اگر از متن ساده برای ایجاد دادهٔ مصنوعی برای اجرای تست‌های سیستم استفاده کنید، افزودن، به‌روزرسانی یا اصلاح دادهٔ تست بدون ساخت ابزار ویژه برای آن کار ساده است. به‌طور مشابه، خروجی متن ساده از تست‌های رگرسیون را می‌توان به‌سادگی تحلیل کرد (با diff مثلاً) یا با Perl، Python یا ابزار اسکریپت‌نویسی دیگری بررسی دقیق‌تری انجام داد.

کمترین مخرج مشترک

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

بخش‌های مرتبط:

  • کنترل کد منبع، صفحهٔ ۸۶
  • مولدهای کد، صفحهٔ ۱۰۲
  • متابرنامه‌نویسی، صفحهٔ ۱۴۴
  • تخته‌سیاه‌ها، صفحهٔ ۱۶۵
  • خودکارسازی همه‌جا، صفحهٔ ۲۳۰
  • همه‌چیز نوشتن است، صفحهٔ ۲۴۸

Challenges

  • پایگاه دادهٔ دفترچهٔ تلفن کوچکی (نام، شماره تلفن و غیره) با نمایش دودویی ساده در زبان انتخابی‌تان طراحی کنید. این کار را قبل از خواندن بقیهٔ این چالش انجام دهید.
    1. آن قالب را به قالب متن ساده با XML تبدیل کنید.
    2. برای هر نسخه، فیلد متغیرطول جدیدی به نام directions اضافه کنید که بتوانید مسیر رسیدن به خانهٔ هر فرد را در آن وارد کنید.
  • چه مسائلی دربارهٔ نسخه‌بندی و گسترش‌پذیری پیش می‌آید؟ کدام قالب اصلاح آسان‌تر بود؟ تبدیل دادهٔ موجود چطور؟

¹ MD5 اغلب برای این منظور استفاده می‌شود. برای مقدمه‌ای عالی بر دنیای شگفت‌انگیز رمزنگاری، به [Sch95] مراجعه کنید.

² همهٔ نرم‌افزارها به‌محض نوشته شدن قدیمی می‌شوند.


۱۵ — بازی‌های پوسته

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

برای برنامه‌نویسی که فایل‌های متنی را دستکاری می‌کند، آن میز کار، پوستهٔ فرمان (command shell) است. از خط فرمان پوسته می‌توانید کل مجموعه ابزارهایتان را فراخوانی کنید و با لوله‌ها آن‌ها را به روش‌هایی ترکیب کنید که سازندگان اصلی‌شان هرگز تصور نکرده بودند. از پوسته می‌توانید برنامه‌ها، دیباگرها، مرورگرها، ویرایشگرها و ابزارهای کمکی را اجرا کنید. می‌توانید فایل‌ها را جستجو کنید، وضعیت سیستم را بپرسید و خروجی را فیلتر کنید. و با برنامه‌نویسی پوسته، می‌توانید دستورات ماکرو پیچیده برای کارهایی که مرتب انجام می‌دهید بسازید.

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

پاسخ ساده «نه» است. رابط‌های گرافیکی عالی‌اند و برای برخی عملیات ساده می‌توانند سریع‌تر و راحت‌تر باشند. جابه‌جایی فایل‌ها، خواندن ایمیل MIME و تایپ نامه‌ها کارهایی‌اند که شاید بخواهید در محیط گرافیکی انجام دهید. اما اگر همهٔ کارهایتان را با GUI انجام دهید، از قابلیت‌های کامل محیطتان محروم می‌شوید. نمی‌توانید کارهای رایج را خودکار کنید یا از قدرت کامل ابزارهای در دسترس استفاده کنید. و نمی‌توانید ابزارهایتان را ترکیب کنید تا ابزارهای ماکرو سفارشی بسازید. یکی از مزایای GUI این است که WYSIWYG — آنچه می‌بینید همان چیزی است که می‌گیرید. معایبش WYSIAYG است — آنچه می‌بینید همهٔ چیزی است که می‌گیرید.

محیط‌های GUI معمولاً به قابلیت‌هایی محدودند که طراحانشان در نظر گرفته‌اند. اگر نیاز دارید فراتر از مدلی که طراح ارائه داده بروید، معمولاً بدون راه می‌مانید — و اغلب اوقات واقعاً نیاز دارید فراتر بروید. برنامه‌نویسان عمل‌گرا فقط کد نمی‌زنند، مدل شیء نمی‌سازند، مستند نمی‌نویسند یا فرایند build را خودکار نمی‌کنند — همهٔ این کارها را انجام می‌دهند. دامنهٔ هر ابزار معمولاً به وظایفی محدود است که از ابزار انتظار می‌رود انجام دهد. برای مثال، فرض کنید باید پیش‌پردازندهٔ کد (برای پیاده‌سازی design-by-contract، pragmaهای چندپردازشی یا چیزی از این دست) در IDE خود ادغام کنید. مگر اینکه طراح IDE صریحاً قلاب‌هایی برای این قابلیت فراهم کرده باشد، نمی‌توانید.

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

به‌عنوان برنامه‌نویس عمل‌گرا، مدام می‌خواهید عملیات موردی انجام دهید — کارهایی که GUI شاید پشتیبانی نکند. خط فرمان برای ترکیب سریع چند دستور برای اجرای پرس‌وجو یا کار دیگر مناسب‌تر است. چند مثال:

همهٔ فایل‌های .c که اخیراً از Makefile شما تغییر کرده‌اند را پیدا کنید.

پوستهfind . -name '*.c' -newer Makefile -print
GUIExplorer را باز کنید، به پوشهٔ درست بروید، روی Makefile کلیک کنید و زمان تغییر را یادداشت کنید. سپس Tools/Find را باز کنید و *.c را برای مشخصات فایل وارد کنید. تب تاریخ را انتخاب کنید و تاریخ Makefile را در اولین فیلد تاریخ وارد کنید. سپس OK بزنید.

آرشیو zip/tar از سورس من بسازید.

پوستهzip archive.zip *.h *.c — یا — tar cvf archive.tar *.h *.c
GUIابزار ZIP (مثل WinZip اشتراکی [URL 41]) را باز کنید، «Create New Archive» را انتخاب کنید، نامش را وارد کنید، پوشهٔ سورس را در دیالوگ add انتخاب کنید، فیلتر را *.c بگذارید، «Add» بزنید، فیلتر را *.h بگذارید، «Add» بزنید، سپس آرشیو را ببندید.

کدام فایل‌های Java در هفتهٔ گذشته تغییر نکرده‌اند؟

پوستهfind . -name '*.java' -mtime +7 -print
GUIبه «Find files» بروید، فیلد «Named» را کلیک کنید و *.java تایپ کنید، تب «Date Modified» را انتخاب کنید. سپس «Between» را بزنید. تاریخ شروع را کلیک کنید و تاریخ آغاز پروژه را وارد کنید. تاریخ پایان را کلیک کنید و تاریخ یک هفته پیش را وارد کنید (تقویم کنار دست داشته باشید). «Find Now» را بزنید.

از آن فایل‌ها، کدام‌ها از کتابخانه‌های awt استفاده می‌کنند؟

پوستهfind . -name '*.java' -mtime +7 -print | xargs grep 'java.awt'
GUIهر فایل از لیست مثال قبلی را در ویرایشگر باز کنید و رشتهٔ java.awt را جستجو کنید. نام هر فایلی که تطابق دارد یادداشت کنید.

واضح است که لیست ادامه دارد. دستورات پوسته ممکن است مبهم یا کوتاه باشند، اما قدرتمند و موجزند. و چون دستورات پوسته می‌توانند در فایل‌های اسکریپت (یا فایل‌های command در سیستم‌های Windows) ترکیب شوند، می‌توانید دنباله‌ای از دستورات برای خودکارسازی کارهایی که مرتب انجام می‌دهید بسازید.

TIP 21

از قدرت پوسته‌های فرمان استفاده کنید

با آشنایی با پوسته، خواهید دید بهره‌وری‌تان بالا می‌رود. نیاز دارید لیستی از همهٔ نام بسته‌های منحصربه‌فردی که صریحاً در کد Java شما import شده‌اند بسازید؟ دستور زیر آن را در فایلی به نام list ذخیره می‌کند:

bash
grep '^import ' *.java |
  sed -e's/.*import *//' -e's/;.*$//' |
  sort -u >list

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

ابزارهای پوسته و سیستم‌های Windows

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

Cygnus Solutions بسته‌ای به نام Cygwin [URL 31] دارد. علاوه بر لایهٔ سازگاری یونیکس برای Windows، Cygwin با مجموعه‌ای از بیش از ۱۲۰ ابزار یونیکس — از جمله ls، grep و find — همراه است. ابزارها و کتابخانه‌ها را می‌توان رایگان دانلود و استفاده کرد، اما حتماً مجوزشان را بخوانید.³ توزیع Cygwin با پوستهٔ Bash همراه است.

استفاده از ابزارهای یونیکس در Windows

ما دسترسی به ابزارهای یونیکس باکیفیت در Windows را دوست داریم و روزانه از آن‌ها استفاده می‌کنیم. اما آگاه باشید مسائل یکپارچه‌سازی وجود دارد. برخلاف همتایان MS-DOS، این ابزارها به حروف بزرگ و کوچک نام فایل حساس‌اند؛ پس ls a*.bat، AUTOEXEC.BAT را پیدا نمی‌کند. ممکن است با نام فایل‌های دارای فاصله و تفاوت جداکنندهٔ مسیر هم مشکل داشته باشید. در نهایت، مشکلات جالبی هنگام اجرای برنامه‌های MS-DOS که انتظار آرگومان‌های سبک MS-DOS دارند زیر پوسته‌های یونیکس پیش می‌آید. برای مثال، ابزارهای Java از JavaSoft زیر یونیکس از دو نقطه به‌عنوان جداکنندهٔ CLASSPATH استفاده می‌کنند، اما زیر MS-DOS از نقطه‌ویرگول. در نتیجه، اسکریپت Bash یا ksh که روی جعبهٔ یونیکس اجرا می‌شود، زیر Windows هم یکسان اجرا می‌شود، اما خط فرمانی که به Java می‌دهد نادرست تفسیر می‌شود.

به‌عنوان جایگزین، David Korn (خالق Korn shell) بسته‌ای به نام UWIN سرهم کرده. اهدافش مانند توزیع Cygwin است — محیط توسعهٔ یونیکس زیر Windows. UWIN با نسخه‌ای از Korn shell همراه است. نسخه‌های تجاری از Global Technologies, Ltd. [URL 30] موجودند. علاوه بر این، AT&T اجازهٔ دانلود رایگان بسته برای ارزیابی و استفادهٔ آکادمیک می‌دهد. باز هم قبل از استفاده مجوزشان را بخوانید.

در نهایت، Tom Christiansen (در زمان نگارش) Perl Power Tools را سرهم می‌کند — تلاشی برای پیاده‌سازی همهٔ ابزارهای آشنای یونیکس به‌صورت قابل‌حمل در Perl [URL 32].

بخش‌های مرتبط:

  • خودکارسازی همه‌جا، صفحهٔ ۲۳۰

Challenges

  • آیا کارهایی هست که الان دستی در GUI انجام می‌دهید؟ آیا به همکاران دستورالعمل‌هایی می‌دهید که شامل چند گام جداگانهٔ «این دکمه را بزن»، «این مورد را انتخاب کن» است؟ آیا می‌توان آن‌ها را خودکار کرد؟
  • هر وقت به محیط جدیدی می‌روید، بفهمید چه پوسته‌هایی در دسترس‌اند. ببینید آیا می‌توانید پوستهٔ فعلی‌تان را با خود ببرید.
  • جایگزین‌های پوستهٔ فعلی‌تان را بررسی کنید. اگر به مشکلی برخوردید که پوسته‌تان از عهدهٔ آن برنمی‌آید، ببینید آیا پوستهٔ جایگزین بهتر عمل می‌کند.

³ مجوز عمومی GNU [URL 57] نوعی ویروس حقوقی است که توسعه‌دهندگان متن‌باز برای محافظت از حقوق خود (و شما) استفاده می‌کنند. باید زمانی صرف خواندن آن کنید. اساساً می‌گوید می‌توانید نرم‌افزار GPL را استفاده و اصلاح کنید، اما اگر تغییراتی توزیع کنید باید طبق GPL مجوزدهی شوند (و به‌عنوان چنین علامت‌گذاری شوند) و باید سورس را در دسترس قرار دهید. این بخش ویروسی است — هر وقت اثری مشتق از اثر GPL بسازید، اثر مشتق‌شده هم باید GPL باشد. اما هنگام صرفاً استفاده از ابزارها شما را محدود نمی‌کند — مالکیت و مجوزدهی نرم‌افزاری که با ابزارها توسعه می‌دهید به خودتان بستگی دارد.


۱۶ — ویرایش قدرتمند

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

یک ویرایشگر

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

باید ماهر باشید. صرفاً تایپ خطی و برش و چسباندن با ماوس کافی نیست. به این شکل به‌اندازهٔ ویرایشگر قدرتمند زیر دستانتان کارآمد نخواهید بود. ده بار زدن یا BACKSPACE برای رفتن به ابتدای خط به‌اندازهٔ یک ضربه مثل ^A، Home یا 0 کارآمد نیست.

TIP 22

یک ویرایشگر را خوب بشناسید

یک ویرایشگر انتخاب کنید، آن را کامل بشناسید و برای همهٔ کارهای ویرایش از آن استفاده کنید. اگر یک ویرایشگر (یا مجموعهٔ keybinding) را در همهٔ فعالیت‌های ویرایش متن به‌کار ببرید، لازم نیست برای دستکاری متن مکث کنید و فکر کنید — ضربه‌های لازم تبدیل به واکنش می‌شوند. ویرایشگر تمدید دست شما می‌شود؛ کلیدها هنگام برش در متن و فکر می‌خوانند. این هدف ماست.

مطمئن شوید ویرایشگری که انتخاب می‌کنید روی همهٔ پلتفرم‌هایی که استفاده می‌کنید در دسترس است. Emacs، vi، CRiSP، Brief و دیگران روی چند پلتفرم — اغلب در نسخه‌های GUI و غیر-GUI (صفحهٔ متنی) — موجودند.

ویژگی‌های ویرایشگر

فراتر از هر ویژگی‌ای که برایتان مفید و راحت است، این‌ها توانایی‌های پایه‌ای‌اند که فکر می‌کنیم هر ویرایشگر شایسته‌ای باید داشته باشد. اگر ویرایشگر شما در هر یک کم دارد، شاید وقت آن رسیده به سمت پیشرفته‌تری بروید.

  • قابل پیکربندی. همهٔ جنبه‌های ویرایشگر باید طبق ترجیحات شما قابل تنظیم باشند، از جمله فونت‌ها، رنگ‌ها، اندازهٔ پنجره‌ها و binding کلیدها (کدام کلیدها چه دستوری اجرا کنند). استفادهٔ صرف از ضربه‌های صفحه‌کلید برای عملیات ویرایش رایج از ماوس یا دستورات منو کارآمدتر است، چون دستانتان هرگز صفحه‌کلید را ترک نمی‌کنند.
  • قابل گسترش. ویرایشگر نباید فقط به‌خاطر آمدن زبان برنامه‌نویسی جدید منسوخ شود. باید بتواند با هر محیط کامپایلری که استفاده می‌کنید یکپارچه شود. باید بتوانید «آموزش» دهید ظرافت‌های هر زبان یا قالب متنی جدید (XML، HTML نسخهٔ ۹ و غیره).
  • قابل برنامه‌نویسی. باید بتوانید ویرایشگر را برای انجام کارهای پیچیده و چندمرحله‌ای برنامه‌نویسی کنید. این را می‌توان با ماکرو یا زبان اسکریپت داخلی (مثلاً Emacs از گونه‌ای از Lisp استفاده می‌کند) انجام داد.

علاوه بر این، بسیاری از ویرایشگرها ویژگی‌هایی دارند که مختص زبان برنامه‌نویسی خاصی‌اند، مثل:

  • هایلایت نحوی
  • تکمیل خودکار
  • تورفتگی خودکار
  • boilerplate اولیهٔ کد یا سند
  • اتصال به سیستم‌های راهنما
  • ویژگی‌های شبیه IDE (کامپایل، دیباگ و غیره)

شکل ۳.۱ — مرتب‌سازی خطوط در ویرایشگر

import java.util.Vector;     emacs: M-x sort-lines   import java.awt.*;
import java.util.Stack;                              import java.net.URL;
import java.net.URL;                                 import java.util.Stack;
import java.awt.*;                                   import java.util.Vector;
                                                 vi: :.,+3!sort

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

داشتن توانایی کامپایل و رفتن مستقیم به خطاها در محیط ویرایشگر در پروژه‌های بزرگ بسیار کاربردی است. Emacs به‌ویژه در این سبک تعامل ماهر است.

بهره‌وری

تعداد شگفت‌انگیزی از افرادی که دیده‌ایم از Notepad ویندوز برای ویرایش کد منبع استفاده می‌کنند. این مثل استفاده از قاشق چای‌خوری به‌عنوان بیل است — صرف تایپ و برش و چسباندن پایه‌ای با ماوس کافی نیست.

چه کارهایی لازم است که به این شکل نمی‌توان انجام داد؟

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

یا فرض کنید کد Java می‌نویسید. دوست دارید importها را به ترتیب الفبایی نگه دارید و کس دیگری چند فایلی را commit کرده که این استاندارد را رعایت نکرده (شاید افراطی به نظر برسد، اما در پروژهٔ بزرگ زمان زیادی در اسکن لیست طولانی import صرفه‌جویی می‌کند). می‌خواهید سریع چند فایل را باز کنید و بخش کوچکی از آن‌ها را مرتب کنید. در ویرایشگرهایی مثل vi و Emacs این کار آسان است (شکل ۳.۱ را ببینید). در Notepad امتحان کنید.

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

  • نام کلاس یا ماژول (مشتق از نام فایل)
  • نام شما و/یا بیانیه‌های حق نشر
  • اسکلت سازه‌ها در آن زبان (مثلاً اعلان سازنده و مخرب)

ویژگی مفید دیگر تورفتگی خودکار است. به‌جای تورفتگی دستی (با فاصله یا tab)، ویرایشگر در زمان مناسب خودکار تورفتگی می‌دهد (مثلاً بعد از تایپ آکولاد باز). بخش خوب این است که می‌توانید از ویرایشگر برای سبک تورفتگی یکنواخت در پروژه استفاده کنید.⁵

از اینجا به کجا؟

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

اگر این شبیه شماستپس فکر کنید به
فقط از ویژگی‌های پایهٔ بسیاری از ویرایشگرهای مختلف استفاده می‌کنم.یک ویرایشگر قدرتمند انتخاب کنید و خوب یاد بگیرید.
ویرایشگر موردعلاقه دارم، اما از همهٔ ویژگی‌هایش استفاده نمی‌کنم.آن‌ها را یاد بگیرید. تعداد ضربه‌هایی که باید تایپ کنید را کم کنید.
ویرایشگر موردعلاقه دارم و تا جایی که ممکن است از آن استفاده می‌کنم.سعی کنید گسترش دهید و برای کارهای بیشتری از آن استفاده کنید.
فکر می‌کنید دیوانه‌ایم. Notepad بهترین ویرایشگر است.تا وقتی خوشحال و مولدید، ادامه دهید! اما اگر دچار «حسادت ویرایشگر» شدید، شاید لازم باشد موضع خود را بازنگری کنید.

چه ویرایشگرهایی در دسترس‌اند؟

با توصیهٔ تسلط بر ویرایشگر شایسته، کدام را توصیه می‌کنیم؟ خب، از این سؤال طفره می‌رویم؛ انتخاب ویرایشگر شخصی است (برخی حتی مذهبی می‌نامندش!). اما در پیوست الف، صفحهٔ ۲۶۶، فهرستی از ویرایشگرهای محبوب و محل دریافتشان آمده.

Challenges

  • برخی ویرایشگرها برای سفارشی‌سازی و اسکریپت‌نویسی از زبان‌های کامل استفاده می‌کنند. Emacs مثلاً از Lisp استفاده می‌کند. به‌عنوان یکی از زبان‌های جدیدی که امسال یاد می‌گیرید، زبان ویرایشگر خود را یاد بگیرید. برای هر کاری که مرتب تکرار می‌کنید، مجموعه‌ای از ماکروها (یا معادل) بسازید.
  • آیا همهٔ چیزی که ویرایشگرتان می‌تواند انجام دهد را می‌دانید؟ سعی کنید همکارانی که همان ویرایشگر را دارند غافلگیر کنید. سعی کنید هر کار ویرایشی را با کمترین ضربه انجام دهید.

⁴ ایده‌آل این است که پوسته‌ای که استفاده می‌کنید keybindingهایی مطابق ویرایشگر شما داشته باشد. Bash مثلاً از keybindingهای vi و emacs پشتیبانی می‌کند.

⁵ هستهٔ لینوکس این‌طور توسعه می‌یابد. توسعه‌دهندگان پراکندهٔ جغرافیایی دارید که بسیاری روی همان قطعات کد کار می‌کنند. فهرست منتشرشده‌ای از تنظیمات (در این مورد برای Emacs) وجود دارد که سبک تورفتگی لازم را توصیف می‌کند.


۱۷ — کنترل کد منبع

پیشرفت، برخلاف آنچه تصور می‌شود، به تغییر بستگی ندارد؛ به توانایی نگه‌داشتن بستگی دارد. کسانی که نمی‌توانند گذشته را به یاد بیاورند محکوم‌اند آن را تکرار کنند.

— George Santayana، Life of Reason

یکی از چیزهای مهمی که در رابط کاربری می‌جوییم کلید UNDO است — دکمه‌ای واحد که اشتباهاتمان را می‌بخشد. بهتر هم اگر محیط از چند سطح undo و redo پشتیبانی کند تا بتوانید به چیزی که چند دقیقه پیش اتفاق افتاد برگردید. اما اگر اشتباه هفتهٔ پیش بوده و از آن موقع ده بار کامپیوتر را خاموش و روشن کرده‌اید چه؟ خب، این یکی از مزایای زیاد استفاده از سیستم کنترل کد منبع: یک UNDO غول‌پیکر — ماشین زمان در مقیاس پروژه که می‌تواند شما را به روزهای خوش هفتهٔ گذشته برگرداند، وقتی کد واقعاً کامپایل و اجرا می‌شد.

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

اما سیستم کنترل کد منبع (SCCS⁶) فراتر از لغو اشتباهات کار می‌کند. SCCS خوب به شما اجازه می‌دهد تغییرات را ردیابی کنید و به سؤالاتی مثل این پاسخ دهید: چه کسی این خط کد را تغییر داد؟ تفاوت نسخهٔ فعلی با هفتهٔ گذشته چیست؟ در این انتشار چند خط کد تغییر کردیم؟ کدام فایل‌ها بیشتر تغییر می‌کنند؟ چنین اطلاعاتی برای ردیابی باگ، ممیزی، عملکرد و کیفیت بی‌قیمت است.

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

اغلب از SCCS برای مدیریت شاخه‌ها در درخت توسعه استفاده می‌کنیم. برای مثال، وقتی نرم‌افزاری را منتشر کردید، معمولاً می‌خواهید برای انتشار بعدی به توسعه ادامه دهید. هم‌زمان باید باگ‌های انتشار فعلی کنار بیایید و نسخه‌های اصلاح‌شده را به مشتریان بفرستید. می‌خواهید این اصلاحات در انتشار بعدی هم ادغام شوند (در صورت مناسب بودن)، اما نمی‌خواهید کد در حال توسعه را به مشتریان بفرستید. با SCCS می‌توانید هر بار که انتشار تولید می‌کنید شاخه‌ای در درخت توسعه بسازید. اصلاحات باگ را روی شاخه اعمال می‌کنید و روی تنهٔ اصلی به توسعه ادامه می‌دهید. چون اصلاحات باگ ممکن است برای تنهٔ اصلی هم مرتبط باشند، برخی سیستم‌ها به شما اجازه می‌دهند تغییرات انتخابی را از شاخه به‌طور خودکار به تنهٔ اصلی ادغام کنید.

سیستم‌های کنترل کد منبع ممکن است فایل‌هایی که نگه می‌دارند را در مخزن مرکزی نگه دارند — کاندیدای عالی برای بایگانی.

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

TIP 23

همیشه از کنترل کد منبع استفاده کنید

همیشه. حتی اگر تیم تک‌نفره روی پروژهٔ یک‌هفته‌ای هستید. حتی اگر نمونهٔ اولیهٔ «یک‌بارمصرف» است. حتی اگر چیزی که روی آن کار می‌کنید کد منبع نیست. مطمئن شوید همه‌چیز زیر کنترل کد منبع است — مستندات، لیست شماره تلفن‌ها، یادداشت‌ها به فروشندگان، Makefileها، روش‌های build و انتشار، آن اسکریپت کوچک پوسته که CD اصلی را می‌سوزاند — همه‌چیز. ما به‌طور معمول تقریباً همهٔ چیزی که تایپ می‌کنیم (از جمله متن این کتاب) را زیر کنترل کد منبع نگه می‌داریم. حتی وقتی روی پروژه‌ای کار نمی‌کنیم، کار روزمره‌مان در مخزن امن است.

کنترل کد منبع و buildها

مزیت پنهان عظیمی در داشتن کل پروژه زیر چتر سیستم کنترل کد منبع وجود دارد: می‌توانید buildهای محصول داشته باشید که خودکار و قابل تکرارند.

مکانیزم build پروژه می‌تواند آخرین سورس را به‌طور خودکار از مخزن بکشد. می‌تواند نیمه‌شب، بعد از رفتن همه (امیدواریم) به خانه، اجرا شود. می‌توانید تست‌های رگرسیون خودکار اجرا کنید تا مطمئن شوید کدنویسی آن روز چیزی را نشکسته. خودکارسازی build یکنواختی را تضمین می‌کند — روش دستی نیست و لازم نیست توسعه‌دهندگان یادشان باشد کد را به ناحیهٔ build ویژه کپی کنند.

build قابل تکرار است چون همیشه می‌توانید سورس را همان‌طور که در تاریخ مشخصی وجود داشت دوباره بسازید.

اما تیم من از کنترل کد منبع استفاده نمی‌کند

شرم بر آن‌ها! به نظر فرصتی برای تبلیغ است! اما تا وقتی نور را ببینند، شاید بهتر است کنترل منبع خصوصی خود را پیاده کنید. از یکی از ابزارهای رایگان فهرست‌شده در پیوست الف استفاده کنید و کار شخصی‌تان را در مخزن امن نگه دارید (علاوه بر آنچه پروژه می‌طلبد). اگرچه شاید تکرار به نظر برسد، تقریباً تضمین می‌کنیم در اولین باری که باید به سؤالاتی مثل «در ماژول xyz چه کردی؟» و «چه چیزی build را شکست؟» پاسخ دهید، دردسر (و پول پروژه) را ذخیره می‌کند. این رویکرد شاید به متقاعد کردن مدیریت هم کمک کند که کنترل کد منبع واقعاً کار می‌کند.

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

محصولات کنترل کد منبع

پیوست الف، صفحهٔ ۲۷۱، URLهایی برای سیستم‌های نمایندهٔ کنترل کد منبع — برخی تجاری و برخی رایگان — می‌دهد. و محصولات بسیار بیشتری موجودند — به FAQ مدیریت پیکربندی نگاه کنید. برای مقدمه بر سیستم کنترل نسخهٔ رایگان CVS، به کتاب ما Pragmatic Version Control [TH03] مراجعه کنید.

بخش‌های مرتبط:

  • متعامد بودن، صفحهٔ ۳۴
  • قدرت متن ساده، صفحهٔ ۷۳
  • همه‌چیز نوشتن است، صفحهٔ ۲۴۸

Challenges

  • حتی اگر در محل کار نتوانید از SCCS استفاده کنید، RCS یا CVS را روی سیستم شخصی نصب کنید. از آن برای مدیریت پروژه‌های شخصی، اسنادی که می‌نویسید و (شاید) تغییرات پیکربندی اعمال‌شده روی خود سیستم کامپیوتر استفاده کنید.
  • به برخی پروژه‌های متن‌باز که آرشیوهای عمومی روی وب دارند نگاه کنید (مثل Mozilla [URL 51]، KDE [URL 54] و Gimp [URL 55]). چطور به‌روزرسانی سورس را می‌گیرید؟ چطور تغییر می‌دهید — آیا پروژه دسترسی را تنظیم می‌کند یا گنجاندن تغییرات را داوری می‌کند؟

⁶ از SCCS با حروف بزرگ برای اشاره به سیستم‌های عمومی کنترل کد منبع استفاده می‌کنیم. سیستم مشخصی به نام «sccs» هم وجود دارد که در ابتدا با AT&T System V Unix منتشر شد.


۱۸ — اشکال‌زدایی

دردناک است به درد خود نگاه کنی و بدانی که خودت و نه کس دیگر آن را ساخته‌ای

— Sophocles، Ajax

کلمهٔ bug از قرن چهاردهم برای توصیف «موضوع ترس» استفاده شده. دریاسالار دکتر Grace Hopper، مخترع COBOL، به مشاهدهٔ اولین باگ کامپیوتری — به‌معنای واقعی، پروانه‌ای گیرکرده در رلهٔ سیستم کامپیوتری اولیه — نسبت داده می‌شود. وقتی از او خواستند توضیح دهد چرا ماشین طبق انتظار رفتار نمی‌کند، تکنسین گزارش داد «باگی در سیستم است» و با وظیفه آن را — بال و همه — در دفترچهٔ گزارش چسباند.

متأسفانه هنوز «باگ» در سیستم داریم، هرچند از نو پرنده‌ای نیست. اما معنای قرن چهاردهم — یک موجود ترسناک — شاید امروز حتی کاربردی‌تر باشد. نقص‌های نرم‌افزاری به روش‌های گوناگون ظاهر می‌شوند، از نیازمندی‌های سوءفهمیده‌شده تا خطاهای کدنویسی. متأسفانه سیستم‌های کامپیوتری مدرن هنوز محدود به انجام آنچه به آن‌ها می‌گویید هستند، نه لزوماً آنچه می‌خواهید.

کسی نرم‌افزار کامل نمی‌نویسد، پس اشکال‌زدایی بخش عمده‌ای از روز شما خواهد بود. بیایید برخی مسائل مربوط به اشکال‌زدایی و راهبردهای کلی برای یافتن باگ‌های لغزان را ببینیم.

روانشناسی اشکال‌زدایی

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

این را بپذیرید که اشکال‌زدایی فقط حل مسئله است و آن را چنین حمله کنید.

وقتی باگ کس دیگری را پیدا کردید، می‌توانید وقت و انرژی صرف سرزنش مقصر کثیف کنید. در برخی محیط‌های کاری بخشی از فرهنگ است و شاید تخلیهٔ احساسی باشد. اما در حوزهٔ فنی می‌خواهید روی رفع مشکل تمرکز کنید، نه سرزنش.

TIP 24

مشکل را رفع کنید، نه سرزنش را

واقعاً مهم نیست باگ تقصیر شماست یا کس دیگری. همچنان مشکل شماست.

ذهنیت اشکال‌زدایی

آسان‌ترین کسی که می‌توان فریب داد خودِ فرد است.

— Edward Bulwer-Lytton، The Disowned

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

TIP 25

وحشت نکنید

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

اگر اولین واکنش شما به دیدن باگ یا گزارش باگ «این غیرممکن است» است، قطعاً اشتباه می‌کنید. حتی یک نورون هم صرف فکر «اما این نمی‌تواند اتفاق بیفتد» نکنید، چون کاملاً واضح است که می‌تواند و اتفاق افتاده.

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

از کجا شروع کنیم

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

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

دقت در گزارش‌های باگ وقتی از طریق شخص ثالث می‌آیند بیشتر کاهش می‌یابد — شاید واقعاً لازم باشد کاربری که باگ را گزارش کرده در عمل ببینید تا جزئیات کافی بگیرید.

Andy یک‌بار روی برنامهٔ گرافیکی بزرگی کار کرد. نزدیک انتشار، تست‌کنندگان گزارش دادند برنامه هر بار که با قلم خاصی یک ضربه می‌کشند خراب می‌شود. برنامه‌نویس مسئول استدلال کرد مشکلی نیست؛ خودش با آن قلم امتحان کرده و درست کار کرده. این گفتگو چند روز رفت و برگشت داشت و حال‌ها تند شد.

در نهایت آن‌ها را در یک اتاق نشاندیم. تست‌کننده ابزار قلم را انتخاب کرد و از گوشهٔ بالا-راست به گوشهٔ پایین-چپ یک ضربه کشید. برنامه منفجر شد. «اوه»، برنامه‌نویس با صدای آرام گفت و با خجالت اعتراف کرد که فقط از پایین-چپ به بالا-راست تست کشیده بود که باگ را نشان نمی‌داد.

دو نکته در این داستان:

  • شاید لازم باشد با کاربری که باگ را گزارش کرده مصاحبه کنید تا دادهٔ بیشتری از آنچه اول داده شده جمع کنید.
  • تست‌های مصنوعی (مثل ضربهٔ تکی برنامه‌نویس از پایین به بالا) برنامه را به‌اندازهٔ کافی تمرین نمی‌دهند. باید هم شرایط مرزی و هم الگوهای واقعی استفادهٔ کاربر نهایی را به‌طور بی‌رحمانه تست کنید. باید این را سیستماتیک انجام دهید (به تست بی‌رحمانه، صفحهٔ ۲۳۷ مراجعه کنید).

راهبردهای اشکال‌زدایی

وقتی فکر می‌کنید می‌دانید چه خبر است، وقت آن است بفهمید برنامه چه فکر می‌کند.

تولیدمثل باگ

نه، باگ‌های ما واقعاً در حال تکثیر نیستند (اگرچه برخی‌شان احتمالاً به‌قدری پیرند که قانونی می‌توانند!). منظورمان نوع دیگری از تولیدمثل است.

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

اما ما بیش از باگی می‌خواهیم که با دنباله‌ای طولانی از گام‌ها قابل تکرار باشد؛ باگی می‌خواهیم که با یک دستور قابل تکرار باشد. رفع باگ سخت‌تر است اگر ۱۵ گام طی کنید تا به نقطه‌ای برسید که باگ ظاهر می‌شود. گاهی با وادار کردن خود به جداسازی شرایطی که باگ را نشان می‌دهد، حتی بینش دربارهٔ نحوهٔ رفعش پیدا می‌کنید.

برای ایده‌های دیگر در این خط، به خودکارسازی همه‌جا، صفحهٔ ۲۳۰ مراجعه کنید.

داده‌هایتان را تجسم کنید

اغلب آسان‌ترین راه فهمیدن برنامه چه می‌کند — یا چه خواهد کرد — نگاه خوب به داده‌ای است که روی آن کار می‌کند. ساده‌ترین مثال رویکرد «نام متغیر = مقدار داده» است که ممکن است به‌صورت متن چاپ‌شده یا فیلدهایی در دیالوگ یا لیست GUI پیاده شود.

اما می‌توانید با دیباگری که داده و همهٔ روابط بین آن‌ها را تجسم می‌کند، بینش عمیق‌تری بگیرید. دیباگرهایی هستند که داده را به‌صورت پرواز سه‌بعدی در منظرهٔ واقعیت مجازی، نمودار موج سه‌بعدی یا نمودارهای ساختاری ساده نشان می‌دهند، همان‌طور که در شکل ۳.۲ آمده. وقتی گام‌به‌گام در برنامه پیش می‌روید، چنین تصاویری می‌توانند ارزش بیش از هزار کلمه داشته باشند، چون باگی که دنبالش بودید ناگهان جلو می‌پرد.

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

دیباگر DDD برخی قابلیت‌های تجسم دارد و رایگان است (به [URL 19] مراجعه کنید). جالب است که DDD با چند زبان کار می‌کند، از جمله Ada، C، C++، Fortran، Java، Modula، Pascal، Perl و Python (طراحی کاملاً متعامد).

شکل ۳.۲ — نمونهٔ نمودار دیباگر لیست پیوندی حلقوی. فلش‌ها اشاره‌گر به گره‌ها را نشان می‌دهند.

           1: list
           (List *)
           0x804db40

                              next
    value = 85                          value = 86
    self = 0x804db40                    self = 0x804db50
    next = 0x804db50                    next = 0x804db40
                              next

ردیابی (Tracing)

دیباگرها معمولاً روی وضعیت فعلی برنامه تمرکز دارند. گاهی بیشتر نیاز دارید — باید وضعیت برنامه یا ساختار داده را در طول زمان ببینید. دیدن stack trace فقط می‌گوید چطور مستقیماً به اینجا رسیدید. نمی‌گوید قبل از این زنجیرهٔ فراخوانی چه می‌کردید، به‌ویژه در سیستم‌های مبتنی بر رویداد.

عبارات ردیابی همان پیام‌های تشخیصی کوچکی‌اند که روی صفحه یا فایل چاپ می‌کنید و می‌گویند «به اینجا رسیدم» و «مقدار x = ۲». نسبت به دیباگرهای سبک IDE ابزار ابتدایی است، اما برای چند دسته خطا که دیباگرها نمی‌توانند، به‌طرز عجیبی مؤثر است. ردیابی در هر سیستمی که زمان خود عامل است — فرایندهای همزمان، سیستم‌های بلادرنگ و برنامه‌های مبتنی بر رویداد — بی‌قیمت است.

می‌توانید از عبارات ردیابی برای «حفر کردن» در کد استفاده کنید. یعنی هنگام پایین رفتن در درخت فراخوانی، عبارات ردیابی اضافه کنید.

پیام‌های trace باید قالب منظم و یکنواخت داشته باشند؛ شاید بخواهید خودکار تجزیه‌شان کنید. برای مثال، اگر باید نشت منبع (مثل باز/بسته نشدن متعادل فایل) را ردیابی کنید، می‌توانید هر باز و هر بسته را در فایل log ثبت کنید. با پردازش log با Perl، به‌راحتی باز کردن مشکل‌ساز را شناسایی می‌کنید.

متغیرهای خراب؟ همسایه‌ها را بررسی کنید

گاهی متغیری را بررسی می‌کنید و انتظار عدد صحیح کوچک دارید، اما به‌جای آن چیزی مثل 0x6e69614d می‌بینید. قبل از آستین بالا زدن برای اشکال‌زدایی جدی، سریع حافظهٔ اطراف این متغیر خراب را ببینید. اغلب سرنخ می‌دهد. در مورد ما، بررسی حافظهٔ اطراف به‌عنوان نویسه نشان می‌دهد:

20333231 6e69614d 2c745320 746f4e0a
 1 2 3    M a i n    S t , n N o t
2c6e776f 2058580a 31323433 00000a33
 o w n , n X X     3 4 2 1 3 n 0 0

به نظر می‌رسد کسی آدرس خیابان را روی شمارندهٔ ما پاشیده. حالا می‌دانیم کجا بگردیم.

اردک لاستیکی (Rubber Ducking)

تکنیک بسیار ساده اما به‌ویژه مفید برای یافتن علت مشکل این است که آن را به کس دیگری توضیح دهید. طرف دیگر باید از روی شانه‌تان به صفحه نگاه کند و مدام سر تکان دهد (مثل اردک لاستیکی که در وان بالا و پایین می‌رود). لازم نیست کلمه‌ای بگوید؛ صرف عمل توضیح گام‌به‌گام آنچه کد قرار است انجام دهد اغلب باعث می‌شود مشکل از صفحه بپرد و خودش را اعلام کند.⁷

ساده به نظر می‌رسد، اما در توضیح مشکل به دیگری باید صریحاً چیزهایی را بگویید که هنگام مرور کد خودتان مسلم می‌گیرید. با verbal کردن برخی از این فرض‌ها، ممکن است ناگهان بینش جدیدی دربارهٔ مشکل پیدا کنید.

حذف به‌روش حذف و خطا (Process of Elimination)

در بیشتر پروژه‌ها، کدی که اشکال‌زدایی می‌کنید ترکیبی از کد برنامهٔ نوشته‌شده توسط شما و دیگران تیم، محصولات شخص ثالث (پایگاه داده، اتصال، کتابخانه‌های گرافیکی، ارتباطات تخصصی یا الگوریتم‌ها و غیره) و محیط پلتفرم (سیستم‌عامل، کتابخانه‌های سیستم و کامپایلرها) است.

ممکن است باگی در OS، کامپایلر یا محصول شخص ثالث وجود داشته باشد — اما این نباید اولین فکرتان باشد. بسیار محتمل‌تر است باگ در کد برنامهٔ در حال توسعه باشد. معمولاً سودآورتر است فرض کنید کد برنامه نادرست به کتابخانه فراخوانی می‌زند تا اینکه کتابخانه خودش خراب باشد. حتی اگر مشکل از شخص ثالث باشد، باز هم باید کد خود را حذف کنید قبل از ارسال گزارش باگ.

روی پروژه‌ای کار کردیم که مهندس ارشد متقاعد بود فراخوانی سیستم select روی Solaris خراب است. هیچ مقدار متقاعدسازی یا منطق ذهنیتش را عوض نکرد (اینکه هر برنامهٔ شبکهٔ دیگر روی جعبه درست کار می‌کرد بی‌ربط بود). هفته‌ها صرف workaround نوشتن کرد که به دلایل عجیبی مشکل را حل نمی‌کرد. وقتی مجبور شد بنشیند و مستندات select را بخواند، مشکل را کشف و در چند دقیقه اصلاح کرد. حالا عبارت «select خراب است» را به‌عنوان یادآوری ملایم هر وقت یکی از ما شروع به سرزنش سیستم برای نقصی می‌کند که احتمالاً مال خودمان است، استفاده می‌کنیم.

TIP 26

«select» خراب نیست

به یاد داشته باشید، اگر رد پای سم دیدید، اسب فکر کنید — نه گورخر. OS احتمالاً خراب نیست. و پایگاه داده هم احتمالاً سالم است.

اگر «فقط یک چیز را تغییر دادید» و سیستم از کار افتاد، آن یک چیز احتمالاً مسئول بود، مستقیم یا غیرمستقیم، هرچقدر هم بعید به نظر برسد. گاهی چیزی که تغییر کرده خارج از کنترل شماست: نسخه‌های جدید OS، کامپایلر، پایگاه داده یا نرم‌افزار شخص ثالث دیگر می‌توانند با کد قبلاً درست خرابکاری کنند. باگ‌های جدید ممکن است ظاهر شوند. باگ‌هایی که workaround داشتید رفع می‌شوند و workaround را می‌شکنند. APIها تغییر می‌کنند، قابلیت‌ها تغییر می‌کنند؛ به‌اختصار، بازی تازه است و باید سیستم را زیر این شرایط جدید دوباره تست کنید. پس هنگام در نظر گرفتن ارتقا به برنامهٔ زمانی دقت کنید؛ شاید بخواهید تا بعد از انتشار بعدی صبر کنید.

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

عنصر غافلگیری

وقتی خودتان را غافلگیر باگی می‌بینید (شاید زیر لب «این غیرممکن است» زمزمه کنید جایی که نشنویم)، باید حقایقی که عزیز دارید را بازنگری کنید. در آن روال لیست پیوندی — همان که می‌دانستید ضدگلوله است و نمی‌تواند علت این باگ باشد — آیا همهٔ شرایط مرزی را تست کردید؟ آن قطعه کد دیگری که سال‌ها استفاده می‌کنید — نمی‌تواند هنوز باگ داشته باشد. می‌تواند؟

البته می‌تواند. میزان غافلگیری‌تان وقتی چیزی اشتباه می‌شود مستقیماً متناسب با میزان اعتماد و ایمانی است که به کد در حال اجرا دارید. به همین دلیل، وقتی با شکست «غافلگیرکننده» روبه‌رو می‌شوید، باید بفهمید یکی یا چند فرض شما اشتباه است. به‌خاطر «می‌دانم» کار می‌کند، روال یا قطعه کد درگیر باگ را رد نکنید. ثابتش کنید. در این زمینه، با این داده، با این شرایط مرزی ثابتش کنید.

TIP 27

فرض نکنید — ثابت کنید

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

همچنین اگر باگ نتیجهٔ دادهٔ بد است که از چند سطح عبور کرده قبل از انفجار، ببینید آیا بررسی بهتر پارامتر در آن روال‌ها زودتر آن را جدا می‌کرد (به بحث‌های دربارهٔ crash زودهنگام و assertionها در صفحات ۱۲۰ و ۱۲۲ مراجعه کنید).

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

اگر رفع این باگ وقت زیادی برد، از خود بپرسید چرا. آیا کاری می‌توانید کنید رفع این باگ دفعهٔ بعد آسان‌تر شود؟ شاید hookهای تست بهتری بسازید یا تحلیل‌گر فایل log بنویسید.

در نهایت، اگر باگ نتیجهٔ فرض اشتباه کسی بود، مشکل را با کل تیم بحث کنید: اگر یک نفر سوءتفاهم دارد، ممکن است بسیاری داشته باشند.

همهٔ این کارها را انجام دهید و امیدواریم دفعهٔ بعد غافلگیر نشوید.

چک‌لیست اشکال‌زدایی

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

بخش‌های مرتبط:

  • برنامه‌نویسی قاطع، صفحهٔ ۱۲۲
  • برنامه‌نویسی بر اساس تصادف، صفحهٔ ۱۷۲
  • خودکارسازی همه‌جا، صفحهٔ ۲۳۰
  • تست بی‌رحمانه، صفحهٔ ۲۳۷

Challenges

  • اشکال‌زدایی به‌اندازهٔ کافی چالش است.

⁷ چرا «اردک لاستیکی»؟ وقتی دانشجوی کارشناسی در Imperial College لندن بود، Dave کار زیادی با دستیار تحقیقاتی به نام Greg Pugh کرد، یکی از بهترین توسعه‌دهندگانی که Dave می‌شناسد. چند ماه Greg یک اردک لاستیکی زرد کوچک با خود حمل می‌کرد و هنگام کدنویسی روی ترمینالش می‌گذاشت. مدتی طول کشید تا Dave جرأت پرسیدن پیدا کند...


۱۹ — دستکاری متن

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

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

تعداد رو به رشد زبان‌های خوب دستکاری متن وجود دارد. توسعه‌دهندگان یونیکس اغلب قدرت پوسته‌های فرمانشان را دوست دارند، تقویت‌شده با ابزارهایی مثل awk و sed. کسانی که ابزار ساختاریافته‌تر می‌خواهند ماهیت شیءگرای Python [URL 9] را دوست دارند. برخی Tcl [URL 23] را ابزار انتخابی خود می‌دانند. ما Ruby [TFH04] و Perl [URL 8] را برای نوشتن اسکریپت‌های کوتاه ترجیح می‌دهیم.

این زبان‌ها فناوری‌های توانمندساز مهم‌اند. با آن‌ها می‌توانید سریع ابزارهای کمکی و نمونه‌های اولیهٔ ایده بسازید — کارهایی که با زبان‌های متعارف شاید پنج یا ده برابر طول بکشد. و آن ضریب برای نوع آزمایشگری که ما انجام می‌دهیم حیاتی است. ۳۰ دقیقه برای امتحان ایدهٔ دیوانه‌وار به‌مراتب بهتر از ۵ ساعت است. یک روز برای خودکارسازی اجزای مهم پروژه قابل‌قبول است؛ یک هفته شاید نباشد. در کتاب The Practice of Programming [KP99]، Kernighan و Pike همان برنامه را در پنج زبان مختلف ساختند. نسخهٔ Perl کوتاه‌ترین بود (۱۷ خط، در مقابل ۱۵۰ خط C). با Perl می‌توانید متن را دستکاری کنید، با برنامه‌ها تعامل کنید، روی شبکه حرف بزنید، صفحات وب را هدایت کنید، حساب اعشاری دلخواه انجام دهید و برنامه‌هایی بنویسید که شبیه فحش دادن Snoopy به نظر برسند.

TIP 28

یک زبان دستکاری متن یاد بگیرید

برای نشان دادن کاربرد گستردهٔ زبان‌های دستکاری متن، نمونه‌ای از برنامه‌هایی که در چند سال گذشته توسعه دادیم:

  • نگهداری طرح پایگاه داده. مجموعه‌ای اسکریپت Perl فایل متن ساده حاوی تعریف طرح پایگاه داده را گرفت و از آن تولید کرد:

    • دستورات SQL برای ایجاد پایگاه داده
    • فایل‌های دادهٔ تخت برای پر کردن فرهنگ داده
    • کتابخانه‌های C برای دسترسی به پایگاه داده
    • اسکریپت‌ها برای بررسی یکپارچگی پایگاه داده
    • صفحات وب حاوی توصیف و نمودار طرح
    • نسخهٔ XML طرح
  • دسترسی به ویژگی Java. سبک خوب برنامه‌نویسی OO محدود کردن دسترسی به ویژگی‌های شیء و وادار کردن کلاس‌های خارجی به get/set از طریق متد است. اما در حالت رایج که ویژگی داخل کلاس با متغیر عضو ساده نمایش داده می‌شود، ساخت get و set برای هر متغیر خسته‌کننده و مکانیکی است. اسکریپت Perl داریم که فایل‌های سورس را اصلاح و تعریف متد درست را برای همهٔ متغیرهای علامت‌گذاری‌شده مناسب درج می‌کند.

  • تولید دادهٔ تست. ده‌ها هزار رکورد دادهٔ تست داشتیم، روی چند فایل و قالب مختلف پخش‌شده، که باید به هم دوخته و به قالبی مناسب برای بارگذاری در پایگاه دادهٔ رابطه‌ای تبدیل می‌شد. Perl در چند ساعت انجام داد (و در فرایند چند خطای سازگاری در دادهٔ اصلی پیدا کرد).

  • نوشتن کتاب. فکر می‌کنیم مهم است هر کدی در کتاب ابتدا تست شده باشد. بیشتر کد این کتاب تست شده. اما با اصل DRY (به شرهای تکرار، صفحهٔ ۲۶ مراجعه کنید) نمی‌خواستیم خطوط کد را از برنامه‌های تست‌شده کپی و در کتاب بچسبانیم. آن‌طور کد تکرار می‌شد و تقریباً تضمین می‌کرد هنگام تغییر برنامه مربوطه، مثال را فراموش کنیم به‌روز کنیم. برای برخی مثال‌ها هم نمی‌خواستیم شما را با همهٔ کد چارچوب خسته کنیم. به Perl روی آوردیم. اسکریپت نسبتاً ساده‌ای هنگام قالب‌بندی کتاب فراخوانی می‌شود — بخش نام‌گذاری‌شده از فایل سورس را استخراج، هایلایت نحوی می‌کند و نتیجه را به زبان حروف‌چینی که استفاده می‌کنیم تبدیل می‌کند.

  • رابط C به Object Pascal. مشتری تیمی از توسعه‌دهندگان Object Pascal روی PC داشت. کدشان باید با بدنهٔ کد C رابط داشته باشد. اسکریپت Perl کوتاهی ساختند که هدرهای C را تجزیه، تعریف همهٔ توابع صادرشده و ساختارهای داده‌ای که استفاده می‌کنند را استخراج کرد. سپس واحدهای Object Pascal با رکوردهای Pascal برای همهٔ ساختارهای C و تعریف procedureهای import برای همهٔ توابع C تولید کرد. این فرایند تولید بخشی از build شد تا هر وقت هدر C تغییر کرد، واحد Object Pascal جدید خودکار ساخته شود.

  • تولید مستندات وب. بسیاری از تیم‌های پروژه مستنداتشان را روی سایت‌های وب داخلی منتشر می‌کنند. بسیار Perl نوشته‌ایم که طرح پایگاه داده، فایل‌های سورس C یا C++، Makefileها و سایر منابع پروژه را تحلیل و مستندات HTML لازم تولید می‌کند. همچنین از Perl برای پیچیدن اسناد با header و footer استاندارد و انتقال به سایت وب استفاده می‌کنیم.

ما تقریباً هر روز از زبان‌های دستکاری متن استفاده می‌کنیم. بسیاری از ایده‌های این کتاب را می‌توان در آن‌ها ساده‌تر از هر زبان دیگری که می‌شناسیم پیاده کرد. این زبان‌ها نوشتن مولدهای کد را آسان می‌کنند — که بعد می‌بینیم.

بخش‌های مرتبط:

  • شرهای تکرار، صفحهٔ ۲۶

Exercises

پاسخ در صفحهٔ ۲۸۵ — تمرین ۱۱. برنامهٔ C شما از enum برای نمایش یکی از ۱۰۰ حالت استفاده می‌کند. می‌خواهید حالت را به‌صورت رشته (نه عدد) برای اشکال‌زدایی چاپ کنید. اسکریپتی بنویسید که از ورودی استاندارد فایلی بخواند حاوی:

name
state_a
state_b
:

فایل name.h را تولید کند:

c
extern const char* NAME_names[];
typedef enum {
   state_a,
   state_b,
   :
} NAME;

و فایل name.c:

c
const char* NAME_names[] = {
    "state_a",
    "state_b",
      :
};

پاسخ در صفحهٔ ۲۸۶ — تمرین ۱۲. نیمهٔ راه نوشتن این کتاب فهمیدیم use strict را در بسیاری از مثال‌های Perl نگذاشته‌ایم. اسکریپتی بنویسید که فایل‌های .pl در یک پوشه را بگردد و use strict را در انتهای بلوک کامنت اولیه به همهٔ فایل‌هایی که ندارند اضافه کند. از همهٔ فایل‌هایی که تغییر می‌دهید پشتیبان بگیرید.


⁸ اینجا router یعنی ابزاری که تیغ‌های برشی را خیلی خیلی سریع می‌چرخاند، نه دستگاه اتصال شبکه‌ها.


۲۰ — مولدهای کد

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

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

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

TIP 29

کدی بنویسید که کد بنویسد

دو نوع اصلی مولد کد وجود دارد:

  1. مولدهای کد غیرفعال (passive) یک‌بار اجرا می‌شوند تا نتیجه تولید کنند. از آن نقطه به بعد، نتیجه مستقل می‌شود — از مولد کد جداست. جادوگرهای بحث‌شده در جادوگران شیطانی، صفحهٔ ۱۹۸، به‌همراه برخی ابزارهای CASE، نمونهٔ مولدهای غیرفعال‌اند.

  2. مولدهای کد فعال (active) هر بار که نتیجهٔشان لازم است استفاده می‌شوند. نتیجه یک‌بارمصرف است — همیشه می‌تواند توسط مولد کد دوباره تولید شود. اغلب مولدهای فعال نوعی اسکریپت یا فایل کنترل می‌خوانند تا نتیجه تولید کنند.

مولدهای کد غیرفعال

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

کاربردهای زیادی دارند:

  • ایجاد فایل‌های سورس جدید. مولد غیرفعال می‌تواند قالب‌ها، دستورالعمل‌های کنترل منبع، اعلان‌های حق نشر و بلوک‌های کامنت استاندارد برای هر فایل جدید در پروژه تولید کند. ویرایشگرهایمان طوری تنظیم‌اند که هر وقت فایل جدید می‌سازیم این کار را انجام دهند: فایل Java جدید ویرایش کنید و بافر ویرایشگر خودکار بلوک کامنت، دستور package و طرح کلی کلاس را از قبل پر شده دارد.

  • تبدیل یک‌باره بین زبان‌های برنامه‌نویسی. نوشتن این کتاب را با سیستم troff شروع کردیم، اما بعد از ۱۵ بخش به LaTeX رفتیم. مولد کدی نوشتیم که سورس troff را خواند و به LaTeX تبدیل کرد. حدود ۹۰٪ دقیق بود؛ بقیه را دستی انجام دادیم. این ویژگی جالبی مولدهای غیرفعال است: لازم نیست کاملاً دقیق باشند. خودتان انتخاب می‌کنید چقدر روی مولد سرمایه‌گذاری کنید در مقابل انرژی‌ای که صرف اصلاح خروجیش می‌کنید.

  • تولید جداول جستجو و منابع دیگری که در زمان اجرا گران محاسبه‌اند. به‌جای محاسبهٔ توابع مثلثاتی، بسیاری از سیستم‌های گرافیکی اولیه از جداول ازپیش‌محاسبه‌شدهٔ سینوس و کسینوس استفاده می‌کردند. معمولاً این جداول با مولد غیرفعال تولید و در سورس کپی می‌شدند.

مولدهای کد فعال

در حالی که مولدهای غیرفعال صرفاً راحتی‌اند، همتایان فعال‌شان اگر بخواهید اصل DRY را رعایت کنید ضروری‌اند. با مولد فعال می‌توانید یک نمایش از قطعه‌ای دانش بگیرید و به همهٔ قالبی که برنامه نیاز دارد تبدیل کنید. این تکرار نیست، چون قالب‌های مشتق یک‌بارمصرف‌اند و در صورت نیاز توسط مولد تولید می‌شوند (از این رو «فعال»).

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

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

جایگزین استفاده از مولد فعال است — طرح را بگیرید و از آن کد سورس ساختارها را تولید کنید، همان‌طور که در شکل ۳.۳ نشان داده شده. حالا هر وقت طرح تغییر کند، کد دسترسی هم خودکار تغییر می‌کند. اگر ستونی حذف شود، فیلد مربوط در ساختار هم ناپدید می‌شود و هر کد سطح بالاتری که از آن ستون استفاده می‌کند کامپایل نمی‌شود. خطا را در زمان کامپایل گرفته‌اید، نه در تولید. البته این طرح فقط وقتی کار می‌کند که تولید کد بخشی از خود فرایند build باشد.⁹

شکل ۳.۳ — مولد کد فعال کد را از طرح پایگاه داده می‌سازد

Schema                          active code generator          struct EmployeeRow
 table employee          →                              →      struct EmployerRow
 table employer                                                 struct BenefitRow
 table benefit

مثال دیگر ادغام محیط‌ها با مولدهای کد وقتی است که زبان‌های مختلف در یک برنامه استفاده می‌شوند. برای ارتباط، هر پایگاه کد به اطلاعات مشترک نیاز دارد — ساختارهای داده، قالب پیام، نام فیلدها و غیره. به‌جای تکرار این اطلاعات، از مولد کد استفاده کنید. گاهی می‌توانید اطلاعات را از فایل‌های سورس یک زبان تجزیه و برای تولید کد در زبان دوم استفاده کنید. اغلب اما ساده‌تر است آن را در نمایش ساده‌تر و زبان‌خنثی بیان کنید و کد هر دو زبان را تولید کنید، همان‌طور که در شکل ۳.۴ آمده. همچنین پاسخ تمرین ۱۳ در صفحهٔ ۲۸۶ را ببینید برای مثال جداسازی تجزیهٔ نمایش تخت از تولید کد.

شکل ۳.۴ — تولید کد از نمایش زبان‌خنثی. در فایل ورودی، خطوط شروع‌شده با M شروع تعریف پیام، خطوط F فیلدها و E پایان پیام را مشخص می‌کنند.

#   Add a product
#   to the 'on-order' list
M   AddProduct
F   id         int
F   name       char[30]
F   order_code int
E
/* Add a product */                      { Add a product }
/* to the 'on-order' list */             { to the 'on-order' list }
typedef struct {                         AddProductMsg = packed record
  int   id;                                id:         LongInt;
  char name[30];                           name:       array[0..29] of char;
  int   order_code;                        order_code: LongInt;
} AddProductMsg;                         end;

مولدهای کد لازم نیست پیچیده باشند

همهٔ این حرف از فعال و غیرفعال ممکن است تصور کند مولدهای کد موجودات پیچیده‌اند. لازم نیست. معمولاً پیچیده‌ترین بخش parser است که فایل ورودی را تحلیل می‌کند. قالب ورودی را ساده نگه دارید و مولد کد ساده می‌شود. به پاسخ تمرین ۱۳ (صفحهٔ ۲۸۶) نگاه کنید: خود تولید کد اساساً دستورات print است.

مولدهای کد لازم نیست کد تولید کنند

اگرچه بسیاری از مثال‌های این بخش مولدهایی را نشان می‌دهند که سورس برنامه تولید می‌کنند، این همیشه لازم نیست. می‌توانید از مولدهای کد برای نوشتن تقریباً هر خروجی استفاده کنید: HTML، XML، متن ساده — هر متنی که ممکن است ورودی جای دیگری در پروژه باشد.

بخش‌های مرتبط:

  • شرهای تکرار، صفحهٔ ۲۶
  • قدرت متن ساده، صفحهٔ ۷۳
  • جادوگران شیطانی، صفحهٔ ۱۹۸
  • خودکارسازی همه‌جا، صفحهٔ ۲۳۰

Exercises

پاسخ در صفحهٔ ۲۸۶ — تمرین ۱۳. مولد کدی بنویسید که فایل ورودی شکل ۳.۴ را بگیرد و خروجی را به دو زبان انتخابی‌تان تولید کند. سعی کنید افزودن زبان‌های جدید آسان باشد.


⁹ چطور از طرح پایگاه داده کد بسازید؟ چند راه هست. اگر طرح در فایل تخت است (مثلاً به‌صورت دستورات create table)، اسکریپت نسبتاً ساده‌ای می‌تواند آن را تجزیه و سورس تولید کند. یا اگر ابزاری برای ایجاد مستقیم طرح در پایگاه داده استفاده می‌کنید، باید بتوانید اطلاعات لازم را مستقیماً از فرهنگ دادهٔ پایگاه استخراج کنید. Perl کتابخانه‌هایی دارد که به بیشتر پایگاه‌های اصلی دسترسی می‌دهند.