본문 바로가기
네이버클라우드/JAVA 웹 프로그래밍

JAVA 13일차 (2023-06-09) 자바 기초 DAY11_국영수 성적 테스트 만들기

by prometedor 2023. 6. 9.

국영수 성적 테스트 만들기

Step01

app.java

// 1) 낱개의 변수 사용
public class App {
  
  public static void main(String[] args) {
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float aver;

    name = "홍길동";
    kor = 100;
    eng = 100;
    math = 100;
    sum = kor + eng + math;
    aver = sum / 3f;
    System.out.printf("%s: 합계=%d, 평균=%.1f\n", name, sum, aver);
  }

}

ㄴ 낱개의  변수를 사용하여 성적을 출력

 

step02

app.java

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
public class App {
  
  public static void main(String[] args) {
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float aver;

    name = "홍길동";
    kor = 100;
    eng = 100;
    math = 100;
    sum = kor + eng + math;
    aver = sum / 3f;

    System.out.printf("%s: 합계=%d, 평균=%.1f\n", name, sum, aver);

    name = "임꺽정";
    kor = 90;
    eng = 90;
    math = 90;
    sum = kor + eng + math;
    aver = sum / 3f;

    System.out.printf("%s: 합계=%d, 평균=%.1f\n", name, sum, aver);

    name = "유관순";
    kor = 80;
    eng = 80;
    math = 80;
    sum = kor + eng + math;
    aver = sum / 3f;

    System.out.printf("%s: 합계=%d, 평균=%.1f\n", name, sum, aver);


  }

}

ㄴ 낱개의 변수 여러번 재사용하여 출력

=> 여러 명의 정보를 낱개의 변수를 이용하여 출력

 

step03

app.java

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
// 3) 배열 사용
public class App {

  public static void main(String[] args) {
    String[] name = new String[10];
    int[] kor = new int[10];
    int[] eng = new int[10];
    int[] math = new int[10];
    int[] sum = new int[10];
    float[] aver = new float[10];
    int length = 0;

    name[length] = "홍길동";
    kor[length] = 100;
    eng[length] = 100;
    math[length] = 100;
    sum[length] = kor[length] + eng[length] + math[length];
    aver[length] = sum[length] / 3f;
    length++;

    name[length] = "임꺽정";
    kor[length] = 90;
    eng[length] = 90;
    math[length] = 90;
    sum[length] = kor[length] + eng[length] + math[length];
    aver[length] = sum[length] / 3f;
    length++;

    name[length] = "유관순";
    kor[length] = 80;
    eng[length] = 80;
    math[length] = 80;
    sum[length] = kor[length] + eng[length] + math[length];
    aver[length] = sum[length] / 3f;
    length++;

    for (int i = 0; i < length; i++) {
      System.out.printf("%s: 합계=%d, 평균=%.1f\n",
          name[i], sum[i], aver[i]);
    }

  }

}

ㄴ 배열 이용하기

 

step04

app.java

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
// 3) 배열 사용
// 4) 클래스를 이용하여 데이터 타입 정의(중첩클래스; 로컬 클래스)
public class App {

  public static void main(String[] args) {
    // ## 사용자 정의 데이터 타입 만들기
    // - 학생의 성적 데이터를 담을 메모리(변수)를 설계한다.
    //
    class Score {
      // 인스턴스 변수(instance variable; instance field)
      // - new 명령으로 생성되는 변수이다.
      // - 데이터를 개별적으로 다루고 싶을 때 인스턴스 변수로 선언한다.
      //
      String name; // 변수 또는 필드
      int kor;
      int eng;
      int math;
      int sum;
      float aver;
    }

	// 사용자 정의 데이터 타입을 사용하는 방법
    // - new 명령을 사용하여 설계도에 기술된 대로 메모리(변수)를 준비
    // - 변수는 Heap 영역에 생성됨
    // - 변수들이 생성된 메모리의 주소를 레퍼런스(주소 변수)에 저장함
    final int MAX_SIZE = 10;
    Score[] scores = new Score[MAX_SIZE];
    int length = 0;

    Score s = new Score();
    s.name = "홍길동";
    s.kor = 100;
    s.eng = 100;
    s.math = 100;
    s.sum = s.kor + s.eng + s.math;
    s.aver = s.sum / 3f;
    scores[length++] = s;

    s = new Score();
    s.name = "임꺽정";
    s.kor = 90;
    s.eng = 90;
    s.math = 90;
    s.sum = s.kor + s.eng + s.math;
    s.aver = s.sum / 3f;
    scores[length++] = s;

    s = new Score();
    s.name = "유관순";
    s.kor = 80;
    s.eng = 80;
    s.math = 80;
    s.sum = s.kor + s.eng + s.math;
    s.aver = s.sum / 3f;
    scores[length++] = s;

    for (int i = 0; i < length; i++) {
      s = scores[i];
      System.out.printf("%s: 합계=%d, 평균=%.1f\n",
          s.name, s.sum, s.aver);
    }

  }

}
// 클래스 문법의 용도?
// 1) 사용자 정의 데이터 타입 만들 때
//    즉 새로운 구조의 메모리를 설계할 때 사용한다.
// 2) 메서드를 묶을 때
//    서로 관련된 기능을 관리하기 쉽게 묶고 싶을 때 사용한다.

ㄴ Score 라는 클래스를 이용하여 데이터 타입을 정의 (로컬 클래스)

 

step05

app.java

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
// 3) 배열 사용
// 4) 클래스를 이용하여 데이터 타입 정의(중첩클래스; 로컬 클래스)
// 5) 출력 기능을 별도의 메서드로 분리(중첩클래스; 스태틱 중첩 클래스)
public class App {

  static class Score {
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float aver;
  }

  public static void main(String[] args) {

    final int MAX_SIZE = 10;
    Score[] scores = new Score[MAX_SIZE];
    int length = 0;

    Score s = new Score();
    s.name = "홍길동";
    s.kor = 100;
    s.eng = 100;
    s.math = 100;
    s.sum = s.kor + s.eng + s.math;
    s.aver = s.sum / 3f;
    scores[length++] = s;

    s = new Score();
    s.name = "임꺽정";
    s.kor = 90;
    s.eng = 90;
    s.math = 90;
    s.sum = s.kor + s.eng + s.math;
    s.aver = s.sum / 3f;
    scores[length++] = s;

    s = new Score();
    s.name = "유관순";
    s.kor = 80;
    s.eng = 80;
    s.math = 80;
    s.sum = s.kor + s.eng + s.math;
    s.aver = s.sum / 3f;
    scores[length++] = s;

    for (int i = 0; i < length; i++) {
      printScore(scores[i]);
    }

  }

  static void printScore(Score s) {
    System.out.printf("%s: 합계=%d, 평균=%.1f\n",
        s.name, s.sum, s.aver);
  }

}

ㄴ 출력기능을 별도의 메소드로 분리 (스태틱 클래스)

=> printScore 라는 메소드를 static 으로 생성하고  Score 클래스의 변수를  사용하므로 Score 클래스도 static 으로 만들어줌

 

step06

app.java

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
// 3) 배열 사용
// 4) 클래스를 이용하여 데이터 타입 정의(중첩클래스; 로컬 클래스)
// 5) 출력 기능을 별도의 메서드로 분리(중첩클래스; 스태틱 중첩 클래스)
// 6) 합계 및 평균을 계산하는 기능을 메서드로 분리
public class App {

  // 여러 메서드에서 공유하려면 클래스 멤버로 만들어야 함
  // - 특히 스태틱 멤버끼리 공유하려면 같은 스태틱 멤버로 만들어야 함
  static class Score {
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float aver;
  }

  public static void main(String[] args) {

    final int MAX_SIZE = 10;
    Score[] scores = new Score[MAX_SIZE];
    int length = 0;

    Score s = new Score();
    s.name = "홍길동";
    s.kor = 100;
    s.eng = 100;
    s.math = 100;
    compute(s);
    scores[length++] = s;

    s = new Score();
    s.name = "임꺽정";
    s.kor = 90;
    s.eng = 90;
    s.math = 90;
    compute(s);
    scores[length++] = s;

    s = new Score();
    s.name = "유관순";
    s.kor = 80;
    s.eng = 80;
    s.math = 80;
    compute(s);
    scores[length++] = s;

    for (int i = 0; i < length; i++) {
      printScore(scores[i]);
    }

  }
  // 리팩토링: 메서드 추출(extract method), static nested class
  // 리팩토링: 메서드 추출(extract method) = 한 개의 메서드는 한 개의 기능을 수행해야 함
  static void compute(Score s) {
    s.sum = s.kor + s.eng + s.math;
    s.aver = s.sum / 3f;
  }

  static void printScore(Score s) {
    System.out.printf("%s: 합계=%d, 평균=%.1f\n",
        s.name, s.sum, s.aver);
  }

}

ㄴ 합계 및 평균을 계산하는 기능을 메서드로 분리

=> compute 라는 이름의 메서드를 static 으로 생성

 

step07

app.java

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
// 3) 배열 사용
// 4) 클래스를 이용하여 데이터 타입 정의(중첩클래스; 로컬 클래스)
// 5) 출력 기능을 별도의 메서드로 분리(중첩클래스; 스태틱 중첩 클래스)
// 6) 합계 및 평균을 계산하는 기능을 메서드로 분리
// 7) GRASP 패턴: Information Expert(정보를 갖고 있는 클래스가 그 정보를 다룬다.)
public class App {

  static class Score {
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float aver;
    
    // 메서드를 이용하여 이 타입의 데이터를 다룰 수 있는 연산자를 정의함
    // - 사용자 정의 데이터 타입 입장에서는 메서드가 연산자 역할을 함
    // - 즉 사용자 정의 데이터 타입에 메서드를 정의하는 것은 그 데이터를 다룰 연산자를 정의하는 것임

    // Score 데이터 값을 다룰 수 있는 새 연산자를 정의해 보자
    // - 다음 메서드는 Score 객체의 국,영,수 값의 합계와 평균을 계산하는 연산자임
    static void compute(Score s) {
      s.sum = s.kor + s.eng + s.math;
      s.aver = s.sum / 3f;
      
      // 클래스 메서드
      // - static이 붙은 메서드
      // - 특정 인스턴스에 대해 사용하는 것이 아니라, 모든 인스턴스에 대해 사용할 수 있음
      // - 특정 인스턴스의 값을 다루고 싶다면 파라미터로 그 인스턴스의 주소를 받아야 함
    }
  }

  public static void main(String[] args) {

    final int MAX_SIZE = 10;
    Score[] scores = new Score[MAX_SIZE];
    int length = 0;

    Score s = new Score();
    s.name = "홍길동";
    s.kor = 100;
    s.eng = 100;
    s.math = 100;   
    // 다음은 Score의 값을 다루는 연산자가 없을 때의 예임
    // score.sum = score.kor + score.eng + score.math;
    // score.average = score.sum / 3f;

    // 사용자 정의 데이터 타입의 값을 연산자를 사용하여 다뤄보자
    Score.compute(s);
    scores[length++] = s;

    s = new Score();
    s.name = "임꺽정";
    s.kor = 90;
    s.eng = 90;
    s.math = 90;
    Score.compute(s);
    scores[length++] = s;

    s = new Score();
    s.name = "유관순";
    s.kor = 80;
    s.eng = 80;
    s.math = 80;
    Score.compute(s);
    scores[length++] = s;

    for (int i = 0; i < length; i++) {
      printScore(scores[i]);
    }

  }

  static void printScore(Score s) {
    System.out.printf("%s: 합계=%d, 평균=%.1f\n",
        s.name, s.sum, s.aver);
  }

}

ㄴ GRASP 패턴: Information Expert (정보를 갖고 있는 클래스가 그 정보를 다룸)

** GRASP Pattern이란?
General Responsibility Assignment Software Patterns
ㄴ Object-Oriented 디자인의 핵심은 각 객체에 책임을 부여하는 것
ㄴ 책임을 부여하는 원칙들을 말하고 있는 패턴
ㄴ 구체적인 구조는 없지만, 철학을 배울 수 있음
ㄴ 총 9가지의 원칙을 가지고 있음

1) Information Expert
ㄴ 책임을 수행할 수 있는 데이터를 가지고 있는 객체에 책임을 부여하는 것
ㄴ 객체는 데이터와 처리로직이 함께 묶여 있는 것
ㄴ 정보 은닉을 통해 자신의 데이터를 감추고 오직 Method로만 데이터를 처리하고, 외부에는 그 기능(책임)만을 제공

 

step08

app.java

package bitcamp.test.step08;

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
// 3) 배열 사용
// 4) 클래스를 이용하여 데이터 타입 정의(중첩클래스; 로컬 클래스)
// 5) 출력 기능을 별도의 메서드로 분리(중첩클래스; 스태틱 중첩 클래스) 
// 6) 합계 및 평균을 계산하는 기능을 메서드로 분리
// 7) GRASP 패턴: Information Expert(정보를 갖고 있는 클래스가 그 정보를 다룬다.)
// 8) 인스턴스 메서드 도입
public class App {
  
  static class Score {
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float aver;

    void compute() {
      this.sum = this.kor + this.eng + this.math;
      this.aver = this.sum / 3f;
    }
  }
  
  public static void main(String[] args) {

    final int MAX_SIZE = 10;
    Score[] scores = new Score[MAX_SIZE];
    int length = 0;

    Score s = new Score();
    s.name = "홍길동";
    s.kor = 100;
    s.eng = 100;
    s.math = 100;
    s.compute();
    scores[length++] = s;

    s = new Score();
    s.name = "임꺽정";
    s.kor = 90;
    s.eng = 90;
    s.math = 90;
    s.compute();
    scores[length++] = s;

    s = new Score();
    s.name = "유관순";
    s.kor = 80;
    s.eng = 80;
    s.math = 80;
    s.compute();
    scores[length++] = s;

    for (int i = 0; i < length; i++) {
      printScore(scores[i]);
    }

  }

  static void printScore(Score s) {
    System.out.printf("%s: 합계=%d, 평균=%.1f\n", 
      s.name, s.sum, s.aver);
  }

}

ㄴ 인스턴스 메서드 도입

=> compute 메서드에서 static 을 제거하고 this 를 적용

ㄴ 파라미터 넘겨줄 필요가 없음

 

step09

app.java

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
// 3) 배열 사용
// 4) 클래스를 이용하여 데이터 타입 정의(중첩클래스; 로컬 클래스)
// 5) 출력 기능을 별도의 메서드로 분리(중첩클래스; 스태틱 중첩 클래스) 
// 6) 합계 및 평균을 계산하는 기능을 메서드로 분리
// 7) GRASP 패턴: Information Expert(정보를 갖고 있는 클래스가 그 정보를 다룬다.)
// 8) 인스턴스 메서드 도입
// 9) 객체 생성이 번거롭고 복잡한 경우 메서드로 분리하는 것이 낫다.(디자인패턴; 팩토리 메서드)
public class App {

  static class Score {
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float aver;

    void compute() {
      this.sum = this.kor + this.eng + this.math;
      this.aver = this.sum / 3f;
    }
  }

  public static void main(String[] args) {

    final int MAX_SIZE = 10;
    Score[] scores = new Score[MAX_SIZE];
    int length = 0;

    scores[length++] = createScore("홍길동", 100, 100, 100);
    scores[length++] = createScore("임꺽정", 90, 90, 90);
    scores[length++] = createScore("유관순", 80, 80, 80);

    for (int i = 0; i < length; i++) {
      printScore(scores[i]);
    }

  }

  // 팩토리 메서드
  static Score createScore(String name, int kor, int eng, int math) {
    Score s = new Score();
    s.name = name;
    s.kor = kor;
    s.eng = eng;
    s.math = math;
    // 다음은 Score의 값을 다루기 위해 스태틱 메서드를 호출하는 예임
    // => static 메서드 = 클래스 메서드

    //    Score.compute(score);
    //

    // 클래스 메서드를 사용할 때 마다 매번 인스턴스의 주소를 파라미터로 넘겨줘야 했음
    // 그러나 인스턴스 메서드를 사용하면 인스턴스 주소를 넘기기가 더 편함
    // 메서드 호출 앞에다 둠
    // 소스 코드의 목적을 이해하는데 더 직관적임
    s.compute(); // 마치 변수 뒤에 연산자를 놓는 i++ 의 예와 비슷함
    return s;
  }

  static void printScore(Score s) {
    System.out.printf("%s: 합계=%d, 평균=%.1f\n",
        s.name, s.sum, s.aver);
  }

}

ㄴ 객체 생성이 번거롭고 복잡한 경우 메서드로 분리하는 것이 나음(디자인패턴; 팩토리 메서드)

=> createScore 메소드를 static 으로 생성

     ㄴ Score 객체를 생성하여 반환

     ㄴ 매개변수로는 이름(name), 국어 점수(kor), 영어 점수(eng), 수학 점수(math)를 받음

디자인 패턴
1) 생성 패턴
추상 팩토리, 빌더, 팩토리 메소드, 프로토타입, 싱글턴
2) 구조 패턴
어댑터, 브리지, 컴포지트, 데코레이터, 퍼사드, 플라이웨이트, 프록시
3) 행동 패턴
책임 연쇄, 커맨드, 인터프리터, 반복자, 중재자, 메멘토, 옵서버, 상태, 전략, 템플릿 메소드, 비지터

팩토리 메소드
=> 객체 생성을 담당하는 메서드로, 객체를 생성하고 초기화한 후에 해당 객체를 반환하는 역할을 함

https://ko.wikipedia.org/wiki/%EB%94%94%EC%9E%90%EC%9D%B8_%ED%8C%A8%ED%84%B4_(%EC%B1%85)

 

step10

app.java

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
// 3) 배열 사용
// 4) 클래스를 이용하여 데이터 타입 정의(중첩클래스; 로컬 클래스)
// 5) 출력 기능을 별도의 메서드로 분리(중첩클래스; 스태틱 중첩 클래스) 
// 6) 합계 및 평균을 계산하는 기능을 메서드로 분리
// 7) GRASP 패턴: Information Expert(정보를 갖고 있는 클래스가 그 정보를 다룬다.)
// 8) 인스턴스 메서드 도입
// 9) 객체 생성이 번거롭고 복잡한 경우 메서드로 분리하는 것이 낫다.(디자인패턴; 팩토리 메서드)
// 10) GRASP 패턴: Information Expert
//     - createScore()를 Score 클래스로 이동
public class App {

  static class Score {
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float aver;

    void compute() {
      this.sum = this.kor + this.eng + this.math;
      this.aver = this.sum / 3f;
    }

    static Score create(String name, int kor, int eng, int math) {
      Score s = new Score();
      s.name = name;
      s.kor = kor;
      s.eng = eng;
      s.math = math;
      s.compute();
      return s;
    }
  }

  public static void main(String[] args) {

    final int MAX_SIZE = 10;
    Score[] scores = new Score[MAX_SIZE];
    int length = 0;

    scores[length++] = Score.create("홍길동", 100, 100, 100);
    scores[length++] = Score.create("임꺽정", 90, 90, 90);
    scores[length++] = Score.create("유관순", 80, 80, 80);

    for (int i = 0; i < length; i++) {
      printScore(scores[i]);
    }

  }

  static void printScore(Score s) {
    System.out.printf("%s: 합계=%d, 평균=%.1f\n",
        s.name, s.sum, s.aver);
  }

}

ㄴ GRASP 패턴: Information Expert  =>  정보를 갖고 있는  클래스가 그 정보를 다룸
    ㄴ createScore()를 Score 클래스로 이동

=> createScore() -> create() 으로 메서드 이름 변경

 

step11

app.java

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
// 3) 배열 사용
// 4) 클래스를 이용하여 데이터 타입 정의(중첩클래스; 로컬 클래스)
// 5) 출력 기능을 별도의 메서드로 분리(중첩클래스; 스태틱 중첩 클래스) 
// 6) 합계 및 평균을 계산하는 기능을 메서드로 분리
// 7) GRASP 패턴: Information Expert(정보를 갖고 있는 클래스가 그 정보를 다룬다.)
// 8) 인스턴스 메서드 도입
// 9) 객체 생성이 번거롭고 복잡한 경우 메서드로 분리하는 것이 낫다.(디자인패턴; 팩토리 메서드)
// 10) GRASP 패턴: Information Expert
// 11) 생성자 도입
public class App {

  static class Score {
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float aver;

    // 생성자: 인스턴스를 생성한 직후 호출하는 메서드
    Score(String name, int kor, int eng, int math) {
      this.name = name;
      this.kor = kor;
      this.eng = eng;
      this.math = math;
      this.compute();
    }

    void compute() {
      this.sum = this.kor + this.eng + this.math;
      this.aver = this.sum / 3f;
    }

  }

  public static void main(String[] args) {

    final int MAX_SIZE = 10;
    Score[] scores = new Score[MAX_SIZE];
    int length = 0;

    // new Score(문자열, int, int, int);
    // => Score 설계도에 따라 인스턴스를 생성하라
    // => 생성한 후 String, int, int, int 파라미터 값을 받는 생성자를 호출하라.
    // => 이렇게 초기화시킨 인스턴스의 주소를 리턴하라.
    scores[length++] = new Score("홍길동", 100, 100, 100);
    scores[length++] = new Score("임꺽정", 90, 90, 90);
    scores[length++] = new Score("유관순", 80, 80, 80);

    for (int i = 0; i < length; i++) {
      printScore(scores[i]);
    }

  }

  static void printScore(Score s) {
    System.out.printf("%s: 합계=%d, 평균=%.1f\n",
        s.name, s.sum, s.aver);
  }

}

ㄴ 생성자 도입

    ㄴ 생성자 : 인스턴스를 생성한 직후 호출하는 메서드

=> score.create -> new Score

=> new Score(문자열, int, int, int);

=> Score 설계도에 따라 인스턴스를 생성하라
=> 생성한 후 String, int, int, int 파라미터 값을 받는 생성자를 호출하라 -> Score(String name, int kor, int eng, int math)
=> 이렇게 초기화시킨 인스턴스의 주소를 리턴하라

 

step12

app.java

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
// 3) 배열 사용
// 4) 클래스를 이용하여 데이터 타입 정의(중첩클래스; 로컬 클래스)
// 5) 출력 기능을 별도의 메서드로 분리(중첩클래스; 스태틱 중첩 클래스) 
// 6) 합계 및 평균을 계산하는 기능을 메서드로 분리
// 7) GRASP 패턴: Information Expert(정보를 갖고 있는 클래스가 그 정보를 다룬다.)
// 8) 인스턴스 메서드 도입
// 9) 객체 생성이 번거롭고 복잡한 경우 메서드로 분리하는 것이 낫다.(디자인패턴; 팩토리 메서드)
// 10) GRASP 패턴: Information Expert
// 11) 생성자 도입
// 12) 클래스를 유지보수 하기 쉽게 별도 소스 파일로 분리
public class App {

  public static void main(String[] args) {

    final int MAX_SIZE = 10;
    Score[] scores = new Score[MAX_SIZE];
    int length = 0;

    // new Score(문자열, int, int, int);
    // => Score 설계도에 따라 인스턴스를 생성하라
    // => 생성한 후 String, int, int, int 파라미터 값을 받는 생성자를 호출하라.
    // => 이렇게 초기화시킨 인스턴스의 주소를 리턴하라.
    scores[length++] = new Score("홍길동", 100, 100, 100);
    scores[length++] = new Score("임꺽정", 90, 90, 90);
    scores[length++] = new Score("유관순", 80, 80, 80);

    for (int i = 0; i < length; i++) {
      printScore(scores[i]);
    }

  }

  static void printScore(Score s) {
    System.out.printf("%s: 합계=%d, 평균=%.1f\n",
        s.name, s.sum, s.aver);
  }

}

 ㄴ 클래스를 유지보수 하기 쉽게 별도 소스 파일로 분리 => Score.java 생성하여 Score 클래스 이동

 

Score.java

class Score {
  String name;
  int kor;
  int eng;
  int math;
  int sum;
  float aver;

  Score(String name, int kor, int eng, int math) {
    this.name = name;
    this.kor = kor;
    this.eng = eng;
    this.math = math;
    this.compute();
  }

  void compute() {
    this.sum = this.kor + this.eng + this.math;
    this.aver = this.sum / 3f;
  }

}

ㄴ static 제거

 

step13

app.java

import bitcamp.test.step13.vo.Score;

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
// 3) 배열 사용
// 4) 클래스를 이용하여 데이터 타입 정의(중첩클래스; 로컬 클래스)
// 5) 출력 기능을 별도의 메서드로 분리(중첩클래스; 스태틱 중첩 클래스) 
// 6) 합계 및 평균을 계산하는 기능을 메서드로 분리
// 7) GRASP 패턴: Information Expert(정보를 갖고 있는 클래스가 그 정보를 다룬다.)
// 8) 인스턴스 메서드 도입
// 9) 객체 생성이 번거롭고 복잡한 경우 메서드로 분리하는 것이 낫다.(디자인패턴; 팩토리 메서드)
// 10) GRASP 패턴: Information Expert
// 11) 생성자 도입
// 12) 클래스를 유지보수 하기 쉽게 별도 소스 파일로 분리
// 13) 클래스를 유지보수 하기 쉽게 패키지로 분류: import, public
//     => 여러 곳에서 사용할 클래스라면 다른 클래스에 안에 두지 말고 패키지의 멤버 클래스로 둬라
//     클래스를 역할에 따라 패키지로 분류
//       => 클래스가 많을 경우 유지보수하기 쉽도록 적절한 패키지로 분산 배치
//       => 데이터 타입의 역할을 하는 클래스의 경우
//          보통 domain, vo(value object), dto(data transfer object) 라는 이름을 가진
//          패키지에 분류함
//       => 패키지가 다르면 modifier 옵션에 따라 접근 범위가 달라짐
//     멤버의 접근 범위 설정
//       => public: 모두 공개
//       => protected: 서브 클래스와 같은 패키지의 멤버는 접근 가능
//       => (default): 같은 패키지의 멤버는 접근 가능
//       => private: 접근 불가. 그 멤버가 속한 클래스의 내부에서만 접근 가능.
public class App {

  public static void main(String[] args) {

    final int MAX_SIZE = 10;
    Score[] scores = new Score[MAX_SIZE];
    int length = 0;

    scores[length++] = new Score("홍길동", 100, 100, 100);
    scores[length++] = new Score("임꺽정", 90, 90, 90);
    scores[length++] = new Score("유관순", 80, 80, 80);

    for (int i = 0; i < length; i++) {
      printScore(scores[i]);
    }

  }

  static void printScore(Score s) {
    System.out.printf("%s: 합계=%d, 평균=%.1f\n",
        s.name, s.sum, s.aver);
  }

}

ㄴ 클래스를 유지보수 하기 쉽게 패키지로 분류: import

=> vo 패키지에 있는 Score 를 import 해주기

 

Score.java

public class Score {
  public String name;
  int kor;
  int eng;
  int math;
  public int sum;
  public float aver;

  public Score(String name, int kor, int eng, int math) {
    this.name = name;
    this.kor = kor;
    this.eng = eng;
    this.math = math;
    this.compute();
  }

  void compute() {
    this.sum = this.kor + this.eng + this.math;
    this.aver = this.sum / 3f;
  }

}

ㄴ 클래스를 유지보수 하기 쉽게 패키지로 분류: public

ㄴ vo 라는 패키지를 생성하고 그 안으로 Score.java 이동

ㄴ Score 클래스 public 으로 만들어주기 (패키지가 달라졌으므로)

 

step14

app.java

import bitcamp.test.step14.vo.Score;

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
// 3) 배열 사용
// 4) 클래스를 이용하여 데이터 타입 정의(중첩클래스; 로컬 클래스)
// 5) 출력 기능을 별도의 메서드로 분리(중첩클래스; 스태틱 중첩 클래스) 
// 6) 합계 및 평균을 계산하는 기능을 메서드로 분리
// 7) GRASP 패턴: Information Expert(정보를 갖고 있는 클래스가 그 정보를 다룬다.)
// 8) 인스턴스 메서드 도입
// 9) 객체 생성이 번거롭고 복잡한 경우 메서드로 분리하는 것이 낫다.(디자인패턴; 팩토리 메서드)
// 10) GRASP 패턴: Information Expert
// 11) 생성자 도입
// 12) 클래스를 유지보수 하기 쉽게 별도 소스 파일로 분리
// 13) 클래스를 유지보수 하기 쉽게 패키지로 분류: import, public
// 14) 외부 접근 차단과 값 꺼내기: private, getter
public class App {

  public static void main(String[] args) {

    final int MAX_SIZE = 10;
    Score[] scores = new Score[MAX_SIZE];
    int length = 0;

    // => 이렇게 초기화시킨 인스턴스의 주소를 리턴하라.
    scores[length++] = new Score("홍길동", 100, 100, 100);
    scores[length++] = new Score("임꺽정", 90, 90, 90);
    scores[length++] = new Score("유관순", 80, 80, 80);

    // 변수에 직접 접근 => 국영수 합계를 임의로 조작 가능
    // scores[0].sum = 20000;

    for (int i = 0; i < length; i++) {
      printScore(scores[i]);
    }

  }

  static void printScore(Score s) {
    System.out.printf("%s: 합계=%d, 평균=%.1f\n",
        s.name, s.getSum(), s.getAver());
  }

}

ㄴ 외부 접근 차단과 값 꺼내기: private, getter

ㄴ scores[0].sum = 20000; 처럼 변수에 직접 접근하여 국영수 합계를 임의로 조작 가능

=> sum 을 private 으로 접근 제한 해주기

 

Score.java

public class Score {
  public String name;
  int kor;
  int eng;
  int math;
  private int sum;
  private float aver;

  public Score(String name, int kor, int eng, int math) {
    this.name = name;
    this.kor = kor;
    this.eng = eng;
    this.math = math;
    this.compute();
  }

  void compute() {
    this.sum = this.kor + this.eng + this.math;
    this.aver = this.sum / 3f;
  }

  // getter: private 으로 접근이 막힌 변수의 값을 리턴해주는 메서드
  public int getSum() {
    return this.sum;
  }

  // getter: private 으로 접근이 막힌 변수의 값을 리턴해주는 메서드
  public float getAver() {
    return this.aver;
  }

}

ㄴ sum 과 aver 값을 private 으로 접근 제한 해주기 => 이렇게 하면 값 리턴도 할 수 없기 때문에 getter 생성이 필요

ㄴ getSum(), getAver() 라는 이름의 getter 생성

=> getter : private 으로 접근이 막힌 변수의 값을 리턴해주는 메서드

 

step15

app.java

import bitcamp.test.step15.vo.Score;

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
// 3) 배열 사용
// 4) 클래스를 이용하여 데이터 타입 정의(중첩클래스; 로컬 클래스)
// 5) 출력 기능을 별도의 메서드로 분리(중첩클래스; 스태틱 중첩 클래스) 
// 6) 합계 및 평균을 계산하는 기능을 메서드로 분리
// 7) GRASP 패턴: Information Expert(정보를 갖고 있는 클래스가 그 정보를 다룬다.)
// 8) 인스턴스 메서드 도입
// 9) 객체 생성이 번거롭고 복잡한 경우 메서드로 분리하는 것이 낫다.(디자인패턴; 팩토리 메서드)
// 10) GRASP 패턴: Information Expert
// 11) 생성자 도입
// 12) 클래스를 유지보수 하기 쉽게 별도 소스 파일로 분리
// 13) 클래스를 유지보수 하기 쉽게 패키지로 분류: import, public
// 14) 외부 접근 차단과 값 꺼내기: private, getter
// 15) 프로그래밍의 일관성을 위해 보통 다른 필드에 대해서도 getter를 만들고 사용한다.
public class App {

  public static void main(String[] args) {

    final int MAX_SIZE = 10;
    Score[] scores = new Score[MAX_SIZE];
    int length = 0;

    scores[length++] = new Score("홍길동", 100, 100, 100);
    scores[length++] = new Score("임꺽정", 90, 90, 90);
    scores[length++] = new Score("유관순", 80, 80, 80);

    // 변수에 직접 접근 => 국영수 합계를 임의로 조작 가능
    // scores[0].sum = 20000;

    for (int i = 0; i < length; i++) {
      printScore(scores[i]);
    }

  }

  static void printScore(Score s) {
    System.out.printf("%s: 합계=%d, 평균=%.1f\n",
        s.getName(), s.getSum(), s.getAver());
  }

}

 

Score.java

package bitcamp.test.step15.vo;

public class Score {
  private String name; // 코드의 일관성을 위해
  int kor;
  int eng;
  int math;
  private int sum; // 필요에 의해 private => 외부에서 접근하면 안되기 때문
  private float aver; // 필요에 의해 private => 외부에서 접근하면 안되기 때문

  public Score(String name, int kor, int eng, int math) {
    this.name = name;
    this.kor = kor;
    this.eng = eng;
    this.math = math;
    this.compute();
  }

  void compute() {
    this.sum = this.kor + this.eng + this.math;
    this.aver = this.sum / 3f;
  }

  // getter: private 으로 접근이 막힌 변수의 값을 리턴해주는 메서드
  public int getSum() {
    return this.sum;
  }

  // getter: private 으로 접근이 막힌 변수의 값을 리턴해주는 메서드
  public float getAver() {
    return this.aver;
  }

  public String getName() {
    return this.name;
  }

}

ㄴ 프로그래밍의 일관성을 위해 보통 다른 필드에 대해서도 getter를 만들고 사용

=> name 은 값 리턴이 필요 없지만 name 도 getter 를 만듦

 

step16

app.java

import bitcamp.test.step16.vo.Score;

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
// 3) 배열 사용
// 4) 클래스를 이용하여 데이터 타입 정의(중첩클래스; 로컬 클래스)
// 5) 출력 기능을 별도의 메서드로 분리(중첩클래스; 스태틱 중첩 클래스) 
// 6) 합계 및 평균을 계산하는 기능을 메서드로 분리
// 7) GRASP 패턴: Information Expert(정보를 갖고 있는 클래스가 그 정보를 다룬다.)
// 8) 인스턴스 메서드 도입
// 9) 객체 생성이 번거롭고 복잡한 경우 메서드로 분리하는 것이 낫다.(디자인패턴; 팩토리 메서드)
// 10) GRASP 패턴: Information Expert
// 11) 생성자 도입
// 12) 클래스를 유지보수 하기 쉽게 별도 소스 파일로 분리
// 13) 클래스를 유지보수 하기 쉽게 패키지로 분류: import, public
// 14) 외부 접근 차단과 값 꺼내기: private, getter
// 15) 프로그래밍의 일관성을 위해 보통 다른 필드에 대해서도 getter를 만들고 사용한다.
// 16) 필드의 직접 접근을 막고 setter를 정의하는 이유
public class App {

  public static void main(String[] args) {

    final int MAX_SIZE = 10;
    Score[] scores = new Score[MAX_SIZE];
    int length = 0;

    scores[length++] = new Score("홍길동", 100, 100, 100);
    scores[length++] = new Score("임꺽정", 90, 90, 90);
    scores[length++] = new Score("유관순", 80, 80, 80);

    // 합계와 평균 계산이 끝난 후에 국어 점수를 변경한다면
    // => 국영수 점수와 합계, 평균이 일치하지 않는 문제가 발생한다.
    // 즉, 데이터에 결함이 발생한다.
    // => 국영수 점수를 변경한 후에 compute()를 호출하면 되지 않을까?
    // => 만약 개발자가 compute() 호출하는 것을 잊어버린다면 아무 소용이 없다.
    // => 만약 유효하지 않은 국영수 점수를 입력한다면?
    // => 흠... 이건 도저히 막을 길이 없다.
    scores[0].kor = 7000; // 이렇게 무효한 점수를 입력하는 것을 막을 수 없다.
    scores[0].compute(); // 호출하지 않으면 아무 소용이 없다.

    for (int i = 0; i < length; i++) {
      printScore(scores[i]);
    }

  }

  static void printScore(Score s) {
    System.out.printf("%s: 국어=%d, 영어=%d, 수학=%d, 합계=%d, 평균=%.1f\n",
        s.getName(), s.kor, s.eng, s.math, s.getSum(), s.getAver());
  }

}

ㄴ 필드의 직접 접근을 막고 setter를 정의하는 이유

=> 합계와 평균 계산이 끝난 후에 국어 점수를 변경한다면 국영수 점수와 합계, 평균이 일치하지 않는 문제가 발생하여 데이터에 결함이 발생

=> 국영수 점수를 변경한 후에 compute() 를 호출하면 되지만, 만약 개발자가 compute() 호출하는 것을 잊어버린다면 아무 소용이 없음

=> 만약 유효하지 않은 국영수 점수를 입력한다면 이 또한 막을 수 없음

 

step17

app.java

import bitcamp.test.step17.vo.Score;

// 1) 낱개의 변수 사용
// 2) 낱개의 변수 재사용
// 3) 배열 사용
// 4) 클래스를 이용하여 데이터 타입 정의(중첩클래스; 로컬 클래스)
// 5) 출력 기능을 별도의 메서드로 분리(중첩클래스; 스태틱 중첩 클래스) 
// 6) 합계 및 평균을 계산하는 기능을 메서드로 분리
// 7) GRASP 패턴: Information Expert(정보를 갖고 있는 클래스가 그 정보를 다룬다.)
// 8) 인스턴스 메서드 도입
// 9) 객체 생성이 번거롭고 복잡한 경우 메서드로 분리하는 것이 낫다.(디자인패턴; 팩토리 메서드)
// 10) GRASP 패턴: Information Expert
// 11) 생성자 도입
// 12) 클래스를 유지보수 하기 쉽게 별도 소스 파일로 분리
// 13) 클래스를 유지보수 하기 쉽게 패키지로 분류: import, public
// 14) 외부 접근 차단과 값 꺼내기: private, getter
// 15) 프로그래밍의 일관성을 위해 보통 다른 필드에 대해서도 getter를 만들고 사용한다.
// 16) 필드의 직접 접근을 막고 setter를 정의하는 이유
// 17) 필드의 직접 접근을 막기: 인스턴스 변수에 무효한 값이 저장되지 않게 하기 위해
//      => getter 정의 : 값을 꺼낼 때 사용
//      => setter 정의 : 값을 변경할 때 사용. 단 유효한 값을 저장하도록 통제한다.
public class App {

  public static void main(String[] args) {

    final int MAX_SIZE = 10;
    Score[] scores = new Score[MAX_SIZE];
    int length = 0;

    scores[length++] = new Score("홍길동", 100, 100, 100);
    scores[length++] = new Score("임꺽정", 90, 90, 90);
    scores[length++] = new Score("유관순", 80, 80, 80);

    // scores[0].kor = 7000; // 접근 불가
    scores[0].setKor(70); // setter를 통해서는 값 변경 가능. 단 유효한 값만 가능.
    // scores[0].compute(); // 호출하는 것을 잊어버릴 수 있기 때문에 setter에서 호출한다.

    for (int i = 0; i < length; i++) {
      printScore(scores[i]);
    }

  }

  static void printScore(Score s) {
    System.out.printf("%s: 국어=%d, 영어=%d, 수학=%d, 합계=%d, 평균=%.1f\n",
        s.getName(), s.getKor(), s.getEng(), s.getMath(), s.getSum(), s.getAver());
  }

}

ㄴ 필드의 직접 접근을 막기: 인스턴스 변수에 무효한 값이 저장되지 않게 하기 위해
    ㄴ getter 정의 : 값을 꺼낼 때 사용
    ㄴ setter 정의 : 값을 변경할 때 사용 (단, 유효한 값을 저장하도록 통제함)

 

Score.java

public class Score {

  // 프로그래밍의 일관성을 위해 그냥 막았다.
  private String name; // 코드의 일관성을 위해(통일성)

  // 직접 접근을 허용했을 때, 무효한 값을 저장할 수 있기 때문에
  // private 으로 접근을 막았다.
  private int kor;
  private int eng;
  private int math;
  private int sum; // 필요에 의해 private => 외부에서 접근하면 안되기 때문
  private float aver; // 필요에 의해 private => 외부에서 접근하면 안되기 때문

  public Score(String name, int kor, int eng, int math) {
    this.name = name;
    this.setKor(kor); // 생성자에서도 무효한 값이 들어올 수 있기 때문에
    this.setEng(eng);
    this.setMath(math);
  }

  public void compute() {
    this.sum = this.kor + this.eng + this.math;
    this.aver = this.sum / 3f;
  }

  // getter: private 으로 접근이 막힌 변수의 값을 리턴해주는 메서드
  public int getSum() {
    return this.sum;
  }

  // getter: private 으로 접근이 막힌 변수의 값을 리턴해주는 메서드
  public float getAver() {
    return this.aver;
  }

  public String getName() {
    return this.name;
  }

  public int getKor() {
    return this.kor;
  }

  public void setKor(int kor) {
    if (kor < 0 || kor > 100) {
      return;
    }
    this.kor = kor;
    this.compute();
  }

  public int getEng() {
    return this.eng;
  }

  public void setEng(int eng) {
    if (eng < 0 || eng > 100) {
      return;
    }
    this.eng = eng;
    this.compute();
  }

  public int getMath() {
    return this.math;
  }

  public void setMath(int math) {
    if (math < 0 || math > 100) {
      return;
    }
    this.math = math;
    this.compute();
  }

}

ㄴ 필드의 직접 접근을 막기: 인스턴스 변수에 무효한 값이 저장되지 않게 하기 위해
    ㄴ getter 정의 : 값을 꺼낼 때 사용
    ㄴ setter 정의 : 값을 변경할 때 사용 (단, 유효한 값을 저장하도록 통제함)

ㄴ kor, eng, math => 직접 접근을 허용했을 때, 무효한 값을 저장할 수 있기 때문에 private 으로 접근 제한

ㄴ setter 생성 시 유효한 값을 저장하도록 통제함

ㄴ 생성자에서도 무효한 값이 들어올 수 있기 때문에 setter 설정을 해줌