خلاصه کتاب کد تمیز – (فصل دوم : اسامی معنادار – بخش اول)

ترجمه و تالیف : فاطمه شیرزادفر
تاریخ انتشار : 05 اسفند 99
خواندن در 6 دقیقه
دسته بندی ها : برنامه نویسی

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

فصل دوم – اسامی معنادار

 خلاصه کتاب کد تمیز – (فصل دوم : اسامی معنادار – بخش اول)

مقدمه

اسم‌ها همه‌جای نرم‌افزار وجود دارند. ما متغییرها، فانکشن‌ها، آرگومان‌ها، کلاس‌ها و پکیج‌ها یا سورس فایل‌های خود و دایرکتوری‌هایی که شامل آن‌ها می‌شن رو نام‌گذاری می‌کنیم. ما حتی فایل‌های jar ، war، ear رو هم نام‌گذاری می‌کنیم. نام‌گذاری می‌کنیم، نام‌گذاری می‌کنیم و نام گذاری می‌کنیم؛ و از اونجایی که این کار رو زیاد انجام می‌دیم پس بهتر هست که این کار رو به روشی درست انجام بدیم. اون چه رو که در ادامه می‌خونید قوانینی ساده برای خلق اسم‌های خوب هستند.

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

۱. استفاده از اسم‌هایی که منظور شما رو بیان کنند.

۲.از دادن اطلاعات اشتباه خودداری کنید.

۳.تفاوت‌های معنادار ایجاد کنید.

۴.از اسم‌های قابل تلفظ استفاده کنین.

۵.از اسم‌های قابل جستجو استفاده کنین.

۶.از رمزگذاری دوری کنید.

۷.از mental mapping خودداری کنید. (‌به عبارتی استفاده از اسم‌هایی که برنامه‌نویسان قبلاً اون‌ها رو با نام دیگری می‌شناسند

۸.نام کلاس‌ها

نام متدها

۱۰.بانمک بازی درنیارین.

۱۱.برای هر مفهوم یک کلمه انتخاب کنین.

۱۲.ایهام ایجاد نکنید.

۱۳.از دامنه کلمات مرتبط با راه‌حل استفاده کنین.

۱۴.از دامنه کلمات صورت مسأله استفاده کنین.

۱۵.کانتکست با معنی اضافه کنین.

۱۶.کانتکست بیخودی اضافه نکنین.

خب بریم که هر کدوم رو توضیح بدیم:

استفاده از اسم‌هایی که منظور شما را بیان کنند (Intention-Revealing Names)

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

int d; // elapsed time in days

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

int elapsedTimeInDays;

int daysSinceCreation;

int daysSinceModification;

int fileAgeInDays;

انتخاب اسم‌هایی که منظور ما رو بیان می‌کنه، فهمیدن و تغییر دادن کد رو آسون‌تر می‌کنه. می‌تونید بگید کدی که در زیر وجود داره چه کاری می‌کنه؟

 public List getThem() {

        List list1 = new ArrayList;

                for (int[] x : theList)

                        If (x[0] == 4)

                                List1.add(x);

         Return list1;

}

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

۱. چه چیزهایی در  theList وجود دارد؟

۲.اهمیت عضو صفرم یک آیتم در  theList چیست؟

۳.اهمیت مقدار ۴ چیست؟

۴.چگونه از لیستی که برگشت داده شده (return شده) استفاده کنم؟

جواب سوال‌های بالا در مثال قبل، قابل تشخیص نیستند ولی باید مشخص شوند. خب بیایین فکر کنیم که داریم روی بازی مین روب کار می‌کنیم بنابراین می‌دونیم که صفحه بازی ما یک لیست از سلول‌هاست که اون رو با theList نمایش می‌دیم. خب بیایین اسم اون رو به gameBoard تغییر بدیم و باید بگم که هر سلول در صفحه توسط یک آرایه ساده نمایش داده می‌شه. همینطور این رو هم می‌دونیم که مقدار صفرم وضعیت سلول هست و وضعیت ۴ هم به معنای این هست که "پرچم گذاری شده".بیایین فقط با دادن اسامی این کانسپت‌ها، کد رو به طور قابل توجهی بهبود ببخشیم:

public List getFlaggedCells() {

        List flaggedCells = new ArrayList();

        for (int[] cell : gameBoard)

                If (cell[STATUS_VALUE] == FLAGGED)

                        flaggedCells.add(cell);

        return flaggedCells;

}

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

public List getFlaggedCells() {

        List flaggedCells = new ArrayList();

        for (Cell cell : gameBoard)

                if (cell.isFlagged())

                        flaggedCells.add(cell);

        return flaggedCells;

}

با انجام این تغییرات ساده دیگه فهمیدن اینکه این کد چه کاری انجام می‌ده سخت نیست. و این است قدرت انتخاب کردن اسم‌های خوب!!

از دادن اطلاعات اشتباه خودداری کنید

ما باید از کلماتی که مفهومشان با منظور ما فاصله زیادی دارن اجتناب کنیم. به عنوان مثال، کلمه‌های hp , aix و sco اسم‌های ضعیفی برای متغییرها هستند؛ چراکه اسامی یونیکس پلتفرم هستند. حتی اگه در حال کدنویسی Hypotenuse هستید ( اگه احیاناً یادتون رفته که Hypotenuse چی هست در ادامه یه اشاره کوچیک بهش می‌کنم) و فکر می‌کنین hp مخفف خوبی واسش به نظر می‌رسه، اما باید بگم که بازم ممکنه باعث دادن اطلاعات اشتباه بشه ( یا به عبارتی disinformation باشه)

 خلاصه کتاب کد تمیز – (فصل دوم : اسامی معنادار – بخش اول)

Hypotenuse

خب بریم ادامه بحث...

 هیچ وقت به یک لیست از اکانت‌ها،‌نام accountList رو ندید چون اون واقعاً یه لیسته ولی کلمه‌ی List معنی یه چیز خاص رو برای برنامه‌نویسا داره. اگه یه کانتینر اکانت نگهداری می‌کنه به این معنی نیست که یک List هست و همونطور که گفتیم میتونه منجر به disinformation بشه. پسaccountGroup ،bunchOfAccounts یا فقط accounts اسم‌های بهتری هستن.

از استفاده از اسم‌هایی که تفاوت‌های جزئی و کوچک باهم دارن هم خودداری کنین، مثلاً چقدر طول می‌کشه که تفاوت جزئی این XYZControllerforEfficientHandlingOfStringsin رو در یک ماژول و بعد یکم جلوتر با این XYZControllerforEfficientStorageOfStrings بفهمید و باهم اشتباه گرفته نشن؟ چراکه هر دو این‌ها، شکل یکسانی دارن.

مثالی از اسم‌هایی که اطلاعات غلط می‌دن، اسم‌هایی هستن که از حروف مثل هم استفاده می‌کنن. مثل کد زیر:

int a = l;

if(0==1)

        a=01;

else

        l = 01;

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

تفاوت‌های بامعنی ایجاد کنید

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

(a1,a2,…,aN) اینطور نام‌گذاری هم که کلاً با اصول نام‌گذاری در تضاده، شاید فکر کنین چون اطلاعات غلط می‌دن، اما نه! در‌واقع اصلاً اطلاعاتی نمیدن؛ مثلاً به این کد نگاه کنین:

public static void copyChars(char a1[], char a2[] {

        for (int i=0; i<a1.length; i++) {

                A2\[[i] = a1[i];

         }

 }

اگه از source و destination استفاده می‌شد فانکشن خواناتری داشتیم.

noise word ها هم دسته‌ی دیگه‌ای از اسامی بی‌معنی هستن. فکر کنین که یه کلاس به اسم Product دارین . اگه کلاسای دیگه‌ای با اسمای ProductInfo و ProductData درست کنین، شاید اسماتون متفاوت باشه ولی معانی متفاوتی نداره و همش یکیه. البته استفاده از پریفیکس‌هایی مثل a, an و the در نام گذاری مشکلی نداره؛ همه‌ی مشکلا از اون جایی شروع می‌شه که مثلاً اسم یک متغییر رو theZork بزارین درحالی که یه متغییر دیگه به نام Zork دارین.

توجه کنین که هیچ وقت نباید از کلمه Variable در یک متغییر و از اسم table در یک Table استفاده کنید.

چطور ممکنه NameString از Name بهتر باشه؟ مثلاً ممکنه Name یه عدد اعشاری باشه؟ خب اگه جوابتون اره هست باید بگم که کل قوانین رو زیر سؤال بردین!

فرمی از نام‌گذاری اشتباه :

getActiveAccount();

getActiveAccounts();

getActiveAccountInfo();

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

تفاوتmoneyAmount از money یا customerInfo از customer یا accountData از account و theMessage از message غیر قابل تشخیصه. نام‌هایی قابل تشخیصه، که تفاوت رو به خواننده نشون بده.

از اسم‌های قابل تلفظ استفاده کنین.

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

اگه نتونین تلفظ کنین پس نمی‌تونین راجع به چیزی بحث کنین. مثل این جمله :

Well,over here on the bee cee arr three cee enn tee we have a pee ess zee kyew it, see?

پس متوجه اهمیت موضوع شدین! یه کمپانی رو میشناسم که مفهومی به اسم genymdhms داره،(generation date, year, month, day, hour, minute and second ) و اونا برای به خاطر سپردن این مفهوم، این جمله را مدام تکرار می‌کنن "gen why emm dee aich emm ess". منم عادت بدی دارم که همه‌ چیز رو همون جور که نوشته شده تلفظ می‌کنم و نهایتاً شروع کردم به گفتن "gen-yah-mudda-hims." و بعدها این  مفهوم توسط جمعی از آنالیزورها و طراحان به همین اسم نامیده شد؛ این برای ما مثل یک شوخی بود اما چه بانمک باشه چه نه، ما داریم با نام گذاری ضعیف کنار میاییم.  مقایسه کنین :

class DtaRcrd102 {

        private Date genymdhms;

        private Date modymdhms;

        private final String pszqint = “102”;

        /\* _...  \*_/

}

یا این؟

class Customer {

        private Date generationTimestamp;

        private Date modificationTimestamp;

        private final String recordId = "102";

        /\* _...  \*_/




}

از اسم‌های قابل جستجو استفاده کنین.

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

به همین علت، نام‌های طولانی نسبت به نام‌های کوتاه برتری دارند. البته طول یک نام باید با اندازه‌ی اسکوپ اون هم مطابقت داشته باشد؛ پس اگه یک متغییر یا ثابت باید در جاهای مختلفی از یک کد استفاده بشه، استفاده از یک اسم search- friendly ضرورت داره. یه بار دیگه مقایسه کنین:

 for (int j=0; j<34; j++) {

         S+= (t[j]*4)/5;

 }

یا این؟

 int realDaysPerIdealDay = 3;

 const int WORK_DAYS_PER_WEEK = 5;

 int sum = 0;

 for (int j=0; j<NUMBER_OF_TASKS; j++) {

         int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;

         int realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK);

         sum += realTaskWeeks;

 }

به sum دقت کنین، اسم کاملاً مناسبی نیست اما حداقل قابل جستجو کردن هست. ممکنه کدنویسی با این اسم‌ها یکم طولانی‌تر بشه اما این‌ رو هم در نظر داشته باشین که پیدا کردن WORK_DAYS_PER_WEEK چقدر راحته.

از رمزگذاری دوری کنید

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

Member prefixes

شما همچنین نیازی به پرفیکس m_ در member variable ها ندارین. کلاس‌ها و فانکشن‌های شما باید به‌قدری کوچیک باشه که نیازی به اون‌ها نباشه و همینطور شما باید از ویرایشگری استفاده کنین که member ها رو بولد یا رنگی‌ کنه و اون‌ها رو متمایز کنه.

 public class Part {

         private String m_dsc; // The textual description

         void setName(String name) {

                 M_dsc = name;

         }

 }




 public class Part {

         String description;

         void setDescription(String description) {

                 this.description = description;

         }

}

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

Interfaces and implementations

گاهی اوقات موارد خاصی برای رمزگذاری وجود دارن. به طور مثال به شما می‌گن که یک ABSTRACT FACTORY برای ایجادکردن شکل‌ها پیاده‌سازی کنین. این factory یک اینترفیس خواهد بود و توسط یک کلاس concrete پیاده‌سازی خواهد شد.خب چه اسمی باید برای اینها انتخاب کنم؟ IShapeFactions و ShapeFective ؟ من ترجیح می‌دم که اینترفیس رو بدون اینکه آراستش کنم ولش کنم. من نمی‌خوام کاربرانم بدونن که من اینترفیس خودم رو به اون ارائه می‌دم، همین که بدونن این یک shape factory هست کافیه. اسم اون رو ShapeFactoryImp یا حتی CshapeFective بزارین، رمزگذاری اینترفیس‌ها واجب و ضروری هست.

خب، فکر می‌کنم تا همین‌جا کافی باشه؛ بقیه موارد رو در پست بعدی می‌نویسم. امیدوارم تا اینجا مفید و کاربردی بوده باشه، اگه هر نظر یا انتقادی دارین توی بخش نظرات با ما درمیون بزارین، و در نهایت از وقتی که گذاشتید ممنونم.

گردآوری و تالیف فاطمه شیرزادفر
آفلاین
user-avatar

تجربه کلمه‌ای هست که همه برای توصیف اشتباهاتشون ازش استفاده میکنن، و من همیشه دنبال اشتباهات جدیدم! برنامه‌نویس هستم و لینوکس‌ دوست

دیدگاه‌ها و پرسش‌ها

برای ارسال نظر لازم است ابتدا وارد سایت شوید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید