دانستنی‌ها

در کامنت‌گذاری بهتر شوید

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

توجه: منظور از “مستندات” در اینجا، مستندات کدی است؛ یعنی هر چیزی که داخل کد برای توضیح کارکرد آن، نوشته می‌شود (مانند کامنت‌های درون خطی، جاواداک و غیره).

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

در اینجا می‌توانید يک نمونه بارز از کامنت‌گذاری و مستندات کدی را ببینید:

public class BadDoc {
  
  /**
   * Compute the values
   * @param x the x parameter
   * @return the list of values
   */
  public List<Double> computeValues(int x) {
    // If x equals 0, we return null
    if(x == 0) {
      return null;
    }
    
    // We compute the list of values
    List<Double> values = new ArrayList<>();
    for(int i = 0; i < x; i++) {
      values.add(i * i);
    }
    
    // We return the values
    return values;
  }
  
}

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

  • وجود یک کامنت جاواداک برای متد computeValues(int x) 
  • وجود چندین کامنت در بدنه متد

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

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

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

(در ادامه، هر یک از نکات گفته شده را جداگانه بررسی می‌کنیم)

 

  1. از کامنت‌ها به درستی استفاده کنید.

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

  • کامنت‌های تک‌خطی (در جاوا، جاوااسکریپت، ++C  و دیگر زبان‌ها با // نوشته می‌شوند.) 
  • کامنت‌های چندخطی (در جاوا، جاوااسکریپت، ++C و دیگر زبان‌ها با /* */ نوشته می‌شوند.)
  • کامنت‌های مستندسازی (در جاوا با /* **/ نوشته می‌شوند.) 

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

پس همانطور که گفتیم دو نوع کامنت‌گذاری داریم که هدف از آن‌ها به شرح زیر می‌باشد:

  • کامنت‌های تک‌خطی (که می‌توانند در چند خط نوشته شوند) برای شفاف‌سازی رفتار کد هنگامی‌که واضح نیست، استفاده می‌شوند. مخاطب این نوع کامنت‌ها، نگهدارنده‌های کد هستند.
  • کامنت‌های مستندسازی، هدف و کاربرد کلاس، متد، متغیر و… را توضیح می‌دهند. مخاطب این نوع کامنت‌ها، کاربران کد هستند.

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

public class Sum {
  
  ////////////////////////////////////////////////////
  /////////////////// Members    /////////////////////
  ////////////////////////////////////////////////////
  private int a;
  private int b;
  
  ////////////////////////////////////////////////////
  /////////////////// Constructors  //////////////////
  ////////////////////////////////////////////////////
  public Sum(int a, int b) {
    this.a = a;
    this.b = b;
  }
  
  ////////////////////////////////////////////////////
  /////////////////// Methods     ////////////////////
  ////////////////////////////////////////////////////
  public int computeSum() {
    return this.a + this.b;
  }
  
  ////////////////////////////////////////////////////
  /////////////////// Getters     ////////////////////
  ////////////////////////////////////////////////////
  public int getA() { 
    return this.a; 
  }
  public int getB() { 
    return this.b; 
  }
  
  ////////////////////////////////////////////////////
  /////////////////// Setters     ////////////////////
  ////////////////////////////////////////////////////
  public void setA(int a) { 
    this.a = a; 
  }
  public void setB(int b) { 
    this.b = b; 
  }
}

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

  1. از استانداردهای مستندسازی در زبان برنامه‌نویسی مورد استفاده‌تان، پیروی کنید.

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

اساسا، اگر شما به زبان برنامه‌نویسی جاوا کد می‌زنید، از استاندارد جاواداک استفاده می‌کنید و اگر از زبان برنامه‌نویسی #C استفاده می‌کنید، از استاندارد مستندسازی #C (با استفاده از ///) استفاده خواهید کرد.

نهایتا، اگر تیم‌تان ساختار خاصی را انتخاب کرده است (مثلا در صورتی که به زبان‌های C یا ++C کد می‌زنید)، باید با آن پیش بروید. به دلیل این‌که شما با آن ساختار موافق نیستید، نباید ساختار مستندسازی مختص خودتان را انتخاب کنید.

  1. کامنت‌های کاربردی بنویسید.

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

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

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

بهتر است توسعه‌دهندگان از قانون زیر پیروی کنند:

کدِ خوب، خودش مستند است.

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

با این وجود، گاهی اوقات بعضی از توسعه‌دهندگان وسواس‌گونه عمل می‌کنند و کدهایی که  به خودی خود، مستندسازی شده‌اند را نیز کامنت‌گذاری می‌کنند که این کار بیهوده است و عملا هیچ کاربردی ندارد، مانند آنچه در زیر می‌بینید:

 	// If x equals 0, we return null
if(x == 0) {
  return null;
}

اگر با دیدن کد فوراً متوجه شویم که چه کاری انجام می‌دهد، دیگر نیازی نیست آن را مجددا به زبان انگلیسی بازنویسی کنیم. بنابراین در مثال بالا، کامنت مناسب، کامنتی است که توضیح دهد چرا x برابر با 0 باعث می‌شود null برگردانیم.

  1. هدف کد را توضیح دهید.

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

  • هدف این متد چیست؟
  • چگونه از این متد استفاده کنم؟

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

کامنت جاواداکی که برای متد computeValues نوشته شده و می‌گوید “این متد مقادیر را محاسبه می‌کند”، بی‌معنی است چرا که هیچ اشاره‌ای به کاری که متد انجام می‌دهد، ندارد. با این توضیحات، ما تنها می‌توانیم حدس بزنیم که این متد چیزی را محاسبه می‌کند، اما چه چیزی را؟ نمی‌دانیم. 

در عوض، با نگاهی به رفتار متد، می‌توانیم اینطور بنویسیم که “این متد مجذورِ تمام اعداد صحیح، از 0 تا  x را (به جز خود x) برمی‌گرداند”. 

  1. نحوه استفاده از کد و محدودیت‌های آن را توضیح دهید. 

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

با این حال، به طور معمول، هر توسعه‌دهنده‌ای باید این مسئله را به خاطر بسپارد که:

مستندات باید به گونه‌ای نوشته شوند که بتوان با استفاده از آن‌ها، تمام تست‌های واحد (Unit Tests) یک متد را نوشت.

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

برای مثال، اگر متدی، اعداد صحیح را به عنوان پارامتر می‌گیرد، توسعه‌دهنده باید در مستند خود، موارد زیر را با جزئیات توضیح دهد:

  • معنی آن پارامتر 
  • دامنه مقادیر معتبر برای آن پارامتر (مثلا “مقادیر باید مثبت باشند”)
  • چه اتفاقی می‌افتد اگر آرگومان نامعتبر باشد (مثلا “اگر x منفی یا برابر با 0 باشد InvalidArgumentException پرتاب می‌شود) 

همچنین در جاوا، ++C و دیگر زبان‌ها، توسعه‌دهنده باید توضیح دهد که اگر کاربر متد، مقادیر null برای آرگومان‌ها استفاده کند چه اتفاقی می‌افتد (البته اگر آرگومان با NonNull Annotation حاشیه‌نگاری شده باشد، نیازی به این کار نیست). یا اینکه وقتی آرگومان null است متد مورد نظر NullPointerException پرتاب می‌کند یا خطای Segmentation Fault می‌دهد، باید در مستندات به وضوح بیان شود.

یک مثال بهبودیافته شده

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

/**
 * Class used as an example for a "better" documentation.
 * It sole purpose is to present some good practices when writing comments.
 * @author Alexandre Lombard
 */
public class GoodDoc {
  
  /**
   * Compute the list of all the square values of the integers from 0 to x excluded
   * @param x a strictly positive integer being the limit for the computation of the square values of the integers
   * @return the list of the square values of the integers from 0 to x excluded,
   *         or <code>null</code> if x is equal to 0, or an empty list is x is negative
   */
  public List<Double> computeValues(int x) {
    if(x == 0) {
      // x should not be equal to 0 because "insert some technical justification here", we interrupt the method and we return null
      return null;
    }
    
    // We create the list that will contain the square values and we fill it
    List<Double> values = new ArrayList<>();
    for(int i = 0; i < x; i++) {
      values.add(i * i);
    }
    
    return values;
  }
  
}

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

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

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

 

این مطلب از این مقاله ترجمه شده است.

.

.

با ما همراه باشید

آدرس کانال تلگرام: JavaCupIR@

آدرس اکانت توییتر: JavaCupIR@

آدرس صفحه اینستاگرام: javacup.ir

آدرس گروه لینکدین: Iranian Java Developers

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا