حالت تاریک
فصل ۲ — رویکرد عملگرایانه
نکتهها و ترفندهایی وجود دارد که در همهٔ سطوح توسعهٔ نرمافزار کاربرد دارند — ایدههایی که تقریباً بدیهیاند و فرایندهایی که عملاً جهانیاند. با این حال، این رویکردها بهندرت بهصورت صریح مستند شدهاند؛ بیشتر آنها را بهصورت جملات پراکنده در بحثهای طراحی، مدیریت پروژه یا coding پیدا میکنید.
در این فصل این ایدهها و فرایندها را کنار هم میآوریم. دو بخش اول، «شرِّ تکرار» و «متعامدی»، ارتباط نزدیکی دارند. اولی شما را از تکرار knowledge در سراسر systemها برمیگرداند؛ دومی از پخش کردن یک تکه knowledge در چند component مختلف system.
با افزایش سرعت تغییر، نگه داشتن applicationها در relevance سختتر میشود. در «برگشتپذیری»، تکنیکهایی را میبینیم که پروژههایتان را در برابر محیط در حال تغییر عایق میکنند.
دو بخش بعدی نیز مرتبطاند. در «گلولههای ردیاب»، دربارهٔ style توسعهای صحبت میکنیم که همزمان جمعآوری requirement، test طراحی و implement کردن code را ممکن میسازد. اگر خیلی خوب به نظر میرسد، حق با شماست: توسعه با گلولهٔ ردیاب همیشه قابل اعمال نیست. وقتی نیست، «نمونه اولیه و یادداشت Post-it» نشان میدهد چطور با prototyping معماری، algorithm، interface و ایدهها را test کنید.
با بالغ شدن تدریجی علوم کامپیوتر، designerها languageهای سطح بالاتری میسازند. compilerای که «make it so» را بپذیرد هنوز اختراع نشده، اما در «زبانهای حوزه» پیشنهادهای فروتنانهای داریم که خودتان میتوانید implement کنید.
در پایان، همهٔ ما در دنیایی با time و resource محدود کار میکنیم. اگر در برآورد زمان کارها مهارت پیدا کنید، میتوانید هر دو کمبود را بهتر تحمل کنید — و bossها را راضیتر نگه دارید؛ این موضوع را در «برآورد» پوشش میدهیم.
با نگه داشتن این اصول بنیادین در ذهن حین توسعه، code بهتری، سریعتر و قویتر مینویسید. حتی میتوانید کاری کنید آسان به نظر برسد.
شرِّ تکرار
دادن دو تکه knowledge متناقض به computer، روش ترجیحی Captain James T. Kirk برای از کار انداختن هوش مصنوعی مهاجم بود. متأسفانه، همان اصل میتواند code شما را هم زمین بزند.
بهعنوان programmer، knowledge جمع میکنیم، سازماندهی میکنیم، نگه میداریم و از آن بهره میبریم. knowledge را در specification مستند میکنیم، در running code زنده میکنیم و برای checkهای لازم در testing از آن استفاده میکنیم.
متأسفانه، knowledge پایدار نیست. تغییر میکند — اغلب سریع. درک شما از یک requirement ممکن است بعد از جلسه با client عوض شود. دولت regulationای را تغییر میدهد و بخشی از business logic کهنه میشود. testها ممکن است نشان دهند algorithm انتخابشده کار نمیکند. همهٔ این ناپایداری یعنی بخش بزرگی از وقتمان را در maintenance mode میگذرانیم و knowledge system را دوباره سازماندهی و بیان میکنیم.
بیشتر مردم فرض میکنند maintenance وقتی شروع میشود که application release شده — یعنی fix کردن bug و افزودن feature. ما فکر میکنیم اشتباه میکنند. programmerها دائماً در maintenance mode هستند. درک ما روزبهروز عوض میشود. requirement جدید وقتی design یا code میکنیم میرسد. شاید environment عوض شود. هر دلیلی که باشد، maintenance فعالیت گسسته نیست؛ بخش روزمرهٔ کل فرایند توسعه است.
وقتی maintenance انجام میدهیم، باید representation چیزها را — آن capsuleهای knowledge جاسازیشده در application — پیدا و تغییر دهیم. مشکل این است که تکرار knowledge در specification، process و programهایی که میسازیم آسان است؛ و وقتی این کار را بکنیم، کابوس maintenance را دعوت میکنیم — کابوسی که خیلی قبل از ship شدن application شروع میشود.
ما معتقدیم تنها راه توسعهٔ reliable نرمافزار — و developmentهایی که فهم و نگهداریشان آسانتر است — پیروی از اصلی است که DRY principle مینامیم:
هر تکه knowledge باید یک representation واحد، بدون ابهام و authoritative در system داشته باشد.
چرا DRY مینامیمش؟
TIP 11 — DRY — Don't Repeat Yourself
جایگزین این است که همان چیز در دو یا چند جا بیان شود. اگر یکی را عوض کنید، باید یادتان باشد بقیه را هم عوض کنید — وگرنه، مثل computerهای alien، program با تناقض از پا درمیآید. مسئلهٔ «آیا یادتان میماند» نیست؛ مسئلهٔ «کی فراموش میکنید» است.
DRY principle بارها در این کتاب برمیگردد، اغلب در contextهایی که ربطی به coding ندارند. ما فکر میکنیم یکی از مهمترین ابزارهای toolbox برنامهنویس عملگراست.
در این بخش مشکلات duplication را ترسیم میکنیم و strategy کلی برای برخورد با آن پیشنهاد میدهیم.
چگونه تکرار پدید میآید؟
بیشتر duplicationهایی که میبینیم در یکی از دستههای زیر میافتد:
- تکرار تحمیلشده (Imposed duplication). developerها احساس میکنند انتخابی ندارند — environment duplication میطلبد.
- تکرار ناخواسته (Inadvertent duplication). developerها نمیفهمند information را duplicate کردهاند.
- تکرار از impatience (Impatient duplication). developerها lazy میشوند و duplicate میکنند چون آسانتر به نظر میرسد.
- تکرار بین developerها (Interdeveloper duplication). چند نفر در team (یا teamهای مختلف) یک تکه information را duplicate میکنند.
بیایید این چهار i تکرار را دقیقتر ببینیم.
تکرار تحمیلشده
گاهی duplication مجبورمان میکند. استاندارد پروژه ممکن است documentهایی بخواهد که information تکراری دارند، یا documentهایی که information code را duplicate میکنند. چند target platform هر کدام language، library و development environment خود را میخواهند و ما definition و procedure مشترک را duplicate میکنیم. خود languageها structureهایی میخواهند که information را duplicate میکنند. همهٔ ما در موقعیتهایی بودهایم که احساس بیقدرتی در برابر duplication کردیم. با این حال، اغلب راهی هست هر تکه knowledge در یک جا بماند، DRY principle رعایت شود و زندگیمان هم آسانتر شود. چند technique:
چند representation از information. در سطح coding، اغلب همان information را در formهای مختلف لازم داریم. شاید client-server application مینویسید با language متفاوت روی client و server و structure مشترکی روی هر دو لازم است. شاید classی لازم است که attributeهایش schema جدول database را mirror کند. شاید کتاب مینویسید و excerpt از programهایی را میخواهید که compile و test هم میکنید.
با کمی ingenuity معمولاً میتوان نیاز به duplication را حذف کرد. اغلب پاسخ نوشتن filter یا code generator ساده است. structureها در languageهای مختلف میتوانند از metadata representation مشترک با code generator ساده هر بار build ساخته شوند (مثالی در Figure 3.4، صفحهٔ 106). class definitionها میتوانند خودکار از schema آنلاین database یا metadata ساخت schema ساخته شوند. code extractهای این کتاب با preprocessor هر بار format متن insert میشوند. trick این است که process فعال باشد: نباید conversion یکباره باشد — وگرنه دوباره duplicate data داریم.
Documentation در code. به programmerها یاد میدهند code را comment کنند: code خوب comment زیاد دارد. متأسفانه، هرگز یاد نمیدهند چرا code به comment نیاز دارد: code بد comment زیاد میخواهد.
DRY principle میگوید knowledge سطح پایین در code بماند — جایی که جایش است — و comment برای توضیح سطح بالا رزرو شود. وگرنه knowledge را duplicate کردهایم و هر تغییر یعنی هم code و هم comment. commentها ناگزیر کهنه میشوند و comment غیرقابل اعتماد بدتر از نبود comment است. (برای اطلاعات بیشتر دربارهٔ comment، «It's All Writing»، صفحهٔ 248.)
Documentation و code. documentation مینویسید، بعد code. چیزی عوض میشود؛ documentation و code را amend میکنید. هر دو representation همان knowledge را دارند. و همه میدانیم در فشار deadline و client مهم، بهتعویق انداختن بهروزرسانی documentation رایج است.
Dave یکبار روی telex switch بینالمللی کار کرد. client — که طبیعی است — test specification exhaustive خواست و software باید در هر delivery همهٔ testها را pass کند. برای اطمینان از اینکه testها specification را دقیق reflect میکنند، team آنها را programmatically از خود document تولید کرد. وقتی client specification را amend کرد، test suite خودکار عوض شد. وقتی team client را متقاعد کرد procedure درست است، تولید acceptance test معمولاً چند ثانیه طول میکشید.
مسائل language. languageهای زیادی duplication قابل توجهی در source تحمیل میکنند. اغلب وقتی language interface ماژول را از implementation جدا میکند. C و C++ header file دارند که name و type information متغیر، function و (در C++) class exportشده را duplicate میکند. Object Pascal حتی در همان file duplicate میکند. اگر remote procedure call یا CORBA [URL 29] استفاده میکنید، interface information بین interface specification و code implementکننده duplicate میشود.
technique آسان برای غلبه بر requirementهای language نیست. بعضی development environment نیاز header file را با تولید خودکار پنهان میکنند و Object Pascal اعلان function تکراری را کوتاه میکند، اما معمولاً با آنچه دارید سر و کار دارید. حداقل در بیشتر مسائل language-based، header file ناسازگار با implementation خطای compilation یا linkage میدهد. هنوز میتوانید اشتباه کنید، اما زودتر خبردار میشوید.
به comment در header و implementation file هم فکر کنید. هیچ فایدهای ندارد header comment function یا class را بین دو file duplicate کنید. header file برای document کردن مسائل interface؛ implementation file برای جزئیات nitty-gritty که consumerهای code لازم ندارند.
تکرار ناخواسته
گاهی duplication نتیجهٔ اشتباه در design است.
مثالی از صنعت distribution. فرض کنید analysis نشان میدهد truck علاوه بر attributeهای دیگر type، license number و driver دارد. delivery route ترکیب route، truck و driver است. classهایی بر این اساس مینویسیم.
اما وقتی Sally مریض میزند و driver را عوض کنیم چه؟ هم Truck و هم DeliveryRoute driver دارند. کدام را عوض کنیم؟ clearly این duplication بد است. normalize کنید طبق business model — آیا truck واقعاً driver را در attribute set پایهاش دارد؟ route؟ شاید object سومی لازم است driver، truck و route را به هم ببندد. هر راهحل نهایی که باشد، از این دادهٔ unnormalized دوری کنید.
نوع کمی کمتر آشکار unnormalized data وقتی چند data element بههم وابستهاند:
cpp
class Line {
public:
Point start;
Point end;
double length;
};در نگاه اول class منطقی به نظر میرسد. line start و end دارد و همیشه length دارد (حتی اگر صفر). اما duplication داریم. length با start و end تعریف میشود: یکی از pointها عوض شود length عوض میشود. بهتر است length فیلد محاسبهشده باشد:
cpp
class Line {
public:
Point start;
Point end;
double length() { return start.distanceTo(end); }
};بعداً در توسعه ممکن است DRY principle را بهخاطر performance نقض کنید. اغلب وقتی data را cache میکنید تا operation گران تکرار نشود. trick محدود کردن impact است. نقض به بیرون expose نمیشود: فقط methodهای داخل class باید consistency را حفظ کنند.
cpp
class Line {
private:
bool changed;
double length;
Point start;
Point end;
public:
void setStart(Point p) { start = p; changed = true; }
void setEnd(Point p) { end = p; changed = true; }
Point getStart(void) { return start; }
Point getEnd(void) { return end; }
double getLength() {
if (changed) {
length = start.distanceTo(end);
changed = false;
}
return length;
}
};این مثال issue مهمی برای languageهای object-oriented مثل Java و C++ هم نشان میدهد: تا جایی که ممکن است همیشه accessor function برای خواندن و نوشتن attribute object استفاده کنید.¹ بعداً افزودن functionality مثل caching آسانتر میشود.
¹ استفاده از accessor function با Uniform Access principle Meyer [Mey97b] مرتبط است: «همهٔ serviceهای یک ماژول باید از notation یکنواخت در دسترس باشند، بدون اینکه مشخص شود از storage یا computation implement شدهاند.»
تکرار از عجله
هر پروژه فشار time دارد — نیروهایی که بهترینها را به shortcut سوق میدهد. routine مشابه چیزی که نوشتهاید لازم است؟ وسوسه میشوید original را copy کنید و چند تغییر بدهید. value برای حداکثر point لازم است؟ اگر header file را عوض کنم کل پروژه rebuild میشود. شاید literal number اینجا؛ و اینجا؛ و اینجا. class مثل یکی در Java runtime لازم است؟ source در دسترس است — پس چرا copy نکنید و تغییرات لازم را بدهید (با رعایت license)؟
اگر این وسوسه را حس کردید، آن ضربالمثل کهنه را یادآوری کنید: «shortcutها تأخیر طولانی میآورند.» شاید چند ثانیه الان save کنید، اما احتمال از دست دادن ساعتها بعد. به مسائل Y2K فکر کنید — بسیاری از laziness developerها در parameterize نکردن اندازهٔ date field یا implement نکردن library متمرکز date service بود.
تکرار از عجله form آسان detect و handle است، اما discipline و willingness برای وقت upfront میخواهد تا بعداً درد save شود.
تکرار بین developerها
شاید سختترین نوع duplication برای detect و handle بین developerهای مختلف پروژه است. set کامل functionality ممکن است ناخواسته duplicate شود و سالها undetected بماند و maintenance problem بسازد. از نزدیک شنیدیم ایالتی در آمریکا systemهای computer دولتی را برای Y2K compliance survey کرد. audit بیش از ۱۰٬۰۰۰ program پیدا کرد که هر کدام version validation شماره Social Security خود را داشت.
در سطح بالا، با design روشن، technical project leader قوی (صفحهٔ 228 در Pragmatic Teams) و تقسیم responsibility روشن در design برخورد کنید. اما در سطح ماژول، مشکل insidiousتر است. functionality یا data پرکاربرد که در area responsibility آشکار نیست، بارها implement میشود.
بهترین راه برخورد: communication فعال و مکرر بین developerها. forum برای بحث مشکل مشترک. (در پروژههای گذشته Usenet newsgroup خصوصی برای exchange ایده و سؤال گذاشتیم — communication غیر مزاحم، حتی cross-site، با history دائمی.) یک عضو team را librarian پروژه منصوب کنید تا exchange knowledge را تسهیل کند. جای مرکزی در source tree برای utility routine و script. عمداً source code و documentation دیگران را بخوانید — informal یا در code review. snoop نمیکنید — یاد میگیرید. access متقابل است — دربارهٔ کندن (دستکاری؟) code شما هم عصبانی نشوید.
TIP 12 — Make It Easy to Reuse
میخواهید environment بسازید که پیدا و reuse کردن چیز موجود از نوشتن خودتان آسانتر باشد. اگر آسان نباشد، مردم نمیکنند. اگر reuse نکنید، knowledge duplicate میکنید.
بخشهای مرتبط: Orthogonality، صفحهٔ 34؛ Text Manipulation، صفحهٔ 99؛ Code Generators، صفحهٔ 102؛ Refactoring، صفحهٔ 184؛ Pragmatic Teams، صفحهٔ 224؛ Ubiquitous Automation، صفحهٔ 230؛ It's All Writing، صفحهٔ 248.
متعامدی
متعامدی (Orthogonality) concept بحرانی است اگر systemهایی میخواهید که design، build، test و extend آسان باشند. اما concept متعامدی بهندرت مستقیم teach میشود — اغلب feature ضمنی method و technique دیگر است. این اشتباه است. وقتی principle متعامدی را مستقیم apply کنید، فوراً quality systemهایی که produce میکنید بهتر میشود.
متعامدی چیست؟
«Orthogonality» اصطلاحی از geometry است. دو خط orthogonalاند اگر در زاویهٔ قائمه meet کنند، مثل محورهای نمودار. در termهای vector، دو خط independentاند. روی یکی حرکت کنید، موقعیت projected روی دیگری عوض نمیشود.
در computing، independence یا decoupling را denote میکند. دو یا چند چیز orthogonalاند اگر تغییر در یکی روی دیگران effect نگذارد. در system well-designed، database code با user interface orthogonal است: interface را عوض کنید database تحت تأثیر نیست؛ database را swap کنید interface عوض نمیشود.
قبل از benefitهای systemهای orthogonal، systemی که orthogonal نیست را ببینیم.
یک system غیرمتعامد
در tour هلیکوپتر Grand Canyon هستید؛ pilot که اشتباه واضح ناهار ماهی خورد، ناگهان ناله میکند و غش میکند. خوشبختانه ۱۰۰ فوت بالای زمین hover گذاشته. منطقی فکر میکنید collective pitch lever² lift کلی را control میکند — پس کمی پایین بیاورید descent ملایم شروع میشود. اما وقتی امتحان میکنید، زندگی آنقدر ساده نیست. دماغهٔ هلیکوپتر میافتد و spiral به چپ شروع میکنید. systemی است که هر control input effect ثانویه دارد. lever چپ را پایین بیاورید باید حرکت compensating به عقب به stick راست و pedal راست بدهید — و هر تغییر دوباره همهٔ controlها را تحت تأثیر میگذارد. system unbelievably پیچیده؛ هر تغییر همهٔ inputها را impact میکند. workload phenomenal: دست و پا مدام حرکت میکنند تا forceهای interacting balance شوند.
control هلیکوپتر clearly orthogonal نیست.
² هلیکوپتر چهار control پایه دارد. cyclic دست راست است — حرکتش هلیکوپتر را در همان جهت میبرد. collective pitch lever دست چپ — بالا بکشید pitch همهٔ bladeها زیاد و lift تولید میشود. throttle انتهای pitch lever. دو pedal پا thrust tail rotor و چرخش را vary میکنند.
مزایای متعامدی
مثل مثال هلیکوپتر، systemهای nonorthogonal ذاتاً برای change و control پیچیدهترند. وقتی componentهای system highly interdependent باشند، fix محلی وجود ندارد.
TIP 13 — Eliminate Effects Between Unrelated Things
componentهای self-contained میخواهیم: independent با purpose واحد و well-defined (آنچه Yourdon و Constantine cohesion [YC86] مینامند). وقتی componentها از هم isolated باشند، میدانید یکی را عوض کنید بدون worry از بقیه — تا interface خارجی component را عوض نکنید، مشکل ripple در کل system ایجاد نمیکنید.
دو benefit بزرگ از systemهای orthogonal: productivity بیشتر و risk کمتر.
افزایش productivity
- تغییرات localized — development time و testing time کمتر. component کوچک self-contained نوشتن از block بزرگ code آسانتر. component ساده design، code، unit test و فراموش — نیازی به تغییر مداوم code موجود با افزودن code جدید نیست.
- رویکرد orthogonal reuse را promote میکند. component با responsibility مشخص و well-defined با component جدید ترکیب میشود — حتی اگر implementor اصلی تصور نکرده. system loosely coupled آسانتر reconfigure و reengineer میشود.
- gain productivity ظریف وقتی componentهای orthogonal combine میکنید: component یکی m کار متمایز، دیگری n کار. اگر orthogonal combine کنید، نتیجه mn کار. اگر orthogonal نباشند overlap هست و نتیجه کمتر. functionality بیشتر per unit effort با combine کردن componentهای orthogonal.
کاهش risk
- بخش diseased code isolated. ماژول sick symptom را کمتر spread میکند؛ slice out و transplant آسانتر.
- system کمتر fragile. تغییر و fix کوچک در area خاص — مشکل محدود همان area.
- system orthogonal احتمالاً better tested — design و run test روی component آسانتر.
- کمتر به vendor، product یا platform خاص tied — interface به third-party component در بخش کوچکتر development isolated.
کاربرد principle متعامدی
تیمهای پروژه
teamهای efficient همه میدانند چه کنند و fully contribute میکنند؛ teamهای دیگر مدام bicker میکنند و از سر راه هم بیرون نمیآیند. اغلب issue متعامدی است. team با overlap زیاد — confusion دربارهٔ responsibility. هر تغییر meeting کل team میخواهد چون هر کس ممکن است affected باشد.
team را چطور با responsibility well-defined و overlap minimal organize کنید؟ پاسخ ساده نیست — به پروژه، analysis areaهای potential change و افراد available بستگی دارد. ترجیح ما: infrastructure از application جدا. هر component infrastructure major (database، communication interface، middleware و غیره) subteam خود. هر division واضح application functionality هم. بعد grouping را با افراد adjust کنید.
measure informal متعامدی structure team: چند نفر برای discuss هر change requested لازم است؟ عدد بزرگتر = group کمتر orthogonal. team orthogonal efficientتر. (با این حال subteamها را encourage میکنیم constantly communicate کنند.)
Design
بیشتر developer با design system orthogonal آشنااند — شاید با کلمات modular، component-based، layered. system از ماژول cooperating با functionality independent compose شود. گاهی layer — هر layer abstraction سطح پایینتر. layering powerful برای design orthogonal: هر layer فقط abstraction layer پایینتر — flexibility در change implementation underlying بدون effect روی code. layering risk runaway dependency بین ماژول را کم میکند. layering در diagramهایی مثل Figure 2.1.
Figure 2.1. نمودار layer معمول
User Interface
Database Report Business
access engine logic
Application framework
Standard C library
Operating systemtest آسان design orthogonal: componentها را map کردید — بپرسید اگر requirement پشت function خاص را dramatically عوض کنم، چند ماژول affected؟ در system orthogonal پاسخ «یک»³. جابهجایی button روی GUI panel نباید database schema را عوض کند. context-sensitive help نباید billing subsystem را عوض کند.
system پیچیده monitor و control plant heating: requirement اصلی GUI؛ بعد voice response با control تلفن touchtone. در design orthogonal فقط ماژول user interface برای این عوض میشود — logic کنترل plant ثابت. با structure درست، هر دو interface روی همان code base underlying. «It's Just a View»، صفحهٔ 157، MVC — در این موقعیت خوب کار میکند.
³ در واقعیت naive است. مگر remarkably lucky باشید، بیشتر change requirement چند function را affect میکند. اما هر functional change ideally یک ماژول.
design چقدر از change دنیای واقعی decoupled است؟ telephone number بهعنوان customer identifier؟ وقتی phone company area code reassign کند چه؟ به property چیزهایی که control ندارید تکیه نکنید.
Toolkitها و libraryها
متعامدی system را با introduce کردن third-party toolkit و library حفظ کنید. technology را wisely انتخاب کنید.
پروژهای Java code باید locally روی server و remotely روی client اجرا شود. RMI و CORBA. با RMI هر call remote method ممکن است exception — implementation naive باید exception handle کند هر وقت remote class استفاده شود. RMI clearly orthogonal نیست: code calling remote class نباید location بداند. CORBA این restriction نداشت.
toolkit (یا library از teammate) bring in کنید — آیا change روی code تحمیل میکند که نباید باشد؟ persistence scheme transparent = orthogonal. اگر object را special way create/access کنید = not orthogonal. isolate کردن detail از code vendor را بعداً عوض کردن آسانتر میکند.
Enterprise Java Beans (EJB) مثال جالب متعامدی: در system transaction-oriented، application code start/end transaction را delineate میکند. با EJB این metadata declarative خارج code — همان application code در environment transaction EJB مختلف بدون change. model برای environmentهای آینده.
Aspect-Oriented Programming (AOP) در Xerox Parc ([KLM 97] و [URL 49]): behavior که otherwise در source پخش است در یک جا express. log message معمولاً explicit call log function در source. با AOP logging orthogonal به چیز logged. Java AOP — aspect:
java
aspect Trace {
advise * Fred.*(..) {
static before {
Log.write("-> Entering " + thisJoinPoint.methodName);
}
}
}aspect weave شود trace message تولید میشود؛ وگرنه نه. در هر صورت source اصلی unchanged.
Coding
هر بار code مینویسید risk کاهش orthogonality application. مگر constantly monitor کنید چه میکنید و context بزرگتر application — unintentionally functionality duplicate در ماژول دیگر یا knowledge دوباره express.
technique برای حفظ orthogonality:
- code decoupled نگه دارید. shy code — ماژولی که unnecessary به ماژول دیگر reveal نکند و به implementation ماژول دیگر rely نکند. Law of Demeter [LH89] — Decoupling and the Law of Demeter، صفحهٔ 138. state object را عوض کنید object خودش انجام دهد — code isolated از implementation دیگر، orthogonality بیشتر.
- از global data دوری کنید. هر reference global data به component دیگر shareکننده tie میکند. حتی read-only global مشکل (مثلاً multithreaded). code آسانتر فهم و maintain اگر context required را explicitly به ماژول pass کنید — در OO اغلب parameter constructor؛ در code دیگر structure با context و reference.
- Singleton pattern [GHJV95] — یک instance object class خاص. بسیاری singleton را global variable میدانند (مخصوصاً Java). با singleton محتاط — linkage غیرضروری.
- از function مشابه دوری کنید. set function مشابه — common code start/end، algorithm مرکزی متفاوت. duplicate code symptom structural. Strategy pattern در Design Patterns.
عادت critical بودن constant نسبت به code — opportunity reorganize برای structure و orthogonality بهتر. refactoring — بخش Refactoring، صفحهٔ 184.
Testing
system orthogonal design و implement آسانتر test. interaction component formalized و limited — testing بیشتر module level. module level (unit) test specify و perform از integration test آسانتر. هر ماژول unit test خود در code — automated در build (Code That's Easy to Test، صفحهٔ 189).
ساخت unit test خود test orthogonality: برای build و link unit test چه لازم است؟ درصد بزرگ system drag in برای compile/link؟ ماژول well decoupled نیست.
bug fixing هم زمان assess orthogonality کل system. fix چقدر localized؟ یک ماژول یا scatter در system؟ یک change همه را fix یا problem جدید؟ automation: source code control (Source Code Control، صفحهٔ 86) — tag bug fix بعد test. report ماهانه trend تعداد source file affected هر bug fix.
Documentation
شگفتآورانه orthogonality به documentation هم apply میشود. محورها content و presentation. documentation truly orthogonal — appearance dramatically عوض شود بدون change content. word processor مدرن style sheet و macro (It's All Writing، صفحهٔ 248).
زندگی با متعامدی
orthogonality نزدیک DRY principle صفحهٔ 27. DRY: minimize duplication در system. orthogonality: کاهش interdependency component. principle orthogonality با DRY — system flexibleتر، understandableتر، debug، test و maintain آسانتر.
اگر به پروژهای آمدید که مردم desperately struggle برای change و هر change چهار چیز دیگر خراب میکند — nightmare هلیکوپتر را یادآوری کنید. پروژه probably orthogonal design و code نیست. وقت refactor.
و اگر pilot هلیکوپتر هستید، ماهی نخورید.
بخشهای مرتبط: The Evils of Duplication، صفحهٔ 26؛ Source Code Control، صفحهٔ 86؛ Design by Contract، صفحهٔ 109؛ Decoupling and the Law of Demeter، صفحهٔ 138؛ Metaprogramming، صفحهٔ 144؛ It's Just a View، صفحهٔ 157؛ Refactoring، صفحهٔ 184؛ Code That's Easy to Test، صفحهٔ 189؛ Evil Wizards، صفحهٔ 198؛ Pragmatic Teams، صفحهٔ 224؛ It's All Writing، صفحهٔ 248.
Challenges
- تفاوت tool GUI بزرگ Windows با utility خط فرمان کوچک combinable در shell. کدام set orthogonalتر و چرا؟ کدام برای purpose intended آسانتر؟ کدام combine با tool دیگر برای challenge جدید آسانتر؟
- C++ multiple inheritance؛ Java multiple interface. impact روی orthogonality؟ تفاوت multiple inheritance و multiple interface؟ تفاوت delegation و inheritance؟
Exercises
1. class Split که input line را به field split میکند. کدام signature Java orthogonalتر است؟ (پاسخ ص. 279)
java
class Split1 {
public Split1(InputStreamReader rdr) { ...
public void readNextLine() throws IOException { ...
public int numFields() { ...
public String getField(int fieldNo) { ...
}
class Split2 {
public Split2(String line) { ...
public int numFields() { ...
public String getField(int fieldNo) { ...
}2. design orthogonalتر: dialog box modeless یا modal؟ (پاسخ ص. 279)
3. language procedural در برابر object technology — کدام system orthogonalتر؟ (پاسخ ص. 280)
برگشتپذیری
Nothing is more dangerous than an idea if it's the only one you have. — Emil-Auguste Chartier, Propos sur la religion, 1938
مهندسها solution ساده و واحد به problem ترجیح میدهند. test ریاضی که با confidence بگویید x = 3 راحتتر از essay fuzzy دربارهٔ causes انقلاب فرانسه. management با engineer موافق: جواب واحد آسان روی spreadsheet و project plan جا میشود.
کاش دنیای واقعی cooperate میکرد! متأسفانه امروز x = 3 کافی است، فردا شاید x = 4، هفتهٔ بعد x = 2.5. هیچچیز forever نیست — و اگر heavily به fact تکیه کنید، تقریباً guarantee میکنید عوض شود.
همیشه بیش از یک راه implement چیزی هست و بیش از one vendor third-party product. اگر با notion myopic «فقط یک راه» وارد پروژه شوید، surprise ناخوشایند دارید. teamهای زیادی چشمشان forcibly باز میشود:
«اما گفتید database XYZ! ۸۵٪ coding تمام — الان نمیتوانیم عوض کنیم!» programmer اعتراض کرد. «متأسفم، شرکت تصمیم گرفت database PDQ standard شود — برای همهٔ پروژهها. دست من نیست. recode. همه weekend تا اطلاع ثانوی.»
تغییر لازم نیست Draconian یا فوری باشد. با پیشرفت پروژه ممکن است در موقعیت untenable گیر کنید. با هر decision بحرانی، team به target کوچکتر commit میکند — نسخهٔ باریکتر reality با option کمتر.
وقتی decision بحرانی زیاد شد، target آنقدر کوچک که اگر حرکت کند، یا باد جهت عوض کند، یا پروانه در Tokyo بال بزند، miss — شاید بهاندازهٔ huge.
⁴ system nonlinear یا chaotic بگیرید و تغییر کوچک به یک input بدهید — نتیجهٔ بزرگ و often unpredictable. پروانه در Tokyo شروع chain of events که tornado در Texas — شبیه پروژههایی که میشناسید؟
مشکل: decision بحرانی easily reversible نیست.
وقتی vendor database، architectural pattern یا deployment model (client-server در برابر stand-alone) را انتخاب کردید، committed به course of action — undo جز با expense زیاد.
برگشتپذیری
بسیاری topic این کتاب برای software flexible و adaptable است. با recommendationها — مخصوصاً DRY (صفحهٔ 26)، decoupling (صفحهٔ 138)، metadata (صفحهٔ 144) — decision بحرانی irreversible کمتری میگیریم. خوب است چون اولین بار همیشه best decision نیست. به technology commit — بعد skilled people کافی پیدا نمیکنید. third-party vendor lock in — قبل از acquire توسط competitor. requirement، user و hardware سریعتر از develop software عوض میشوند.
فرض کنید early relational database vendor A. بعد performance test — database خیلی slow، object database vendor B سریعتر. در پروژه conventional out of luck — call third-party در code entangled. اما اگر database را abstract کردید — persistence as service — flexibility عوض کردن horse در midstream.
مشابه: پروژه client-server — late marketing server expensive، stand-alone version میخواهد. سخت؟ deployment issue — چند روز. بیشتر = reversibility فکر نکردهاید. برعکس: stand-alone باید client-server یا n-tier deploy شود — نباید سخت باشد.
اشتباه: فرض decision cast in stone — آماده نبودن برای contingency. decision را در sand ساحل بنویسید نه سنگ — موج بزرگ هر وقت wipe out.
TIP 14 — There Are No Final Decisions
معماری flexible
بسیاری code flexible نگه میدارند — architecture، deployment و vendor integration را هم flexible فکر کنید.
CORBA portion پروژه را از change language یا platform development عایق میکند. performance Java platform expectation را برآورده نمیکند؟ client را C++ recode — بقیه unchanged. rules engine C++ flexible نیست؟ Smalltalk version. CORBA — hit فقط component جایگزین.
Unix develop؟ کدام؟ portability concern؟ Windows version خاص؟ 3.1، 95، 98، NT، CE، 2000؟ support version دیگر سخت؟ decision soft و pliable = سخت نیست. poor encapsulation، high coupling، hard-coded logic = شاید impossible.
marketing deployment system را نمیداند؟ upfront فکر کنید — stand-alone، client-server یا n-tier با configuration file. program نوشتهایم که همین کار را میکند.
third-party product پشت interface abstract well-defined پنهان — همیشه توانستهایم. اگر cleanly isolate نشد — statement در code — requirement در metadata، mechanism automatic مثل Aspects (صفحهٔ 39) یا Perl insert. هر mechanism reversible — automatic add = automatic remove.
کسی future را نمیداند — ما هم نه! code را rock-n-roll enable کنید: rock on وقتی میتواند، roll with punches وقتی باید.
بخشهای مرتبط: Decoupling and the Law of Demeter، صفحهٔ 138؛ Metaprogramming، صفحهٔ 144؛ It's Just a View، صفحهٔ 157.
Challenges
کمی quantum mechanics با گربه Schrödinger. گربه در جعبه بسته + ذره radioactive — ۵۰٪ fission دو قطعه — گربه میمیرد؛ وگرنه OK. گربه مرده یا زنده؟ Schrödinger: هر دو. هر reaction sub-nuclear با دو outcome — universe clone. یکی event occurred، دیگر نه. گربه زنده یک universe، مرده دیگر. تا جعبه باز نکنید نمیدانید کدام universe.
coding برای future سخت است.
code evolution را مثل جعبه Schrödinger ببینید: هر decision version متفاوت future. code چند future ممکن support؟ کدام likely؟ support سخت؟
جرأت باز کردن جعبه را دارید؟
گلولههای ردیاب
Ready, fire, aim
دو راه شلیک machine gun در تاریکی:⁵ target دقیق (range، elevation، azimuth)، شرایط environment (دما، رطوبت، فشار هوا، باد)، specification cartridge و bullet، interaction با gun — table یا firing computer bearing و elevation barrel. همه exact — bullet نزدیک target. یا tracer bullet.
tracer bullet در فاصله روی ammo belt کنار ammunition معمولی. fire شود phosphorus ignite — trail از gun تا hit. tracer به target برخورد کند — bullet معمولی هم.
tracer به calculation ترجیح — feedback فوری؛ همان environment ammunition واقعی — effect خارجی minimize.
analogy violent اما برای پروژه جدید — مخصوصاً چیزی که قبلاً ساخته نشده. مثل gunner — target در تاریکی. user system شبیه این ندیده — requirement vague. algorithm، technique، language، library ناآشنا — unknown زیاد. پروژه time میبرد — environment عوض میشود قبل از تمام.
پاسخ classic: specify to death — کاغذ requirement، unknown tie down، environment constrain. fire با dead reckoning — calculation بزرگ upfront، shoot و hope.
Pragmatic Programmer ترجیح tracer bullet.
⁵ pedantic: راههای زیاد — از جمله چشم بسته spray. analogy است — liberty مجاز.
Code که در تاریکی میدرخشد
tracer bullet همان environment و constraint bullet واقعی — سریع به target — gunner feedback فوری. از نظر practical relatively cheap.
در code: از requirement به aspect سیستم نهایی — سریع، visible، repeatable.
TIP 15 — Use Tracer Bullets to Find the Target
client-server database marketing پیچیده. requirement: specify و execute temporal query. server range relational و specialized database. client GUI Object Pascal — C library interface server. query روی server Lisp-like قبل از SQL optimized execution. unknown و environment زیاد — GUI behavior uncertain.
فرصت tracer code. framework front end، library represent query، structure convert stored query به database-specific. assemble — کار میکند. build اول: query همه row جدول — UI با library، library serialize/unserialize query، server SQL از result. ماهها بعد framework flesh out — functionality parallel augment هر component tracer. UI query type جدید — library grow، SQL generation sophisticated.
tracer code disposable نیست — for keeps. error checking، structure، documentation، self-checking مثل production code — فقط fully functional نیست. end-to-end connection component — فاصله تا target، adjust. on target — add functionality آسان.
tracer development: پروژه never finished — change و function add. incremental.
alternative conventional: heavy engineering — module در vacuum code؛ combine subassembly؛ یک روز application کامل — آن وقت user و test.
مزیت tracer:
- user زود چیز working میبیند. expectation communicate (Great Expectations، صفحهٔ 255) — immature میدانند. lack functionality disappoint نمیکند — ecstatic progress. contribute — buy-in. همان user میگویند iteration چقدر close target.
- developer structure کار. daunting: کاغذ خالی. end-to-end interaction در code — team کمتر از thin air. productive، consistency.
- integration platform. system end-to-end connected — environment add code بعد unit test. big-bang integration نیست — integrate هر روز (اغلب چند بار). impact change apparent — interaction limited — debug و test سریعتر accurate.
- چیز برای demo. sponsor و top brass demo inconvenient — tracer همیشه چیزی show.
- feel progress بهتر. use case یکی یکی. done — بعدی. measure performance و demonstrate progress آسانتر. development کوچکتر — monolithic block ۹۵٪ complete هفتهها avoided.
گلولههای ردیاب همیشه به target نمیخورند
tracer نشان میدهد چه hit میکنید — همیشه target نیست. aim adjust تا on target — همان point.
tracer code وقتی ۱۰۰٪ مطمئن نیستید کجا میروید. surprise نکنید اولین attempt miss: user «منظورم این نبود»، data موقع لازم نیست، performance problem likely. change برای نزدیکتر — thankful lean development. body code کوچک — inertia کم — change سریع. feedback و version accurate سریعتر و ارزانتر. هر component major در tracer — user confident reality نه paper spec.
Tracer Code در برابر Prototyping
شاید فکر کنید tracer فقط prototyping با نام aggressive. تفاوت هست. prototype: explore aspect خاص system نهایی. true prototype: throw away — recode proper با lesson.
مثال: application shipper — pack box odd-sized در container. UI intuitive، algorithm packing complex.
prototype UI در GUI tool — responsive کافی. layout agree — throw away — recode با business logic target language. prototype algorithm — functional test Perl، performance test نزدیک machine. decision — start again final environment. prototyping — مفید.
tracer problem دیگر: application as whole چطور hang together. user interaction practice؛ developer architectural skeleton. tracer: trivial packing algorithm (first-come first-served) + UI simple working. component plumbed — framework show user و developer. overtime flesh out — stub complete. framework intact — behavior مثل tracer اول.
تکرار: prototype disposable code. tracer lean اما complete — بخش skeleton system نهایی. prototype reconnaissance قبل از fire tracer.
بخشهای مرتبط: Good-Enough Software، صفحهٔ 9؛ Prototypes and Post-it Notes، صفحهٔ 53؛ The Specification Trap، صفحهٔ 217؛ Great Expectations، صفحهٔ 255.
نمونه اولیه و یادداشت Post-it
صنعتهای مختلف prototype برای idea خاص — prototype از full-scale production ارزانتر. car maker prototype design جدید — aerodynamics، styling، structural. clay model wind tunnel؛ balsa wood و duct tape art department. modeling روی computer — cost کمتر. element risky یا uncertain با cost greatly reduced try.
software prototype هم — analyze و expose risk، correction با cost greatly reduced. مثل car maker — target prototype به aspect خاص.
prototype همیشه code-based نیست. material مختلف — Post-it برای prototype dynamic مثل workflow و application logic. UI: drawing whiteboard، mock-up paint program، interface builder.
prototype جواب few question — cheaper و faster از application production. detail unimportant — GUI prototype result یا data incorrect OK؛ computational/performance aspect — GUI poor یا بدون GUI OK.
اگر environment اجازه detail را نمیدهد — واقعاً prototype میسازید؟ شاید tracer bullet مناسبتر (Tracer Bullets، صفحهٔ 48).
چیزهایی برای prototype
risk carry میکند. tried نشده، critical به system نهایی، unproven/experimental/doubtful، comfortable نیستید. prototype:
- Architecture
- functionality جدید در system موجود
- structure یا content data خارجی
- tool یا component third-party
- issue performance
- design user interface
prototype learning experience — value در code نیست در lesson.
TIP 16 — Prototype to Learn
چگونه از prototype استفاده کنیم
detail قابل ignore:
- Correctness. dummy data.
- Completeness. محدود — یک input، یک menu item.
- Robustness. error checking incomplete — off predefined path crash — OK.
- Style. prototype comment/documentation کم — documentation از experience prototype زیاد؛ روی prototype system کم.
prototype detail gloss — focus aspect خاص. implement با language سطح بالاتر از پروژه (Perl، Python، Tcl). scripting language detail defer — functional ناقص یا slow. UI prototype: Tcl/Tk، Visual Basic، Powerbuilder، Delphi.
scripting glue — combine low-level piece. Windows: Visual Basic + COM. Perl/Python bind C library — hand یا SWIG [URL 28]. assemble component existing — see how work.
Prototyping معماری
prototype کل system — برخلاف tracer، ماژول لازم functional نباشد. شاید code لازم نباشد — whiteboard، Post-it، index card. system as whole — detail defer. area architectural prototype:
- responsibility component major well-defined و appropriate؟
- collaboration component major well-defined؟
- coupling minimize؟
- duplication potential identify؟
- interface definition و constraint acceptable؟
- هر ماژول access path به data لازم در execution — وقتی لازم؟
آخرین item بیشتر surprise و valuable result.
⁶ اگر absolute (نه relative) performance investigate — language نزدیک performance target language.
چگونه از prototype استفاده نکنیم
قبل code-based prototype — همه بفهمند disposable code مینویسید. prototype deceptively attractive برای کسانی که prototype نمیدانند. code disposable، incomplete، unable complete — clear. demonstrated prototype completeness mislead — sponsor/management deploy prototype insist — expectation درست. prototype ماشین balsa wood و duct tape — rush-hour drive نکنید!
culture purpose prototype misinterpret — tracer bullet بهتر — framework solid برای development آینده.
prototype proper: save time، money، pain — identify و correct problem spot early — fix cheap و easy.
بخشهای مرتبط: The Cat Ate My Source Code، صفحهٔ 2؛ Communicate!، صفحهٔ 18؛ Tracer Bullets، صفحهٔ 48؛ Great Expectations، صفحهٔ 255.
Exercises
4. Marketing brainstorm Web-page design — clickable image map — model image (car، phone، house). target page و content — prototype چند. ۱۵ دقیقه. چه tool؟ (پاسخ ص. 280)
زبانهای حوزه
The limits of language are the limits of one's world. — Ludwig Wittgenstein
language کامپیوتر influence فکر دربارهٔ problem و communication. هر language feature list دارد — buzzword مثل static در برابر dynamic typing، early در برابر late binding، inheritance model (single، multiple یا none) — که solution suggest یا obscure میکند. design solution با Lisp در ذهن result متفاوت از solution based on C-style thinking. برعکس — و فکر میکنیم مهمتر — language problem domain هم programming solution suggest میکند.
همیشه سعی میکنیم code با vocabulary application domain بنویسیم (The Requirements Pit، صفحهٔ 210 — project glossary). در بعضی case میتوانیم level بعد برویم و واقعاً با vocabulary، syntax و semantics — language — domain program کنیم.
وقتی به user system proposed گوش میدهید، شاید دقیقاً بگویند system چطور کار کند:
Listen for transactions defined by ABC Regulation 12.3 on a set of X.25 lines, translate them to XYZ Company's format 43B, retransmit them on the satellite uplink, and store for future analysis.
اگر user statement well-bounded زیاد دارد، mini-language tailored به application domain invent کنید که exactly آنچه میخواهند express کند:
From X25LINE1 (Format=ABC123) {
Put TELSTAR1 (Format=XYZ43B);
Store DB;
}language لازم نیست executable — capture requirement — specification. step بعد: implement — specification executable code.
requirement جدید: transaction balance منفی store نشود — برگردد X.25 format اصلی:
From X25LINE1 (Format=ABC123) {
if (ABC123.balance < 0) {
Put X25LINE1 (Format=ABC123);
}
else {
Put TELSTAR1 (Format=XYZ43B);
Store DB;
}
}آسان بود؟ support درست — program نزدیکتر domain. end user در این language program نمیکنند — tool برای شما نزدیکتر domain.
TIP 17 — Program Close to the Problem Domain
language ساده configure/control application یا complex specify rule/procedure — project نزدیکتر problem domain. coding abstraction بالاتر — focus domain problem — detail implementation petty ignore.
user زیاد application: end user business rule و output؛ secondary user: operations، configuration/test manager، support/maintenance programmer، generation آینده developer. هر کدام problem domain — mini-environment و language.
خطاهای domain-specific
programming در problem domain — validation domain-specific — error vocabulary user:
From X25LINE1 (Format=AB123)language general-purpose:
Syntax error: undeclared identifiermini-language:
"AB123" is not a format. Known formats are ABC123,
XYZ43B, PDQB, and 42.implement کردن mini-language
سادهترین: line-oriented easily parsed — switch یا regex Perl. Exercise 5 ص. 281 — C ساده.
پیچیدهتر: syntax formal — BNF⁷ — parser generator. C/C++: yacc/bison [URL 27] — Lex and Yacc [LMB92]. Java: javaCC [URL 26]. Exercise 7 ص. 282 — bison. syntax known — mini-language ساده زیاد work نیست.
راه دیگر: extend existing — Python [URL 9]:
python
record = X25LINE1.get(format=ABC123)
if (record.balance < 0):
X25LINE1.put(record, format=ABC123)
else:
TELSTAR1.put(record, format=XYZ43B)
DB.store(record)⁷ BNF (Backus-Naur Form) — context-free grammar recursive. compiler construction/parsing — BNF exhaustive.
⁸ Thanks to Eric Vought for this example.
Data language و Imperative language
دو usage:
Data language — data structure برای application. configuration.
sendmail — routing email Internet — feature عالی — configuration file هزار خط language خود sendmail:
Mlocal, P=/usr/bin/procmail,
F=lsDFMAw5:/|@qSPfhn9,
S=10/30, R=20/40,
T=DNS/RFC822/X-Unix,
A=procmail -Y -a $h -d $ureadability strength sendmail نیست.
Microsoft data language — menu، widget، dialog Windows resource. Figure 2.2 — readableتر sendmail — همان usage — compile data structure.
Figure 2.2. Windows .rc file
MAIN_MENU MENU
{
POPUP "&File"
{
MENUITEM "&New", CM_FILENEW
MENUITEM "&Open...", CM_FILEOPEN
MENUITEM "&Save", CM_FILESAVE
}
}
MY_DIALOG_BOX DIALOG 6, 15, 292, 287
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
WS_CAPTION | WS_SYSMENU
CAPTION "My Dialog Box"
FONT 8, "MS Sans Serif"
{
DEFPUSHBUTTON "OK", ID_OK, 232, 16, 50, 14
PUSHBUTTON "Help", ID_HELP, 232, 52, 50, 14
CONTROL "Edit Text Control", ID_EDIT1,
"EDIT", WS_BORDER | WS_TABSTOP, 16, 16, 80, 56
CHECKBOX "Checkbox", ID_CHECKBOX1, 153, 65, 42, 38,
BS_AUTOCHECKBOX | WS_TABSTOP
}Imperative language — execute — statement، control construct (script ص. 58).
imperative برای maintenance — integrate legacy در GUI جدید — screen scraping — keystroke و response — mini-language script:
locate prompt "SSN:"
type "%s" social_security_number
type enter
waitfor keyboardunlock
if text_at(10,14) is "INVALID SSN" return bad_ssn
if text_at(10,14) is "DUPLICATE SSN" return dup_ssn
# etc...time enter Social Security — interpreter script — control transaction. interpreter embedded — share data (callback).
programming در domain maintenance programmer. mainframe change — field جابهجا — update high-level description نه grovel C.
Stand-alone و Embedded language
mini-language مستقیم application useful نباشد — specification language artifact (metadata) compile/read/use (Metaprogramming، ص. 144).
Perl generate derivation از schema specification — language مشترک database — SQL، C، Web، XML. application specification مستقیم use نمیکند — output rely.
embed imperative high-level در application — execute runtime — change behavior با change script بدون compile — maintenance dynamic domain simplify.
Easy Development یا Easy Maintenance?
grammar ساده line-oriented تا complex شبیه language واقعی. implement complex extra effort — چرا؟
trade-off: extendibility و maintenance. parse language «واقعی» سختتر write — understand و extend آینده آسانتر. language خیلی ساده parse آسان — cryptic مثل sendmail ص. 60.
application lifetime expected را exceed — upfront grammar complex readable — effort initial many times support و maintenance.
بخشهای مرتبط: Metaprogramming، صفحهٔ 144.
Challenges
- requirement پروژه فعلی در domain-specific language express؟ compiler/translator generate بیشتر code؟
- mini-language — effort implement — framework یک پروژه reuse پروژه دیگر؟
Exercises
5. mini-language control drawing package (turtle-graphics). command حرف تکی — بعضی + number. rectangle: (پاسخ ص. 281)
P 2 # select pen 2
D # pen down
W 2 # draw west 2cm
N 1 # then north 1
E 2 # then east 2
S 1 # then back south
U # pen upparse — simple add command جدید.
6. BNF grammar time specification — همه accept: (پاسخ ص. 282)
4pm, 7:38pm, 23:42, 3:16, 3:16am7. parser Exercise 6 با yacc/bison (پاسخ ص. 282)
8. time parser با Perl — regex parser خوب (پاسخ ص. 283)
برآورد
سریع! ارسال «War and Peace» روی خط modem 56k چقدر طول میکشد؟ برای یک میلیون name و address چقدر فضای disk لازم است؟ عبور یک block 1000-byte از router چقدر زمان میبرد؟ تحویل پروژهتان چند ماه طول میکشد؟
در یک سطح، همهٔ این سؤالها بیمعنا هستند — همه اطلاعات لازم را ندارند. با این حال، اگر در برآورد راحت باشید، همه را میتوان جواب داد؛ و در فرایند تولید estimate، درک بیشتری از دنیایی که programهایتان در آن زندگی میکنند به دست میآورید.
با یاد گرفتن برآورد و توسعهٔ این skill تا جایی که intuitive feel برای magnitude چیزها داشته باشید، میتوانید feasibility را با ظاهری شبهجادویی نشان دهید. وقتی کسی بگوید «backup را روی خط ISDN به central site میفرستیم»، intuitively میدانید practical است یا نه. وقتی code میزنید، میدانید کدام subsystemها optimize لازم دارند و کدام را میتوان رها کرد.
TIP 18 — Estimate to Avoid Surprises
بهعنوان bonus، در پایان این بخش single correct answer را فاش میکنیم که هر وقت estimate خواستند بدهید.
چقدر دقت کافی است؟
تا حدی، همهٔ جوابها estimate هستند — فقط بعضی accurateتر. پس اولین سؤالی که وقتی estimate میخواهند باید بپرسید: contextی که جواب در آن گرفته میشود چیست؟ high accuracy میخواهند یا ballpark figure؟
- اگر مادربزرگ بپرسد کی میرسی، احتمالاً میخواهد بداند ناهار درست کند یا شام. diver زیر آب که air تمام میشود، احتمالاً جواب تا second میخواهد.
- مقدار π چقدر؟ اگر edging دور گلدان دایرهای میخرید، «3»¹⁰ احتمالاً کافی است. در مدرسه شاید 22/7. در NASA شاید 12 رقم اعشار.
نکتهٔ جالب estimating این است که unitهایی که استفاده میکنید در interpretation نتیجه فرق میگذارد. اگر بگویید «حدود 130 working day»، انتظار میرود pretty close بیاید. اما «حدود شش ماه» — هر زمان بین پنج تا هفت ماه. هر دو همان duration را represent میکنند، اما «130 day» accuracy بالاتری imply میکند از آنچه احساس میکنید. time estimate را اینطور scale کنید:
| Duration | Quote estimate in |
|---|---|
| 1–15 days | days |
| 3–8 weeks | weeks |
| 8–30 weeks | months |
| 30+ weeks | think hard before giving an estimate |
پس اگر بعد از کار لازم تصمیم گرفتید پروژه 125 working day (25 week) طول میکشد، شاید estimate «حدود شش ماه» deliver کنید.
همان concept برای estimate هر quantity: unit جواب را طوری انتخاب کنید که accuracy intended را reflect کند.
¹⁰ «3» برای legislator هم apparently کافی بود. در 1897، Indiana State Legislature House Bill No. 246 میخواست π از این پس «3» باشد. Bill در reading دوم tabled شد وقتی professor ریاضی گفت قدرتشان تا pass کردن law of nature نمیرسد.
estimate از کجا میآید؟
همهٔ estimateها بر model مسئله based هستند. اما قبل از عمیق شدن در technique ساخت model، trick estimating پایه که همیشه جواب خوب میدهد: از کسی که قبلاً انجام داده بپرسید. قبل از commit شدن به model building، کسی را پیدا کنید که در situation مشابه بوده.
ببینید problemشان چطور solve شد. unlikely exact match پیدا کنید، اما surprisingly often experience دیگران را successfully draw on میکنید.
بفهمید چه چیزی پرسیده شده
اولین بخش هر exercise estimating: understanding آنچه پرسیده شده. علاوه بر accuracy issues بالا، grasp scope domain لازم است. اغلب scope implicit در سؤال است، اما habit کنید قبل از guess دربارهٔ scope فکر کنید. scope انتخابشده اغلب بخشی از جواب است: «فرض کنیم accident ترافیکی نیست و بنzin در ماشین است، 20 دقیقه دیگر میرسم.»
model system بسازید
بخش fun estimating. از understanding سؤال، mental model rough و bare-bones بسازید. اگر response time estimate میکنید، model شاید server و arriving traffic داشته باشد. برای پروژه، model stepهای organization در development و تصویر very rough implementation system باشد.
model building هم creative است هم در long term مفید. اغلب فرایند ساخت model به discovery الگو و process underlying که روی surface آشکار نبود lead میکند. شاید بخواهید سؤال اصلی reexamine کنید: «X خواستید، اما Y variant X با نصف time و فقط یک feature کمتر شدنی است.»
ساخت model inaccuracy به فرایند estimating introduce میکند. inevitable و beneficial. model simplicity را با accuracy trade off میکنید. double effort روی model شاید فقط slight increase accuracy. experience میگوید کی stop refining.
model را به component بشکنید
model دارید — decompose به component. mathematical rule پیدا کنید که componentها interact. گاهی component single value add میکند. بعضی multiply factor. بعضی complicatedتر (مثل simulate arrival traffic در node).
هر component معمولاً parameter دارد که contribution به model overall را affect میکند. در این stage فقط parameter identify کنید.
به هر parameter مقدار بدهید
parameter broken out — value assign. error در این step introduce میکنید. trick: parameter با most impact روی result — concentrate روی in-order-of-magnitude درست. معمولاً parameter add شده کمتر significant از multiply/divide. double line speed ممکن است data received در ساعت double؛ add 5ms transit delay effect noticeable ندارد.
justifiable way برای calculate critical parameter داشته باشید. queuing example: measure actual transaction arrival rate system موجود؛ یا similar system. measure time serve request؛ یا estimate با technique این section. اغلب estimate بر subestimate دیگر based — بزرگترین error اینجا creep in.
جوابها را calculate کنید
فقط در سادهترین case estimate single answer دارد — «five cross-town block را 15 دقیقه راه میروم.» system complexتر — hedge. multiple calculation با vary critical parameter تا driver model را بفهمید. spreadsheet کمک بزرگ. جواب را couch in terms parameter: «response time roughly three quarter second اگر SCSI bus و 64MB memory؛ one second با 48MB.» (توجه «three quarter second» feeling accuracy متفاوت از 750ms convey میکند.)
در calculation phase answer strange — arithmetic درست — understanding problem یا model احتمالاً wrong. valuable information.
prowess estimating را track کنید
record estimate — ببینید چقدر close بود. subestimate هم track. اغلب estimate pretty good — بعد while expect.
estimate wrong — shrug و walk away نکنید. why differ from guess. parameter reality match نکرد؟ model wrong؟ time uncover — estimate بعدی better.
برآورد schedule پروژه
rule معمول estimating در face complexity و vagary application development sizable break down. often only way determine timetable: gaining experience روی همان project. paradox نیست اگر incremental development practice کنید — repeat:
- Check requirements
- Analyze risk
- Design, implement, integrate
- Validate with users
initially فقط vague idea iteration count یا duration. بعضی method require nail down در initial plan — برای همهٔ پروژهها جز trivial mistake. مگر application similar به previous با same team و technology — فقط guessing.
coding و testing functionality initial — end increment اول mark. based on experience — refine guess iteration count و content هر increment. refinement better — confidence schedule grow.
TIP 19 — Iterate the Schedule with the Code
شاید با management popular نباشد — single hard-and-fast number قبل start. help understand team، productivity و environment schedule determine. formalize و refine schedule part of each iteration — accurate scheduling estimate ممکن deliver.
وقتی estimate میخواهند چه بگویید
«I'll get back to you.»
تقریباً always result بهتر اگر process slow down و time برای step این section. estimate coffee machine — (مثل coffee) haunt.
بخشهای مرتبط: Algorithm Speed، صفحهٔ 177.
Challenges
- log estimate شروع کنید. accuracy track. error > 50% — where estimate wrong.
Exercises
9. «کدام bandwidth بالاتر: line ارتباطی 1Mbps یا person که بین دو computer با tape پر 4GB در جیب راه میرود؟» constraint چه میگذارید تا scope answer درست باشد؟ (مثلاً time access tape ignore.) (پاسخ ص. 283)
10. پس کدام bandwidth بالاتر؟ (پاسخ ص. 284)