Skip to content

Conversation

@ca1af
Copy link
Collaborator

@ca1af ca1af commented Oct 30, 2023

No description provided.

@deok-beom
Copy link

코드의 구조나 각 객체들의 역할과 책임 등에 대해서는 피드백 할 거리가 없을 만큼 잘 작성되었습니다. 👍
리팩토링을 위해 많이 고민한 흔적이 보입니다.

굳이 한가지를 꼽자면 코드 작성 스타일에 대해서도 한 번 고민해보면 좋을 것 같습니다.

  1. 클래스 내용(변수, 메소드)의 순서는 어떤 '논리적 순서'를 가지고 작성한 것인지 생각해보세요 (메소드가 추가된 순서대로 정렬되는 것은 비추🙅‍♂️)
  2. 중괄호를 사용할 때 많이 사용되는 Egyption Brackets에 대해서도 한 번 살펴보면 좋을 것 같습니다.

@deok-beom
Copy link

저의 경우에는 클래스를 다음과 같은 스타일로 작성합니다.

  1. 필드, 초기화 블록은 가장 상위에
  • 상수
  • 클래스 변수
  • 클래스 초기화 블록
  • 인스턴스 변수
  • 인스턴스 초기화 블록
  • 위의 각 각 필드들은 순서가 바뀌더라도 같은 속성끼리 묶여서 배치되는 것이 좋고 각 그룹을 띄어쓰기로 구분짓는 것도 좋은 방법이라고 생각됩니다.
  1. 그 다음 생성자가 위치
  • 실제 객체를 생성하는 주 생성자가 첫번째로
  • 주 생성자를 호출하는 부 생성자들을 다음에 위치
  • 주 생성자와 부 생성자가 여러개라면, 주 생성자를 놓고 그 주 생성자를 호출하는 부 생성자를 아래에 위치시킨 뒤, 다음 주 생성자와 부 생성자들을 정렬
  1. 그 다음 메소드가 위치
  • lombok을 사용하지 않는다면 getter, setter 가 먼저 위치 (제일 뒤로 빼야 한다는 의견도 있음.. 이 의견도 합리적)
  • public 메소드를 먼저 위치시키고 해당 메소드 내부에서 호출하는 private 메소드는 호출되는 순서대로 바로 아래에 위치 시킨다.
  • equals(), hashCode(), toString() 처럼 Object 클래스를 재작성한 메소드를 마지막에 위치 시킨다.

@ca1af
Copy link
Collaborator Author

ca1af commented Nov 17, 2023

저의 경우에는 클래스를 다음과 같은 스타일로 작성합니다.

  1. 필드, 초기화 블록은 가장 상위에
  • 상수
  • 클래스 변수
  • 클래스 초기화 블록
  • 인스턴스 변수
  • 인스턴스 초기화 블록
  • 위의 각 각 필드들은 순서가 바뀌더라도 같은 속성끼리 묶여서 배치되는 것이 좋고 각 그룹을 띄어쓰기로 구분짓는 것도 좋은 방법이라고 생각됩니다.
  1. 그 다음 생성자가 위치
  • 실제 객체를 생성하는 주 생성자가 첫번째로
  • 주 생성자를 호출하는 부 생성자들을 다음에 위치
  • 주 생성자와 부 생성자가 여러개라면, 주 생성자를 놓고 그 주 생성자를 호출하는 부 생성자를 아래에 위치시킨 뒤, 다음 주 생성자와 부 생성자들을 정렬
  1. 그 다음 메소드가 위치
  • lombok을 사용하지 않는다면 getter, setter 가 먼저 위치 (제일 뒤로 빼야 한다는 의견도 있음.. 이 의견도 합리적)
  • public 메소드를 먼저 위치시키고 해당 메소드 내부에서 호출하는 private 메소드는 호출되는 순서대로 바로 아래에 위치 시킨다.
  • equals(), hashCode(), toString() 처럼 Object 클래스를 재작성한 메소드를 마지막에 위치 시킨다.

저의 경우에는 클래스를 다음과 같은 스타일로 작성합니다.

  1. 필드, 초기화 블록은 가장 상위에
  • 상수
  • 클래스 변수
  • 클래스 초기화 블록
  • 인스턴스 변수
  • 인스턴스 초기화 블록
  • 위의 각 각 필드들은 순서가 바뀌더라도 같은 속성끼리 묶여서 배치되는 것이 좋고 각 그룹을 띄어쓰기로 구분짓는 것도 좋은 방법이라고 생각됩니다.
  1. 그 다음 생성자가 위치
  • 실제 객체를 생성하는 주 생성자가 첫번째로
  • 주 생성자를 호출하는 부 생성자들을 다음에 위치
  • 주 생성자와 부 생성자가 여러개라면, 주 생성자를 놓고 그 주 생성자를 호출하는 부 생성자를 아래에 위치시킨 뒤, 다음 주 생성자와 부 생성자들을 정렬
  1. 그 다음 메소드가 위치
  • lombok을 사용하지 않는다면 getter, setter 가 먼저 위치 (제일 뒤로 빼야 한다는 의견도 있음.. 이 의견도 합리적)
  • public 메소드를 먼저 위치시키고 해당 메소드 내부에서 호출하는 private 메소드는 호출되는 순서대로 바로 아래에 위치 시킨다.
  • equals(), hashCode(), toString() 처럼 Object 클래스를 재작성한 메소드를 마지막에 위치 시킨다.

클래스 내용의 순서를 적절히 잘 신경쓰면 더 가독성 있는 코드를 만들 수 있겠군요! ㅎㅎ 감사합니당

"Egyptian brackets" 부분은 아마도

if (someCondition) throw ;

와 같이 작성된 부분을 보고 말씀해주신 것 같은데 혹시 맞을까요?

해당 부분은 얼른 수정해보겠읍니당

@minseon33
Copy link

위에서 말한대로 코드에 대해 더이상 할 이야기가 없을만큼 잘 작성된 것 같습니다.
덕분에 코드 스타일이며 객체지향적으로 역할 분리하는것에 대해 많이 배워갈 수 있어서 정말 감사합니다.

특히 클래스 내용(변수, 메소드)의 순서에 대해서는 한번도 고민해보지 않았던 주제라서 매우 흥미게 읽었습니다.
지금까지 변수를 생각없이 뒤죽박죽으로 섞어놓았는데 바로 실제 업무 코드에 적용해봐야겠습니다.

그런데 한가지 궁금한점이
위에서도 순서는 상관없다고 하셨지만 필드중에서 상수를 제일 위에 제시하고 있는데 그렇게 하는 이유가 있을까요?
또한 생성자와 초기화블록이 헷갈려서 개념을 찾아보니 객체를 생성과 동시에 초기화 블록을 실행하여 초기화 하는것이라고 하더라구요..!

그렇다면 아래 코드처럼 필드에 바로 초기화 할 수도 있는데
public class Example{ int instanceVar = 10; }

굳이 초기화 블럭을 사용하여 초기화하는 이유는 무엇이라고 생각하시나요?
그저 코드의 가독성 때문인가요?

@deok-beom
Copy link

한가지 궁금한점이 위에서도 순서는 상관없다고 하셨지만 필드중에서 상수를 제일 위에 제시하고 있는데 그렇게 하는 이유가 있을까요?

일단 중요한 것은 클래스에서 쓰이는 필드(static이 붙은 필드)와 인스턴스 범위에서 쓰이는 필드들이 묶여있다는 것이 중요하다고 생각합니다.
상수도 클래스 범위에서 사용되는 상수(static final이 붙은 필드)와 인스턴스 각 각에서 쓰이는 상수(final만 붙은 필드)들이 있을 수 있는데 그 순서에 상관 없이 그룹으로 잘 묶여있는 것이 중요하다고 생각합니다.

public class Student {
   // 클래스 범위
   private final static String COMPANY_NAME= "스파르타 코딩클럽";
   private static int someClassNumber;
   private static String someClassString;

   // 인스턴스 범위
   private final int GENERATION;
   private final int SOME_COMPLEX_NUMBER;
   private int someInstanceString;
   protected String campName;

   {
       campName = "내일배움캠프";
   }
   
   public Student(int generation) {
       this.generation = generation;
   }
}

또한 생성자와 초기화블록이 헷갈려서 개념을 찾아보니 객체 생성과 동시에 초기화 블록을 실행하여 초기화 하는것이라고 하더라구요..!
그렇다면 아래 코드처럼 필드에 바로 초기화 할 수도 있는데
public class Example{ int instanceVar = 10; }

굳이 초기화 블럭을 사용하여 초기화하는 이유는 무엇이라고 생각하시나요? 그저 코드의 가독성 때문인가요?

클래스 초기화 블럭(혹은 스태틱 초기화 블럭, static이 붙은 것)의 실행 시점은 프로그램이 실행되고 난 직후 클래스들이 로드될 때 입니다. 해당 시점에 필요한 클래스 필드들의 복잡한 초기화를 실행하거나 필요한 로직들을 수행시킬 수 있습니다.

private static final Logger logger = LoggerFactory.getLogger(TargetClass.class);
...

static {
  complexNumber =  ...뭔가 엄청나게 복잡한 계산을 수행한 ...
  SOME_COMPLEX_NUMBER = complexNumber;
  log.info("스태틱 초기화 블록의 실행 시점은 필드의 직접 초기화가 수행된 이후이기 때문에")
  log.info("이처럼 로그를 남기는 것과 같은 로직들을 수행시킬 수도 있습니다.");
}

인스턴스 초기화 블럭(static이 붙지 않은 것)의 실행 시점은 객체의 생성자를 호출하고, super 생성자를 호출한 다음입니다.
이 블럭을 이용해서 어떤 생성자든 간에 공통으로 수행되어야 하는 로직.. 특히 super 생성자가 호출된 다음에 필요한 로직을 정의할 수 있습니다.

public class Crew extends Student {
    private final String SUBJECT;
    private final String CLASS;

    {
        log.info("{}에 새로운 대원이 합류했습니다!", super.campName);
    }

    public Crew(int generation) {
        super(generation);
        SUBJECT = "스프링";
        CLASS = "B반";
    }
   ...
}

물론 다음과 같이 메소드로 사용하는 방법이라도 동작은 크게 다르지 않습니다.

public class Crew extends Student {
  ...
  public Crew(int generation) {
    super(generation);
    logWelcome();
    SUBJECT = "스프링";
    CLASS = "B반";
  }
  
  public Crew(int generation, String subject) {
    super(generation);
    logWelcome();
    SUBJECT = subject;
    CLASS = "B반";
  }
   
  public Crew(int generation, String subject, String class) {
    super(generation);
    logWelcome();
    SUBJECT = subject;
    CLASS = class;
  }
 
  private void logWelcome() {
    log.info("{}에 새로운 대원이 합류했습니다!", super.campName);
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants