正確命名
正確的名稱可以提高程式碼的可讀性,從而節省熟悉時間,因為當名稱大致描述其功能時,使用方法會更容易。由於程式碼中的所有內容都由名稱(變數、方法、類別、文件物件等)組成,因此在創建正確、乾淨的程式碼時,這一點變得非常重要。基於上述所述,名稱應該傳達變數的含義,例如變數存在的原因、它的作用以及如何使用它。我會一次又一次地指出,描述變數的最佳註釋是它的正確名稱。命名介面
介面通常使用以大寫字母開頭並以駝峰式大小寫 (CamelCase) 書寫的名稱。在編寫介面時,在其前面加上 I 前綴以將其指定為介面(例如 IUserService),這曾經是一個很好的做法,但這非常醜陋且分散注意力。在這種情況下,最好不要寫它(UserService),並在其實作中新增-Impl(UserServiceImpl)。好吧,或者作為最後的手段,將前綴 C (CUserService) 添加到其實作中。類別名
就像介面一樣,名稱大寫並使用駝峰風格(CamelCase)。無論發生什麼樣的災難,無論截止日期有多快,但永遠不要記住,類別的名稱永遠不應該是動詞!類別和物件名稱必須是名詞及其組合(UserController、UserDetails、UserAccount 等)。您不應為每個類別的名稱提供該應用程式的縮寫,因為這只會增加不必要的複雜性(例如,我們有一個用戶資料遷移應用程序,我們將向每個類別添加一個UDM - UDMUserDeatils、 UDMUserAccount、UDMUserController )。方法名稱
通常方法的名稱以小寫字母開頭,但也使用駝峰式(CamelCase)。上面我們討論了類名永遠不該是動詞的事實。這裡的情況是截然相反的:方法的名稱必須是動詞或其與動詞的組合:findUserById、findAllUsers、createUser 等。建立方法(以及變數和類別)時,為避免混淆,請使用一種命名方法。例如,要尋找用戶,該方法可以寫為 getUserById 或 findUserById。還有一件事:不要在方法名稱中使用幽默,因為他們可能不明白這個笑話,以及這個方法的作用。變數名稱
在大多數情況下,變數名稱以小寫字母開頭,也使用駝峰式命名,除非變數是全域常數。在這種情況下,名稱的所有字母均以大寫形式書寫,並且在單字之間用下劃線“_”分隔。命名變數時,為了方便起見,您可以使用有意義的上下文。換句話說,當有一個變數作為較大物件的一部分時(例如,firstName、lastName、status),在這種情況下,您可以添加一個前綴,指示該變數所屬的物件。例如:使用者名字、使用者姓氏、使用者狀態。當變數具有完全不同的含義時,您還需要避免使用類似的名稱。變項的常見反義詞:- 開始/結束
- 第一個/最後一個
- 鎖定/解鎖
- 最小/最大
- 下一個/上一個
- 老新
- 開啟/關閉
- 可見/不可見
- 源/目標
- 來源/目的地
- 上/下
短變數名
當我們有像 x 或 n 之類的變數時,我們不會立即看出編寫程式碼的人的意圖。方法 n 的作用並不明顯:它需要更多深思熟慮的思考(那就是時間,時間,時間)。例如,我們有一個欄位 - 負責使用者的 id,我們將呼叫此變數 ResponsibleUserId,而不是像 x 或只是 id 這樣的名稱,這會立即增加可讀性和意義。然而,像 n 這樣的短名稱在小方法的本地更改中佔有一席之地,其中包含該更改的程式碼區塊只是幾行程式碼,並且方法名稱完美地描述了那裡發生的情況。開發人員看到這樣的變量,就會明白它的次要重要性和非常有限的範圍。因此,變數名稱的長度存在一定依賴性:變數名稱越長,變數就越全局,反之亦然。例如,按日期尋找最後儲存的使用者的方法:public User findLastUser() {
return findAllUsers().stream()
.sorted((x, y) -> -x.getCreatedDate().compareTo(y.getCreatedDate()))
.findFirst()
.orElseThrow(() -> new ResourceNotFoundException("Any user doesn't exist "));
}
這裡我們使用短名稱 x 和 y 來設定流排序,然後忘記它們。
最佳長度
讓我們繼續名字長度的話題。最佳名稱長度介於 MaximumNumberOfUsersInTheCurrentGroup 名稱長度和 n 之間。也就是說,太短的程式碼會缺乏意義,而太長的程式碼會拉伸程式而不增加可讀性,而且他們只是懶得每次都寫它們。不考慮上述情況,對於像 n 這樣的短名稱變量,您需要將長度保持在大約 8-16 個字元。這不是一個嚴格的規則:更多的是一個指導方針。小差異
我不能忽視名稱中的細微差異,因為這也是一種不好的做法,因為您可能會感到困惑或花費大量額外時間來注意名稱中的細微差異。例如,InvalidDataAccessApiUsageException 和 InvalidDataAccessResourceUsageException 之間的差異很難一眼看出。此外,使用小 L 和 O 時經常會出現錯誤訊息,因為它們很容易與 1 和 0 混淆:在某些字體中,差異更明顯,而在其他字體中則不那麼明顯。語意部分
我們需要將語義部分放入名稱中,但不要過度使用同義詞,因為例如 UserData 和 UserInfo 實際上具有相同的含義,並且我們必須更深入地研究程式碼以了解我們需要什麼特定物件。避免使用無資訊的單字,例如,firstNameString:為什麼我們需要單字 string?名稱可以是日期類型物件嗎?當然不是:因此,簡單地說 - 名字。作為一個例子,我想提到布林變量,例如 flagDelete。「標誌」一詞不具有任何語義意義。將其稱為“isDelete”會更合理。虛假資訊
我還想就錯誤的命名說幾句話。假設我們的名稱為 userActivityList,且名為 userActivityList 的物件不是 List 類型,而是用於儲存的其他容器或自訂物件。這可能會讓普通程式設計師感到困惑:最好將其稱為 userActivityGroup 或 userActivities 之類的名稱。搜尋
短而簡單的名稱的缺點之一是它們很難在大量程式碼中找到,因為什麼更容易找到:名為 name 或 NAME_FOR_DEFAULT_USER 的變數?當然是第二種選擇。有必要避免名稱中經常出現的單字(字母),因為這只會增加搜尋過程中找到的檔案數量,這是不好的。我們想提醒您,程式設計師花在閱讀程式碼上的時間比編寫程式碼的時間多,因此請注意應用程式元素的命名。但如果你沒能成功命名呢?如果方法的名稱不能很好地描述其功能怎麼辦?這就是它發揮作用的地方,我們的下一個項目是評論。評論
沒有什麼比相關的評論更好的了,但沒有什麼比無意義的、過時的或誤導性的評論更能讓模組變得混亂了。這是一把雙面刃,不是嗎?儘管如此,你不應該將評論視為明確的善:相反,應將其視為較小的惡。畢竟,註釋本質上是對程式碼中未成功表達的思想的補償。例如,如果結果太令人困惑,我們會使用它們以某種方式傳達該方法的本質。在這種情況下,最好正確地重構程式碼,而不是編寫描述性註解。註解越舊,情況就越糟糕,因為程式碼往往會成長和演變,但註解可以保持不變,而且越深入,這些註解就變得越可疑。不準確的評論比沒有評論更糟糕,因為它們會造成混亂和欺騙,給人錯誤的期望。即使我們有一個非常棘手的程式碼,仍然值得不對其進行評論,而是重寫它。評論類型
-
法律註釋是出於法律原因留在每個原始碼檔案開頭的註釋,例如:
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
-
資訊性註釋- 提供代碼解釋的註釋(提供給定代碼部分的附加資訊或意圖。
舉個例子:
/* * Объединяет пользователя из бд и пришедшего для обновления * Когда в requestUser поле пустое, оно заполняется старыми данными из 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):
// Слишком долго отрабатывает // Не запускайте, если не располагаете избытком времени @Ignore @Test public void someIntegrationTest() { …… }
-
TODO - 註釋是未來需要完成的註釋,但由於某些原因現在無法完成。這是一個很好的做法,但仍然需要定期審查它們以刪除不相關的內容以避免混亂。
Примером послужит:
//TODO: Add a check for the current user ID (when will be created security context) @Override public Resource downloadFile(File file) { return fileManager.download(file); }
Тут мы помечаем, что нужно добавить проверку юзера, который скачивает (id которого мы вытащим из security контекста) с тем, кто сохранил.
-
усorвающий комментарий — комментарий, подчеркивающий важность Howого-то обстоятельства, что на первый взгляд может показаться несущественным.
Как пример, кусочек метода, заполняющий тестовую БД, некими скриптами:
Stream.of(IOUtils.resourceToString("/fill-scripts/" + x, StandardCharsets.UTF_8) .trim() .split(";")) .forEach(jdbcTemplate::update); // Вызов trim() очень важен, убирает возможные пробелы в конце скрипта // чтобы при считке и разбивке на отдельные requestы не было пустых
-
javaDoc — комментарии, которые описывают API определенного функционала для общего пользования. Наверное, самые полезные комментарии, так How с documentированным API в разы легче работать, но они также могут устаревать, How и любые другие. Поэтому не забываем, что главный вклад в documentацию вносится не комментариями, а хорошим codeом.
Пример вполне обычного метода обновления пользователя:
/** * Обновляет передаваемые поля для пользователя по id. * * @param id id обновляемого пользователя * @param user пользователь с заполненными полями для обновления * @return обновленный пользователь */ User update(Long id, User user);
Плохие сценарии комментариев
-
бормочущий комментарий — комментарии, которые обычно пишут на скорую руку, смысл которых понятен только разработчику, писавшего их, так How только он видит ту ситуацию с теми нюансами, на которые он и ссылается.
Рассмотрим данный пример:
public void configureSomeSystem() { try{ String configPath = filesLocation.concat("/").concat(CONFIGURATION_FILE); FileInputStream stream = new FileInputStream(configPath); } catch (FileNotFoundException e) { //В случае отсутствия конфигурационного file, загружается конфигурация по умолчанию } }
Кто загружает эти настройки? Были ли они загружены ранее? Метод предназначен для перехвата исключений и вызова дефолтных настроек? Слишком много вопросов возникает, ответы на которые можно получить лишь углубившись в изучение других частей системы.
-
избыточный комментарий — комментарий, который не несёт смысловой нагрузки, так How и так понятно что происходит в заданном участке codeа (он читается не проще, чем code).
Смотрим пример:
public class JdbcConnection{ public class JdbcConnection{ /** * Журнальный компонент, связанный с текущим классом */ private Logger log = Logger.getLogger(JdbcConnection.class.getName()); /** * Создаёт и возвращает connection с помощью входящих параметров */ 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; }
Какой смысл таких комментариев, если мы и так всё прекрасно видим
-
недостоверные комментарии — комментарии, не соответствующие истине и лишь вгоняющие в заблуждение (дезинформирующие). Как например:
/** * Вспомогательный метод, закрывает соединение со сканером, если isNotUsing истинно */ private void scanClose(Scanner scan, boolean isNotUsing) throws Exception { if (!isNotUsing) { throw new Exception("The scanner is still in use"); } scan.close(); }
What в этом комменте не так? А то, что он немножко врёт нам, ведь соединение закрывается, если isNotUsing = false, но ниHow не наоборот, How нам вещает пометка.
-
обязательные комментарии — комментарии, которые считают обязательными (Javadoc), но кои по факту иногда бывают излишне нагромождающими, недостоверными и ненужными (нужно задуматься, а нужны ли здесь такие комментарии).
Пример:
/** * Creation пользователя по переданным параметрам * @param firstName Name созданного пользователя * @param middleName среднее Name созданного пользователя * @param lastName фамorя созданного пользователя * @param age возраст созданного пользователя * @param address addressс созданного пользователя * @return пользователь который был создан */ User createNewUser(String firstName, String middleName, String lastName, String age, String address);
Смогли бы вы понять, что делает метод без этих комментариев? Скорее всего да, поэтому комментарии в этом случае стают бессмысленными.
-
журнальные комментарии — комментарии, которые иногда добавляют в начало модуля, при каждом его редактировании (что-то вроде журнала вносимых изменений).
/** * Записи ведутся с 09 января 2020; ********************************************************************** * 09.01.2020 : Обеспечение соединения с БД с помощью Jdbc Connection; * 15.01.2020 : Добавление интерфейсов уровня дао для работы с БД; * 23.01.2020 : Добавление интеграционных тестов для БД; * 28.01.2020 : Имплементация интерфейсов уровня дао; * 01.02.2020 : Разработка интерфейсов для сервисов, * согласно требованиям прописанным в user stories; * 16.02.2020 : Имплементация интерфейсов сервисов * (реализация бизнес логики связанной с работой БД); * 25.02.2020 : Добавление тестов для сервисов; * 08.03.2020 : Празднование восьмого марта(Миша опять в хлам); * 21.03.2020 : Рефакторинг сервис слоя; */
Когда-то этот проход был оправдан, но с появлением систем управления исходным codeом (например — Git), это стало лишним нагромождением и усложнением codeа.
-
комментарии ссылки на авторов — комментарии, преднаmeaningм которых является, указание человека, писавшего code, чтобы можно было связаться и обсудить, How что и зачем:
* @author Bender Benderovich
Опять же, системы контроля версий прекрасно запоминают, кто и когда добавил данный code, и подобный подход излишен.
-
註解程式碼是由於某種原因被註解掉的程式碼。最糟糕的習慣之一,因為你註解掉了它然後忘記了,而其他開發人員根本沒有勇氣刪除它(如果它是有價值的東西怎麼辦)。
// public void someMethod(SomeObject obj) { // ..... // }
結果,它像垃圾一樣堆積起來。在任何情況下都不應該留下這樣的程式碼。如果您確實需要它,請不要忘記版本控制系統。
-
非顯而易見的註釋是以不必要的複雜方式描述某事物的註釋。
/* * Начать с массива, размер которого достаточен для хранения * всех byteов данных (плюс byteы фильтра) с запасом, плюс 300 byte * для данных заголовка */ this.dataBytes = new byte[(this.size * (this.deep + 1) * 2)+300];
註釋應該解釋程式碼,而不需要解釋本身。這裡發生了什麼事?什麼是“過濾位元組”?+1 與此有什麼關係?為什麼正好是300?
- 使用易於維護的樣式:維護過於花哨和異國情調的樣式可能會很煩人和耗時。
- 不要在引用單行的行末尾使用註釋:這會產生大量註釋,並且很難為每一行提供富有表現力的註釋。
- 在建立評論時,請嘗試回答“為什麼”而不是“如何”的問題。
- 避免縮寫。正如我上面所說,我們不需要對評論進行解釋:評論就是解釋。
- 您可以使用註解來標記測量單位和可接受值的範圍。
- 將註釋放在靠近它們描述的程式碼的地方。
GO TO FULL VERSION