

نام های صحیح
نامهای صحیح خوانایی کد را بهبود میبخشد، بنابراین زمان لازم برای آشنایی با کد را کاهش میدهد، زیرا استفاده از یک روش زمانی که نام آن تقریباً عملکرد آن را توصیف میکند، بسیار آسانتر است. همه چیز در کد از نام ها (متغیرها، متدها، کلاس ها، اشیا، فایل ها و غیره) تشکیل شده است، بنابراین این نکته هنگام ایجاد کد صحیح و تمیز بسیار مهم می شود. بر اساس موارد فوق، نام باید معنی را بیان کند، به عنوان مثال، چرا متغیر وجود دارد، چه کاری انجام می دهد و چگونه از آن استفاده می شود. من بیش از یک بار متذکر می شوم که بهترین نظر برای یک متغیر، گذاشتن یک نام خوب است.
از سریال تلویزیونی "شرلوک" (2010-2017)
نام گذاری رابط ها
رابط ها معمولاً دارای نام هایی هستند که با حرف بزرگ شروع می شوند و با CamelCase نوشته می شوند. هنگام نوشتن یک رابط، استفاده از پیشوند "I" برای تعیین آن به عنوان یک رابط (مثلاً IUserService) تمرین خوبی در نظر گرفته می شد، اما این کار بسیار زشت و حواس پرت کننده به نظر می رسد. در چنین مواردی بهتر است پیشوند (UserService) را حذف کرده و "Impl" را به عنوان پسوند به نام پیاده سازی آن اضافه کنید (مثلا UserServiceImpl). یا احتمالاً به عنوان آخرین راه حل، یک پیشوند "C" به نام پیاده سازی اضافه کنید (مثلا CUserService).نام کلاس ها
درست مانند رابط ها، نام کلاس ها با حروف بزرگ نوشته می شوند و از CamelCase استفاده می کنند. مهم نیست که با یک آخرالزمان زامبی روبرو هستیم، مهم نیست که پایان آن نزدیک است - هرگز، هرگز، هرگز نام یک کلاس نباید فعل باشد! نام کلاس ها و شی ها باید اسم ها یا اسم های مرکب (UserController، UserDetails، UserAccount و غیره) باشند. شما نباید مخفف برنامه را در انتهای نام هر کلاس بچسبانید، زیرا این فقط پیچیدگی غیر ضروری را اضافه می کند. به عنوان مثال، اگر ما یک برنامه User Data Migration داریم، لطفاً "UDM" را به هر کلاس اضافه نکنید، به عنوان مثال UDMUserDetails، UDMUserAccount، UDMUserController.نام روش ها
معمولاً نام روشها با حروف کوچک شروع میشود، اما از سبک camel case (camelCase) نیز استفاده میکنند. در بالا گفتیم که نام کلاس ها هرگز نباید فعل باشند. در اینجا وضعیت دقیقا برعکس است: نام متدها باید افعال یا عبارات فعل باشد: findUserById، findAllUsers، createUser و غیره. هنگام ایجاد یک متد (و همچنین متغیرها و کلاس ها)، بنابراین از یک قرارداد نامگذاری ثابت برای جلوگیری از سردرگمی استفاده کنید. به عنوان مثال، برای یافتن یک کاربر، یک روش می تواند getUserById یا findUserById نامیده شود. و یک چیز دیگر: در نام روش ها از طنز استفاده نکنید، زیرا ممکن است دیگران متوجه شوخی نشوند. در نتیجه، آنها ممکن است در درک آنچه که این روش انجام می دهد شکست بخورند.نام متغیرها
در اکثر موارد، نام متغیرها با یک حرف کوچک شروع می شود و همچنین از camelCase استفاده می شود، به جز زمانی که متغیر یک ثابت جهانی باشد. در چنین مواردی، تمام حروف نام با حروف بزرگ نوشته می شود و کلمات با یک خط زیر ("_") از هم جدا می شوند. برای راحتی، میتوانید هنگام نامگذاری متغیرها از بافت معنادار استفاده کنید. به عبارت دیگر، زمانی که یک متغیر به عنوان بخشی از چیزی بزرگتر وجود دارد، به عنوان مثال، firstName، lastName، یا وضعیت. در چنین مواردی می توانید پیشوندی اضافه کنید که نشان دهنده شیئی است که این متغیر به آن تعلق دارد. به عنوان مثال: userFirstName، userLastName، userStatus. همچنین باید از نامهای مشابه برای متغیرها که معانی کاملاً متفاوتی دارند اجتناب کنید. در اینجا برخی از متضادهای رایج مورد استفاده در نام متغیرها آمده است:- شروع/پایان
- اول آخر
- قفل / باز شده
- حداقل/حداکثر
- بعدی/قبلی
- قدیمی/جدید
- باز / بسته
- قابل مشاهده / نامرئی
- منبع/هدف
- منبع/مقصد
- بالا پایین
نام متغیرهای کوتاه
وقتی متغیرهایی مانند x یا n یا چیزی شبیه آن داریم، بلافاصله قصد شخصی که کد را نوشته است، نمیبینیم. مشخص نیست n چه می کند. پی بردن به آن نیاز به تفکر دقیق تری دارد (و این به معنای زمان، زمان، زمان است). برای مثال، فرض کنید فیلدی داریم که نشان دهنده شناسه کاربر مسئول است. به جای نام متغیری مانند x یا به سادگی id، نام این متغیر را "responsibleUserId" می گذاریم که بلافاصله خوانایی و محتوای اطلاعاتی را بهبود می بخشد. همانطور که گفته شد، نامهای کوتاه مانند n به عنوان متغیرهای محلی در متدهای کوچک جای دارند، جایی که بلوک کد شامل این متغیر فقط چند خط طول دارد و نام متد کاملاً آنچه را که در آنجا اتفاق میافتد توصیف میکند. با دیدن چنین متغیری، یک توسعه دهنده متوجه می شود که اهمیت ثانویه دارد و دامنه بسیار محدودی دارد. در نتیجه، دامنه وابستگی خاصی به طول نام متغیر دارد: هر چه نام طولانیتر باشد، متغیر جهانیتر است و بالعکس. به عنوان مثال، در اینجا روشی برای یافتن آخرین کاربر ذخیره شده بر اساس تاریخ آمده است:public User findLastUser() {
return findAllUsers().stream()
.sorted((x, y) -> -x.getCreatedDate().compareTo(y.getCreatedDate()))
.findFirst()
.orElseThrow(() -> new ResourceNotFoundException("No user exists"));
}
در اینجا از متغیرهای با نام کوتاه x و y برای مرتب سازی جریان استفاده می کنیم و سپس آنها را فراموش می کنیم.
طول بهینه
بیایید به موضوع طول نام ادامه دهیم. طول نام بهینه جایی بین n و حداکثرNumberOfUsersInTheCurrentGroup است. به عبارت دیگر، نامهای کوتاه از کمبود معنی رنج میبرند، در حالی که نامهایی که بیش از حد طولانی هستند، برنامه را بدون افزودن خوانایی طولانیتر میکنند و ما به سادگی برای نوشتن هر بار آنها تنبلی میکنیم. جدا از موردی که در بالا برای متغیرهایی با نام کوتاه مانند n توضیح داده شد، باید به طول حدود 8-16 کاراکتر بچسبید. این یک قانون سختگیرانه نیست، فقط یک دستورالعمل است.تفاوت های کوچک
من نمی توانم به تفاوت های ظریف در نام ها اشاره نکنم. این نیز یک عمل بد است، زیرا این تفاوتها میتوانند به سادگی گیجکننده باشند یا نیاز به صرف زمان زیادی برای مشاهده آنها داشته باشند. به عنوان مثال، تشخیص تفاوت بین InvalidDataAccessApiUsageException و InvalidDataAccessResourceUsageException در یک نگاه دشوار است. همچنین اغلب هنگام استفاده از حروف کوچک L و O ممکن است سردرگمی ایجاد شود، زیرا به راحتی می توان آنها را با 1 و 0 اشتباه گرفت. در برخی از فونت ها تفاوت آشکارتر است، در برخی کمتر است.معنی
ما باید نام ها را معنی دار کنیم، اما از طریق مترادف ها ابهام ایجاد نکنیم، زیرا به عنوان مثال، UserData و UserInfo در واقع معنی یکسانی دارند. در این مورد، ما باید در کد عمیقتر بگردیم تا بفهمیم به کدام شی خاص نیاز داریم. از کلماتی که اطلاعات مفیدی را منتقل نمی کنند اجتناب کنید. به عنوان مثال، در firstNameString، چرا به کلمه String نیاز داریم؟ آیا این واقعاً می تواند یک شی Date باشد؟ البته که نه. بنابراین، ما به سادگی از firstName استفاده می کنیم. همچنین می خواهم به متغیرهای بولی اشاره کنم. به عنوان مثال، یک Boolean به نام flagDeleted را در نظر بگیرید. کلمه پرچم معنی ندارد. منطقی تر است که آن را حذف شده بنامیم.اطلاعات غلط
من همچنین می خواهم چند کلمه در مورد قراردادهای نامگذاری نادرست بگویم. فرض کنید یک متغیر به نام userActivityList داریم، اما به جای اینکه یک List باشد، این شی یک نوع کانتینر دیگر یا شی ذخیره سازی سفارشی است. این می تواند برنامه نویس معمولی را گیج کند: بهتر است آن را چیزی مانند userActivityGroup یا userActivities بنامیم.جستجو کردن
یکی از ایرادات نامهای کوتاه و ساده این است که یافتن آنها در حجم وسیعی از کدها دشوار است. البته گزینه دوم ما باید از کلمات (حروف) در نامها اجتناب کنیم، زیرا آنها فقط تعداد فایلهای منطبق را در طول جستجو افزایش میدهند، که خوب نیست. من می خواهم به شما یادآوری کنم که برنامه نویسان زمان بیشتری را صرف خواندن کد می کنند تا نوشتن آن، پس در مورد نامگذاری عناصر برنامه خود هوشمندانه عمل کنید. اما اگر یک نام خوب پیدا نشود چه؟ اگر نام یک روش عملکرد آن را به خوبی توصیف نکند چه می شود؟ اینجاست که نظرات وارد مرحله می شوند.نظرات

انواع نظرات
-
نظرات حقوقی - نظرات در ابتدای هر فایل منبع به دلایل قانونی، به عنوان مثال:
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
-
نظرات آموزنده - نظراتی که توضیحی از کد را نشان می دهد (ارائه اطلاعات اضافی یا توضیح در مورد هدف یک بخش معین از کد).
مثلا:
/* * Combines the user from the database with the one passed for updating * When a field in requestUser is empty, it is filled with old data from foundUser */ private User mergeUser(User requestUser, User foundUser) { return new User( foundUser.getId(), requestUser.getFirstName() == null ? requestUser.getFirstName() : foundUser.getFirstName(), requestUser.getMiddleName() == null ? requestUser.getMiddleName() : foundUser.getMiddleName(), requestUser.getLastName() == null ? requestUser.getLastName() : foundUser.getLastName(), requestUser.getAge() == null ? requestUser.getAge() : foundUser.getAge() ); }
در این مورد، می توانید بدون نظر انجام دهید، زیرا نام روش و پارامترهای آن، همراه با عملکرد بسیار شفاف، خود را به خوبی توصیف می کنند.
-
نظرات هشدار - نظری که برای هشدار دادن به سایر توسعه دهندگان در مورد عواقب نامطلوب یک اقدام (به عنوان مثال، هشدار به آنها در مورد اینکه چرا یک آزمایش به عنوان @Ignore علامت گذاری شده است):
// Takes too long to run // Don't run if you don't have a lot of time @Ignore @Test public void someIntegrationTest() { …… }
-
TODO - نظراتی که یادداشتی درباره کاری است که باید در آینده انجام شود و به دلایلی اکنون نمی توان انجام داد. این یک روش خوب است، اما برای حذف موارد نامربوط و جلوگیری از بهم ریختگی، چنین نظراتی باید مرتباً بررسی شوند.
یک مثال می تواند این باشد:
// TODO: Add a check for the current user ID (when the security context is created) @Override public Resource downloadFile(File file) { return fileManager.download(file); }
در اینجا به این واقعیت اشاره می کنیم که باید مقایسه ای بین کاربری که عملیات دانلود را انجام می دهد (که شناسه او را از زمینه امنیتی استخراج می کنیم) با شخصی که عملیات ذخیره را انجام داده است اضافه کنیم.
-
نظرات تقویت کننده - نظراتی که بر اهمیت شرایطی تأکید می کنند که در نگاه اول ممکن است بی اهمیت به نظر برسد.
به عنوان مثال، بخشی از یک روش را در نظر بگیرید که یک پایگاه داده آزمایشی را با چند اسکریپت پر می کند:
Stream.of(IOUtils.resourceToString("/fill-scripts/" + x, StandardCharsets.UTF_8) .trim() .split(";")) .forEach(jdbcTemplate::update); // The trim() call is very important. It removes possible spaces at the end of the script // so that when we read and split into separate requests, we don't end up with empty ones
-
نظرات Javadoc - نظراتی که API را برای عملکرد خاصی توصیف می کنند. احتمالاً مفیدترین نظرات وجود دارد، زیرا کار با API مستند بسیار ساده تر است. گفته می شود، آنها همچنین می توانند مانند هر نوع نظر دیگری قدیمی باشند. بنابراین، هرگز فراموش نکنید که سهم اصلی در مستندسازی نه توسط نظرات، بلکه توسط کد خوب انجام می شود.
در اینجا مثالی از یک روش نسبتا رایج برای به روز رسانی یک کاربر آورده شده است:
/** * Updates the passed fields for a user based on its id. * * @param id id of the user to be updated * @param user user with populated fields for updating * @return updated user */ User update(Long id, User user);
نظرات بد
-
نظر زمزمه - نظراتی که معمولاً با عجله نوشته میشوند و معنای آنها فقط برای توسعهدهندهای که آنها را نوشته است قابل درک است، زیرا فقط او موقعیت ظریفی را که نظر به آن اشاره دارد درک میکند.
این مثال را در نظر بگیرید:
public void configureSomeSystem() { try{ String configPath = filesLocation.concat("/").concat(CONFIGURATION_FILE); FileInputStream stream = new FileInputStream(configPath); } catch (FileNotFoundException e) { // If there is no configuration file, the default configuration is loaded } }
چه کسی این تنظیمات را بارگیری می کند؟ آیا آنها قبلا بارگذاری شده اند؟ آیا این روش قرار است استثناها را بگیرد و تنظیمات پیش فرض را بارگیری کند؟ سوالات بسیار زیادی مطرح می شود که تنها با بررسی سایر بخش های سیستم می توان به آنها پاسخ داد.
- نظرات اضافی - نظراتی که بار معنایی ندارند، زیرا آنچه در یک بخش معین از کد اتفاق می افتد کاملاً واضح است. به عبارت دیگر، خواندن نظر آسان تر از کد نیست.
بیایید یک مثال را ببینیم:
public class JdbcConnection{ public class JdbcConnection{ /** * The logger associated with the current class */ private Logger log = Logger.getLogger(JdbcConnection.class.getName()); /** * Creates and returns a connection using the input parameters */ public static Connection buildConnection(String url, String login, String password, String driver) throws Exception { Class.forName(driver); connection = DriverManager.getConnection(url, login, password); log.info("Created connection with db"); return connection; }
این گونه اظهار نظرها چه فایده ای دارد؟ همه چیزهایی که توضیح می دهند قبلاً کاملاً واضح است.
-
نظرات غیر قابل اعتماد - نظراتی که نادرست هستند و فقط گمراه کننده هستند (اطلاعات نادرست). به عنوان مثال، این یکی است.
/** * Helper method. Closes the connection with the scanner if isNotUsing is true */ private void scanClose(Scanner scan, boolean isNotUsing) throws Exception { if (!isNotUsing) { throw new Exception("The scanner is still in use"); } scan.close(); }
این کامنت چه اشکالی دارد؟ این واقعیت که کمی به ما دروغ می گوید، به این صورت که اگر isNotUsing اتصال بسته می شود، نادرست است، نه برعکس، همانطور که نظر به ما اطلاع می دهد.
-
نظرات اجباری - نظراتی که اجباری تلقی می شوند (مثلاً نظرات Javadoc)، اما در واقع گاهی اوقات بیش از حد انباشته می شوند و غیر قابل اعتماد و غیر ضروری هستند (شما باید فکر کنید که آیا این نظرات واقعاً مورد نیاز هستند یا خیر).
مثال:
/**
* Create a user based on the parameters
* @param firstName first name of the created user
* @param middleName middle name of the created user
* @param lastName last name of the created user
* @param age age of the created user
* @param address address of the created user
* @return user that was created
*/
User createNewUser(String firstName, String middleName, String lastName, String age, String address);
آیا می توانید بفهمید که این روش بدون این نظرات چه می کند؟ به احتمال زیاد، بله، بنابراین نظرات در اینجا بی معنی می شوند.
نظرات گزارش - نظراتی که گاهی اوقات هر بار که یک ماژول ویرایش می شود به ابتدای ماژول اضافه می شود (چیزی شبیه گزارش تغییرات).
/**
* Records kept since January 9, 2020;
**********************************************************************
* 9 Jan 2020: Providing a database connection using JDBC Connection;
* 15 Jan 2020: Adding DAO-level interfaces for working with the database;
* 23 Jan 2020: Adding integration tests for the database;
* 28 Jan 2020: Implementation of DAO-level interfaces;
* 1 Feb 2020: Development of interfaces for services,
* in accordance with the requirements specified in user stories;
* 16 Feb 2020: Implementation of service interfaces
* (implementation of business logic related to the work of the database);
* 25 Feb 2020: Adding tests for services;
* 8 Mar 2020: Celebration of International Women's Day (Terry is drunk again);
* 21 Mar 2020: Refactoring the service layer;
*/
این رویکرد زمانی توجیه میشد، اما با ظهور سیستمهای کنترل نسخه (به عنوان مثال، Git)، به یک درهم ریختگی و پیچیدگی غیر ضروری کد تبدیل شد.
نظرات نویسنده - نظراتی که هدف آنها نشان دادن شخصی است که کد را نوشته است، بنابراین می توانید با او تماس بگیرید و درباره چگونگی، چیستی و چرایی صحبت کنید، به عنوان مثال:
* @author Bender Bending
یک بار دیگر، سیستمهای کنترل نسخه دقیقاً به خاطر میآورند که چه کسی و چه زمانی هر بیت کد را اضافه کرده است، بنابراین این رویکرد اضافی است.
کد اظهار نظر - کدی که به دلایلی اظهار نظر شده است. این یکی از بدترین عادتهاست، زیرا اتفاقی که میافتد این است که شما درباره چیزی نظر میدهید و آن را فراموش میکنید، و سپس دیگر توسعهدهندگان جرات حذف آن را ندارند (بالاخره، اگر چیزی ارزشمند باشد چه میشود؟).
// public void someMethod(SomeObject obj) {
// .....
// }
در نتیجه، کدهای ارسال شده مانند سطل زباله جمع می شوند. به هیچ وجه نباید چنین کدی را ترک کنید. اگر واقعاً به آن نیاز دارید، سیستم کنترل نسخه را فراموش نکنید.
نظرات غیر واضح - نظراتی که چیزی را به روشی بیش از حد پیچیده توصیف می کنند.
/*
* Start with an array large enough to store
* all the data bytes (plus filter bytes) with a cushion, plus 300 bytes
* for header data
*/
this.dataBytes = new byte[(this.size * (this.deep + 1) * 2)+300];
یک نظر باید کد را توضیح دهد. خود نباید نیاز به توضیح داشته باشد. پس اینجا چه اشکالی دارد؟ "بایت های فیلتر" چیست؟ آن "+ 1" اصلاً درباره چیست؟ چرا دقیقا 300؟
- از سبک هایی استفاده کنید که نگهداری آنها آسان است: حفظ سبک هایی که بیش از حد فانتزی و عجیب و غریب هستند آزاردهنده و وقت گیر است.
- از نظرات پایانی که به خطوط منفرد اشاره می کنند استفاده نکنید: نتیجه انبوهی از نظرات است. علاوه بر این، فکر کردن یک نظر معنادار برای هر خط دشوار است.
- وقتی نظری را می نویسید، سعی کنید به این سوال پاسخ دهید "چرا" نه "چگونه".
- از اطلاعات خلاصه شده خودداری کنید. همانطور که در بالا گفتم، ما برای یک نظر نیازی به توضیح نداریم: نظر خود توضیح است.
- میتوانید از نظرات برای یادداشت واحدها و محدودههای ارزش استفاده کنید.
- نظرات را نزدیک به کدی که توصیف می کنند قرار دهید.
GO TO FULL VERSION