개발자입니다
[비트캠프] 43일차(9주차3일) - Java(리팩토링: 데이터 타입 정의로써 클래) 본문
[비트캠프] 43일차(9주차3일) - Java(리팩토링: 데이터 타입 정의로써 클래)
끈기JK 2023. 1. 4. 10:35com.eomcs.oop.ex02
com.eomcs.oop.ex02.Exam01xx
① 낱개 변수 사용
② class 문법 : 새 데이터 타입 정의
class Score 분리한다. 이를 User Defined Data Type이라 한다.
③ method 문법 : 중복 코드 추출
출력 코드를 printScore()로 추출한다.
④ 리팩토링 : 1기능 1메서드
sum, aver 계산 부분을 compute() 로 분리한다.
⑤ 리팩토링 : 메서드 이동
compute() 를 실질적으로 사용하는 Score로 이동한다.
compute() 메서드는 Score data 전용 operator라 볼 수 있다.
class 문법 → data를 정의하고 그 데이터를 다루는 operator를 만든다.
⑥ 인스턴스에 더 쉽게 접근하는 법 : 인스턴스 메서드
→ GRASP의 Information Expert
⑦ 패키지 멤버 클래스
nested class → package member class 로 변경
⑧ 클래스를 패키지로 분류(관리 용이)
클래스를 데이터 타입별 분류한다 : domain (= vo = dto)
- Score1의 접근 제어 문법
- public : 완전 공개
- protected : 서브 클래스, 같은 패키지
- (default) : 같은 패키지
- private : 비공개
⑨ 객체 초기화 문법 : 생성자
Score2() 라고 생성자(constructor)를 추가한다.
0) 낱개 변수 사용
public class Exam0100 {
public static void main(String[] args) {
String name;
int kor;
int eng;
int math;
int sum;
float aver;
name = "홍길동";
kor = 100;
eng = 90;
math = 85;
sum = kor + eng + math;
aver = (float) sum / 3;
System.out.printf("%s: %d, %d, %d, %d, %.1f\n", name, kor, eng, math, sum, aver);
name = "임꺽정";
kor = 90;
eng = 80;
math = 75;
sum = kor + eng + math;
aver = (float) sum / 3;
System.out.printf("%s: %d, %d, %d, %d, %.1f\n", name, kor, eng, math, sum, aver);
name = "유관순";
kor = 80;
eng = 70;
math = 65;
sum = kor + eng + math;
aver = (float) sum / 3;
System.out.printf("%s: %d, %d, %d, %d, %.1f\n", name, kor, eng, math, sum, aver);
}
}
1) 성적 데이터를 저장할 사용자 정의 데이터 타입을 만든다.
public class Exam0110 {
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 영역에 생성된다.
// - 변수들이 생성된 메모리의 주소를 레퍼런스(주소 변수)에 저장한다.
Score s1 = new Score();
// - 클래스로 만든 메모리는 레퍼런스를 통해 접근한다.
s1.name = "홍길동";
s1.kor = 100;
s1.eng = 90;
s1.math = 85;
s1.sum = s1.kor + s1.eng + s1.math;
s1.aver = (float) s1.sum / 3;
System.out.printf("%s: %d, %d, %d, %d, %.1f\n",
s1.name, s1.kor, s1.eng, s1.math, s1.sum, s1.aver);
Score s2 = new Score();
s2.name = "임꺽정";
s2.kor = 90;
s2.eng = 80;
s2.math = 75;
s2.sum = s2.kor + s2.eng + s2.math;
s2.aver = (float) s2.sum / 3;
System.out.printf("%s: %d, %d, %d, %d, %.1f\n", s2.name,
s2.kor, s2.eng, s2.math, s2.sum, s2.aver);
Score s3 = new Score();
s3.name = "유관순";
s3.kor = 80;
s3.eng = 70;
s3.math = 65;
s3.sum = s3.kor + s3.eng + s3.math;
s3.aver = (float) s3.sum / 3;
System.out.printf("%s: %d, %d, %d, %d, %.1f\n",
s3.name, s3.kor, s3.eng, s3.math, s3.sum, s3.aver);
}
}
클래스 문법의 용도?
1) 사용자 정의 데이터 타입 만들 때
즉 새로운 구조의 메모리를 설계할 때 사용한다.
2) 메서드를 묶을 때
서로 관련된 기능을 관리하기 쉽게 묶고 싶을 때 사용한다.
2) 리팩토링: 메서드 추출(extract method), static nested class
public class Exam0120 {
// 여러 메서드에서 공유하려면 클래스 멤버로 만들어야 한다.
// - 특히 스태틱 멤버끼리 공유하려면 같은 스태틱 멤버로 만들어야 한다.
static class Score {
String name; // 변수 또는 필드
int kor;
int eng;
int math;
int sum;
float aver;
}
public static void main(String[] args) {
Score s1 = new Score();
s1.name = "홍길동";
s1.kor = 100;
s1.eng = 90;
s1.math = 85;
printScore(s1);
Score s2 = new Score();
s2.name = "임꺽정";
s2.kor = 90;
s2.eng = 80;
s2.math = 75;
printScore(s2);
Score s3 = new Score();
s3.name = "유관순";
s3.kor = 80;
s3.eng = 70;
s3.math = 65;
printScore(s3);
}
static void printScore(Score s) {
s.sum = s.kor + s.eng + s.math;
s.aver = (float) s.sum / 3;
System.out.printf("%s: %d, %d, %d, %d, %.1f\n",
s.name, s.kor, s.eng, s.math, s.sum, s.aver);
}
}
3) 리팩토링: 메서드 추출(extract method) = 한 개의 메서드는 한 개의 기능을 수행해야 한다.
public class Exam0130 {
static class Score {
String name;
int kor;
int eng;
int math;
int sum;
float aver;
}
public static void main(String[] args) {
Score s1 = new Score();
s1.name = "홍길동";
s1.kor = 100;
s1.eng = 90;
s1.math = 85;
compute(s1);
printScore(s1);
Score s2 = new Score();
s2.name = "임꺽정";
s2.kor = 90;
s2.eng = 80;
s2.math = 75;
compute(s2);
printScore(s2);
Score s3 = new Score();
s3.name = "유관순";
s3.kor = 80;
s3.eng = 70;
s3.math = 65;
compute(s3);
printScore(s3);
}
static void printScore(Score s) {
System.out.printf("%s: %d, %d, %d, %d, %.1f\n",
s.name, s.kor, s.eng, s.math, s.sum, s.aver);
}
static void compute(Score s) {
s.sum = s.kor + s.eng + s.math;
s.aver = (float) s.sum / 3;
}
}
4) GRASP(General Responsibility Assignment Software Patterns) 패턴
=> Information Expert: 데이터를 다룰 때는 그 데이터를 갖고 있는 객체에게 묻는다.
리팩토링: 메서드 이동(Move Method)
=> 메서드를 관련된 클래스로 이동시킨다. => 코드의 이해가 쉽다.
public class Exam0140 {
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 = (float) s.sum / 3;
}
// 클래스 메서드
// - static이 붙은 메서드이다.
// - 특정 인스턴스에 대해 사용하는 것이 아니라, 모든 인스턴스에 대해 사용할 수 있다.
// - 특정 인스턴스의 값을 다루고 싶다면 파라미터로 그 인스턴스의 주소를 받아야 한다.
}
public static void main(String[] args) {
Score s1 = new Score();
s1.name = "홍길동";
s1.kor = 100;
s1.eng = 90;
s1.math = 85;
// 다음은 Score의 값을 다루는 연산자가 없을 때의 예이다.
// core.sum = score.kor + score.eng + score.math;
// score.average = score.sum / 3f;
// 사용자 정의 데이터 타입의 값을 연산자를 사용하여 다뤄보자!
Score.compute(s1);
printScore(s1);
Score s2 = new Score();
s2.name = "임꺽정";
s2.kor = 90;
s2.eng = 80;
s2.math = 75;
Score.compute(s2);
printScore(s2);
Score s3 = new Score();
s3.name = "유관순";
s3.kor = 80;
s3.eng = 70;
s3.math = 65;
Score.compute(s3);
printScore(s3);
}
static void printScore(Score s) {
System.out.printf("%s: %d, %d, %d, %d, %.1f\n",
s.name, s.kor, s.eng, s.math, s.sum, s.aver);
}
}
스태틱 메서드와 인스턴스 메서드
static 메서드(클래스 메서드) => Score.compute(s1); 에서 Score는 메서드가 소속된 클래스, s1은 파라미터 이다.
non-static 메서드(인스턴스 메서드) => s1. comput(); 에서 s1은 메서드가 소속된 클래스의 인스턴스 주소이다.
↑ 이는 인스턴스를 보다 쉽게 다루는 메서드 문법이다.
인스턴스 메서드와 this
this: 메서드를 호출할 때 앞쪽에서 넘겨준 인스턴스 주소를 받는 built-in 로컬 변수.
non-static 메서드에만 존재한다.
인스턴스 주소는 this 내장 변수에 자동 저장된다. 인스턴스 주소를 받기 위해 따로 변수를 선언할 필요가 없다.
s1.compute(); 에서 s1의 주소를 compute()에 전달한다.
void compute() {
this.sum = this.kore + this.eng + this.math;
}
5) 인스턴스 메서드: 인스턴스 주소를 받는 더 쉬운 문법
public class Exam0150 {
static class Score {
String name;
int kor;
int eng;
int math;
int sum;
float aver;
// static method ==> instance method
// 클래스 메서드로 연산자를 정의하면,
// - 계산을 수행할 때마다 인스턴스의 주소를 파라미터로 받아야 한다.
// - 매우 번거롭다.
//
// public static void calculate(Score score) {
// score.sum = score.kor + score.eng + score.math;
// score.average = score.sum / 3f;
// }
// - 그래서 자바는 "인스턴스 메서드"라는 것을 제공한다.
//
// 인스턴스 메서드
// - 메서드가 호출할 때 인스턴스의 주소를 파라미터로 넘기지 않는다.
// - 메서드를 호출할 때(연산자를 사용할 때), 메서드 앞에 인스턴스 주소를 적는다.
// - 이렇게 전달된 인스턴스 주소는 메서드에 내장된 this라는 변수에 자동 복사된다.
// - 그래서 파라미터 대신 this를 사용하면 된다.
// - 인스턴스 메서드는 static을 붙이지 않는다.
void compute() {
// 인스턴스 메서드를 호출할 때 넘겨준 인스턴스를 주소는
// this 라면 내장 변수(built-in)에 보관된다.
this.sum = this.kor + this.eng + this.math;
this.aver = (float) this.sum / 3;
}
}
public static void main(String[] args) {
Score s1 = new Score();
s1.name = "홍길동";
s1.kor = 100;
s1.eng = 90;
s1.math = 85;
// 다음은 Score의 값을 다루기 위해 non-instance 메서드를 호출하는 예이다.
// => non-instance 메서드 = static 메서드 = 클래스 메서드
//
// Score.calculate(score);
//
// 클래스 메서드를 사용할 때 마다 매번 인스턴스의 주소를 파라미터로 넘겨줘야 했다.
// 그러나 인스턴스 메서드를 사용하면 인스턴스 주소를 넘기기가 더 편하다.
// 메서드 호출 앞에다 둔다.
// 소스 코드의 목적을 이해하는데 더 직관적이다.
s1.compute(); // 마치 변수 뒤에 연산자를 놓는 i++ 의 예와 비슷하다.
printScore(s1);
Score s2 = new Score();
s2.name = "임꺽정";
s2.kor = 90;
s2.eng = 80;
s2.math = 75;
s2.compute();
printScore(s2);
Score s3 = new Score();
s3.name = "유관순";
s3.kor = 80;
s3.eng = 70;
s3.math = 65;
s3.compute();
printScore(s3);
}
static void printScore(Score s) {
System.out.printf("%s: %d, %d, %d, %d, %.1f\n",
s.name, s.kor, s.eng, s.math, s.sum, s.aver);
}
}
6) 패키지 멤버 클래스:
=> 여러 곳에서 사용할 클래스라면 다른 클래스에 안에 두지 말고 패키지의 멤버 클래스로 둬라!
public class Exam0160 {
public static void main(String[] args) {
Score s1 = new Score();
s1.name = "홍길동";
s1.kor = 100;
s1.eng = 90;
s1.math = 85;
s1.compute();
printScore(s1);
Score s2 = new Score();
s2.name = "임꺽정";
s2.kor = 90;
s2.eng = 80;
s2.math = 75;
s2.compute();
printScore(s2);
Score s3 = new Score();
s3.name = "유관순";
s3.kor = 80;
s3.eng = 70;
s3.math = 65;
s3.compute();
printScore(s3);
}
static void printScore(Score s) {
System.out.printf("%s: %d, %d, %d, %d, %.1f\n",
s.name, s.kor, s.eng, s.math, s.sum, s.aver);
}
}
// 다른 패키지에서도 사용할 수 있도록 public 으로 공개한다.
public 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 = (float) this.sum / 3;
}
}
7) 클래스를 역할에 따라 패키지로 분류
=> 클래스가 많을 경우 유지보수하기 쉽도록 적절한 패키지로 분산 배치한다.
=> 데이터 타입의 역할을 하는 클래스의 경우
보통 domain, vo(value object), dto(data transfer object) 라는 이름을 가진 패키지에 분류한다.
=> 패키지가 다르면 modifier 옵션에 따라 접근 범위가 달라진다.
멤버의 접근 범위 설정
=> public: 모두 공개
=> protected: 서브 클래스와 같은 패키지의 멤버는 접근 가능
=> (default): 같은 패키지의 멤버는 접근 가능
=> private: 접근 불가! 그 멤버가 속한 클래스의 내부에서만 접근 가능
package com.eomcs.oop.ex02;
import com.eomcs.oop.ex02.domain.Score1;
public class Exam0170 {
public static void main(String[] args) {
Score1 s1 = new Score1();
s1.name = "홍길동";
s1.kor = 100;
s1.eng = 90;
s1.math = 85;
s1.compute();
printScore(s1);
Score1 s2 = new Score1();
s2.name = "임꺽정";
s2.kor = 90;
s2.eng = 80;
s2.math = 75;
s2.compute();
printScore(s2);
Score1 s3 = new Score1();
s3.name = "유관순";
s3.kor = 80;
s3.eng = 70;
s3.math = 65;
s3.compute();
printScore(s3);
}
static void printScore(Score1 s) {
System.out.printf("%s: %d, %d, %d, %d, %.1f\n",
s.name, s.kor, s.eng, s.math, s.sum, s.aver);
}
}
package com.eomcs.oop.ex02.domain;
// 다른 패키지에서도 이 클래스를 사용할 수 있도록 하려면 public 으로 공개해야 한다.
public class Score1 {
// 다른 패키지에서 이 설계도에 따라 만든 변수에 접근할 수 있도록 하려면
// 접근 범위를 public으로 공개해야 한다.
public String name;
public int kor;
public int eng;
public int math;
public int sum;
public float aver;
// 다른 패키지에서 메서드를 사용할 수 있도록 하려면 public 으로 공개해야 한다.
public void compute() {
this.sum = this.kor + this.eng + this.math;
this.aver = (float) this.sum / 3;
}
}
8) 생성자 도입: 인스턴스를 생성할 때 값을 초기화시키는 특별한 메서드
package com.eomcs.oop.ex02;
import com.eomcs.oop.ex02.domain.Score2;
public class Exam0180 {
public static void main(String[] args) {
Score2 s1 = new Score2("홍길동", 100, 90, 85);
printScore(s1);
Score2 s2 = new Score2("임꺽정", 90, 80, 75);
printScore(s2);
Score2 s3 = new Score2("유관순", 80, 70, 65);
printScore(s3);
}
static void printScore(Score2 s) {
System.out.printf("%s: %d, %d, %d, %d, %.1f\n",
s.name, s.kor, s.eng, s.math, s.sum, s.aver);
}
}
package com.eomcs.oop.ex02.domain;
public class Score2 {
public String name;
public int kor;
public int eng;
public int math;
public int sum;
public float aver;
// new 연산자를 이용하여 인스턴스를 만들 때 자동으로 호출되는 특별한 문법의 메서드
// => 생성자(constructor)
// - 메서드명은 클래스 이름과 같아야 한다.
// - 리턴 타입은 없다.
// - 오직 new 명령을 실행할 때 호출할 수 있다. 나중에 따로 호출할 수 없다.
public Score2(String n, int k, int e, int m) {
this.name = n;
this.kor = k;
this.eng = e;
this.math = m;
this.compute();
}
public void compute() {
this.sum = this.kor + this.eng + this.math;
this.aver = (float) this.sum / 3;
}
}
Score2.class를 Eclipse의 Navigator로 보면 this 변수가 정의되어 있다. 인스턴스 메서드는 this가 내장된다.
Local variable table:
[pc: 0, pc: 30] local: this index: 0 type: com.eomcs.oop.ex02.domain.Score2
[pc: 0, pc: 30] local: n index: 1 type: java.lang.String
[pc: 0, pc: 30] local: k index: 2 type: int
[pc: 0, pc: 30] local: e index: 3 type: int
[pc: 0, pc: 30] local: m index: 4 type: int
생성자
생성자는 클래스 이름과 같아야 한다. 리턴타입이 없다.
생성자 사용 전/후
① 사용 전
인스턴스 생성 후 인스턴스 필드에 개별 접근하여 값 할당한다.
② 사용 후
인스턴스 생성시 생성자 이용해 값 할당한다. 인스턴스 생성 후 즉시 생성자 자동호출된다.
Score 설계도에 따라 인스턴스 생성 → 생성자 호출
Help > Eclipse Marketplace > "code together" 검색 후 설치
조언
*초보자로써 습관을 잘 들이는 방법은 처음에 안에서 클래스 정의하고 밖에서 쓰고싶으면 밖에 끄집어 내라. 이렇게 하다보면 밖에 클래스 정의하는 이유를 이해할 수 있다.
과제
/
'네이버클라우드 AIaaS 개발자 양성과정 1기 > Java' 카테고리의 다른 글
[Java] 예제 소스 정리 - 변수 종류, 인스턴스 메서드, 스태틱 메서드, 생성자, 초기화 블록 (0) | 2023.01.05 |
---|---|
[비트캠프] 44일차(9주차4일) - Java(리팩토링: 메서드 묶음으로써 클래스), myapp-07~08 (0) | 2023.01.04 |
[Java] 예제 소스 정리 - 클래스(인스턴스, public, default, 중첩 클래스) (0) | 2023.01.03 |
[비트캠프] 42일차(9주차2일) - Java(클래스) (0) | 2023.01.03 |
[비트캠프] 41일차(9주차1일) - Java(메서드) (0) | 2023.01.02 |