CodeGym /Java Blog /무작위의 /코딩 규칙: 올바른 이름, 좋은 댓글과 나쁜 댓글의 힘
John Squirrels
레벨 41
San Francisco

코딩 규칙: 올바른 이름, 좋은 댓글과 나쁜 댓글의 힘

무작위의 그룹에 게시되었습니다
코딩 규칙: 올바른 이름, 좋은 댓글과 나쁜 댓글의 힘 - 1다른 사람의 코드를 얼마나 자주 파헤쳐야 했습니까? 무슨 일이 일어나고 있는지 논리를 이해하기 위해 2시간이 아닌 2일을 보낼 수 있습니다. 재미있는 점은 코드를 작성한 사람에게는 모든 것이 명확하고 완전히 투명하다는 것입니다. 이것은 놀라운 일이 아닙니다. 결국 완벽한 코드는 매우 모호한 개념입니다. 각 개발자는 세상과 코드에 대한 고유한 비전을 가지고 있기 때문입니다. 동료와 나는 같은 코드를 보고 그 정확성과 깨끗함에 대해 다른 의견을 가진 상황에 한 번 이상 있었습니다.코딩 규칙: 올바른 이름, 좋은 댓글과 나쁜 댓글의 힘 - 2익숙한 것 같지 않나요? 그럼에도 불구하고 준수해야 할 몇 가지 오랜 테스트를 거친 원칙이 있습니다. 결국 그들은 당신에게 유리할 것입니다. 당신이 받고 싶은 상태로 코드를 남겨두면 세상이 조금 더 행복하고 깨끗해질 것이기 때문입니다. 이전 기사 에서(또는 오히려 작은 안내서) 코딩 규칙에 대해 시스템 전체와 개체, 인터페이스, 클래스, 메서드 및 변수와 같은 구성 요소를 작성하기 위한 권장 사항을 약간 얻었습니다. 같은 기사에서 특정 요소의 올바른 이름을 언급했습니다. 오늘 이것에 대해 이야기하고 싶습니다. 올바른 이름을 사용하면 코드를 훨씬 더 쉽게 읽을 수 있기 때문입니다. 올바른 코드에 대한 주제는 약간의 반성, 코드의 작은 주석 예제, 이것이 좋은지 좋지 않은지에 대한 고려로 마무리할 것입니다. 글쎄, 시작하자.

올바른 이름

올바른 이름은 코드 가독성을 향상시켜 코드에 익숙해지는 데 필요한 시간을 줄여줍니다. 이름이 해당 기능을 대략적으로 설명할 때 메서드를 사용하는 것이 훨씬 더 쉽기 때문입니다. 코드의 모든 것은 이름(변수, 메서드, 클래스, 개체, 파일 등)으로 구성되므로 올바르고 깔끔한 코드를 만들 때 이 점이 매우 중요합니다. 위의 내용을 바탕으로 이름은 변수가 왜 존재하는지, 무엇을 하는지, 어떻게 사용되는지 등의 의미를 전달해야 합니다. 변수에 대한 가장 좋은 설명은 좋은 이름을 지정하는 것임을 한 번 이상 언급하겠습니다.코딩 규칙: 올바른 이름, 좋은 댓글과 나쁜 댓글의 힘 - 3

TV 시리즈 "셜록"(2010-2017)에서

명명 인터페이스

인터페이스는 일반적으로 대문자로 시작하는 이름을 가지며 CamelCase로 작성됩니다. 인터페이스를 작성할 때 인터페이스로 지정하기 위해 접두사 "I"를 추가하는 것이 좋은 방법으로 간주되었지만(예: IUserService) 보기 흉하고 산만해 보입니다. 이러한 경우 접두사(UserService)를 생략하고 구현 이름에 접미사로 "Impl"을 추가하는 것이 좋습니다(예: UserServiceImpl). 또는 최후의 수단으로 구현 이름에 "C" 접두사를 추가합니다(예: CUserService).

클래스 이름

인터페이스와 마찬가지로 클래스 이름은 대문자로 표시되며 CamelCase를 사용합니다. 우리가 좀비 아포칼립스에 직면하고 있든 상관없습니다. 종말이 가까이 와도 상관없습니다. 절대, 절대, 절대로 클래스 이름이 동사가 되어서는 안 됩니다! 클래스 및 개체 이름은 명사 또는 복합 명사(UserController, UserDetails, UserAccount 등)여야 합니다. 불필요한 복잡성만 추가할 수 있으므로 각 클래스 이름 끝에 응용 프로그램의 약어를 붙여서는 안 됩니다. 예를 들어 사용자 데이터 마이그레이션 애플리케이션이 있는 경우 각 클래스(예: UDMUserDetails, UDMUserAccount, UDMUserController)에 "UDM"을 추가하지 마십시오.

메서드 이름

일반적으로 메서드 이름은 소문자로 시작하지만 카멜 케이스 스타일(camelCase)도 사용합니다. 위에서 우리는 클래스 이름이 동사가 되어서는 안된다고 말했습니다. 여기에서는 상황이 정반대입니다. 메소드의 이름은 동사 또는 동사구여야 합니다: findUserById, findAllUsers, createUser 등. 메소드(변수 및 클래스 포함)를 생성할 때 혼동을 피하기 위해 일관된 명명 규칙을 사용하십시오. 예를 들어 사용자를 찾기 위해 메서드 이름을 getUserById 또는 findUserById로 지정할 수 있습니다. 그리고 한 가지 더: 방법의 이름에 유머를 사용하지 마십시오. 다른 사람들이 농담을 이해하지 못할 수 있기 때문입니다. 결과적으로 그들은 방법이 무엇을 하는지 파악하지 못할 수 있습니다.

변수 이름

대부분의 경우 변수 이름은 소문자로 시작하고 변수가 전역 상수인 경우를 제외하고 낙타 케이스도 사용합니다. 이 경우 이름의 모든 문자는 대문자로 작성되고 단어는 밑줄("_")로 구분됩니다. 편의를 위해 변수 이름을 지정할 때 의미 있는 컨텍스트를 사용할 수 있습니다. 즉, 변수가 더 큰 것의 일부로 존재하는 경우(예: firstName, lastName 또는 status)입니다. 이러한 경우 이 변수가 속한 개체를 나타내는 접두사를 추가할 수 있습니다. 예: userFirstName, userLastName, userStatus. 또한 변수가 완전히 다른 의미를 가질 때 유사한 이름을 피해야 합니다. 다음은 변수 이름에 자주 사용되는 반의어입니다.
  • 시작/끝
  • 처음/마지막
  • 잠금/잠금 해제
  • 최소 최대
  • 다음/이전
  • 구/신
  • 열림/닫힘
  • 보이는/보이지 않는
  • 소스/타깃
  • 소스/대상
  • 위아래

짧은 변수 이름

x나 n과 같은 변수가 있으면 코드를 작성한 사람의 의도를 즉시 알 수 없습니다. n이 무엇을 하는지는 명확하지 않습니다. 그것을 알아내려면 더 신중한 숙고가 필요합니다(그리고 이것은 시간, 시간, 시간을 의미합니다). 예를 들어 담당 사용자의 ID를 나타내는 필드가 있다고 가정합니다. 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과 maximumNumberOfUsersInTheCurrentGroup 사이입니다. 즉, 짧은 이름은 의미가 부족하고 너무 긴 이름은 가독성을 추가하지 않고 프로그램을 길어지게 하며 매번 이름을 쓰기에는 너무 게을러집니다. n과 같은 짧은 이름을 가진 변수에 대해 위에서 설명한 경우를 제외하고 약 8-16자 길이를 고수해야 합니다. 이것은 엄격한 규칙이 아니라 지침일 뿐입니다.

작은 차이

이름의 미묘한 차이를 언급하지 않을 수 없습니다. 이러한 차이는 단순히 혼동을 주거나 차이를 알아차리기 위해 많은 추가 시간을 소비해야 할 수 있기 때문에 이것은 또한 나쁜 습관입니다. 예를 들어 InvalidDataAccessApiUsageException과 InvalidDataAccessResourceUsageException의 차이점은 한눈에 파악하기 어렵습니다. 또한 소문자 L과 O를 사용할 때 혼동이 자주 발생할 수 있습니다. 1과 0으로 쉽게 오인될 수 있기 때문입니다. 일부 글꼴에서는 차이가 더 분명하고 일부에서는 차이가 적습니다.

의미

예를 들어 UserData와 UserInfo는 실제로 동일한 의미를 갖기 때문에 이름을 의미 있게 만들어야 하지만 동의어를 통해 모호성을 만들지 않아야 합니다. 이 경우 필요한 특정 개체를 이해하려면 코드를 더 깊이 파고들어야 합니다. 유용한 정보를 전달하지 않는 단어는 피하십시오. 예를 들어, firstNameString에서 String이라는 단어가 필요한 이유는 무엇입니까? 이것이 정말 Date 객체일 수 있습니까? 당연히 아니지. 따라서 우리는 단순히 firstName을 사용합니다. 부울 변수도 언급하고 싶습니다. 예를 들어 flagDeleted라는 부울을 사용합니다. 플래그라는 단어는 의미가 없습니다. isDeleted라고 부르는 것이 더 합리적입니다.

그릇된 정보

또한 잘못된 명명 규칙에 대해 몇 마디 말하고 싶습니다. userActivityList라는 변수가 있지만 List가 아니라 이 개체가 다른 컨테이너 유형 또는 사용자 지정 저장소 개체라고 가정해 보겠습니다. 이는 일반 프로그래머에게 혼란을 줄 수 있습니다. userActivityGroup 또는 userActivities와 같은 이름으로 부르는 것이 좋습니다.

찾다

짧고 단순한 이름의 단점 중 하나는 큰 코드 본문에서 찾기 어렵다는 것입니다. "name" 또는 "NAME_FOR_DEFAULT_USER" 중 어느 것이 더 찾기 쉬울까요? 물론 두 번째 옵션입니다. 이름에 자주 나오는 단어(문자)는 검색 중에 일치하는 파일의 수만 증가하므로 좋지 않으므로 피해야 합니다. 프로그래머는 코드를 작성하는 것보다 읽는 데 더 많은 시간을 소비하므로 응용 프로그램 요소의 이름을 현명하게 지정해야 합니다. 하지만 좋은 이름을 찾을 수 없다면 어떨까요? 메서드 이름이 해당 기능을 잘 설명하지 못하는 경우에는 어떻게 해야 합니까? 댓글이 무대에 들어가는 곳입니다.

코멘트

코딩 규칙: 올바른 이름, 좋은 댓글과 나쁜 댓글의 힘 - 4적절한 주석보다 더 좋은 것은 없지만 무의미하고 구식이거나 잘못된 주석과 같이 모듈을 어지럽히는 것은 없습니다. 그들은 양날의 검이 될 수 있습니다. 그래도 댓글을 명백하게 좋은 것으로 취급해서는 안 되며 오히려 덜 나쁜 것으로 취급해야 합니다. 결국 주석은 본질적으로 코드에서 명확하게 나오지 않는 생각을 보상하는 방법입니다. 예를 들어 방법 자체가 너무 혼란스러운 경우 방법의 본질을 전달하기 위해 사용합니다. 이 상황에서는 설명 메모를 작성하는 것보다 코드를 올바르게 리팩터링하는 것이 좋습니다. 오래된 주석일수록 코드가 성장하고 발전하는 경향이 있기 때문에 주석은 더 나쁩니다. 그러나 주석은 동일하게 유지될 수 있습니다. 댓글이 생성된 후 시간이 지날수록 더 의심스러울 수 있습니다. 부정확한 의견은 혼란스럽고 기만적이며 잘못된 기대를 주기 때문에 전혀 의견이 없는 것보다 훨씬 더 나쁩니다. 그리고 매우 까다로운 코드가 있더라도 주석을 달기보다는 다시 작성해야 합니다.

댓글 유형

  • 법률 설명 — 법적인 이유로 각 소스 파일의 시작 부분에 있는 설명입니다. 예를 들면 다음과 같습니다.

    
    * 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);
           }
    

    여기서 우리는 다운로드 작업을 수행하는 사용자(보안 컨텍스트에서 추출할 ID)와 저장 작업을 수행한 사용자의 비교를 추가해야 한다는 사실에 주목합니다.

  • 강화 댓글 — 언뜻 보면 사소해 보일 수 있는 상황의 중요성을 강조하는 댓글입니다.

    예를 들어, 일부 스크립트로 테스트 데이터베이스를 채우는 메소드를 고려하십시오.

    
    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이 false이면 연결이 닫히고 그 반대가 아니라는 점에서 그것이 우리에게 약간 거짓말이라는 사실입니다.

  • 필수 주석 — 필수로 간주되는 주석(예: 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)의 출현으로 코드가 불필요하게 복잡해지고 복잡해졌습니다.

  • Authorship comment — 코드를 작성한 사람을 표시하는 것이 목적인 주석으로, 그 사람에게 연락하여 어떻게, 무엇을, 왜에 대해 논의할 수 있습니다. 예를 들면 다음과 같습니다.

    
    * @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?

이미 댓글을 작성하기로 결정했다면 다음과 같은 몇 가지 팁이 있습니다.
  1. 유지하기 쉬운 스타일 사용: 너무 화려하고 이국적인 스타일을 유지하는 것은 번거롭고 시간이 많이 걸립니다.
  2. 한 줄을 참조하는 줄 끝 주석을 사용하지 마십시오. 결과적으로 주석 더미가 커집니다. 더군다나 대사 하나하나에 의미 있는 코멘트를 떠올리기도 어렵다.
  3. 댓글을 작성할 때 "어떻게"가 아니라 "왜"라는 질문에 답하도록 노력하십시오.
  4. 요약 정보를 피하십시오. 위에서 말했듯이 댓글에 대한 설명은 필요하지 않습니다. 댓글 자체가 설명입니다.
  5. 주석을 사용하여 단위 및 값 범위를 기록할 수 있습니다.
  6. 주석이 설명하는 코드 가까이에 주석을 배치하십시오.
마지막으로, 최고의 댓글은 댓글이 아니라 애플리케이션 전반에 걸쳐 능숙한 이름을 사용하는 것임을 상기시켜 드리고 싶습니다. 일반적으로 대부분의 시간 동안 기존 코드를 사용하여 유지하고 확장합니다. 나쁜 코드는 장애물이기 때문에 이 코드가 읽기 쉽고 이해하기 쉬울 때 훨씬 더 편리합니다. 작업에 렌치를 던지는 것과 같으며 서두름은 충실한 동반자입니다. 그리고 나쁜 코드가 많을수록 성능이 더 떨어집니다. 이는 때때로 리팩토링이 필요함을 의미합니다. 그러나 처음부터 다음 개발자가 당신을 찾아 죽이려고 하지 않는 코드를 작성하려고 하면 자주 리팩터링할 필요가 없습니다. 그러나 제품의 조건과 요구 사항이 새로운 종속성과 연결이 추가됨에 따라 지속적으로 변경되기 때문에 여전히 필요합니다. 글쎄, 오늘은 그게 다인 것 같아. 여기까지 읽어주신 모든 분들께 감사드립니다 :)
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION