728x90

2주차

주차마다 원래 관리를 해주어야 하는 스터디 이지만 이미 끝났으므로 공부하면 바로 포스팅을 할 계획이다.

  • 프리미티브 타입 종류와 값의 범위 그리고 기본 값
  • 프리미티브 타입과 레퍼런스 타입
  • 리터럴
  • 변수 선언 및 초기화하는 방법
  • 변수의 스코프와 라이프타임
  • 타입 변환, 캐스팅 그리고 타입 프로모션
  • 1차 및 2차 배열 선언하기
  • 타입 추론, var

primitive 타입 종류와 값의 범위 그리고 기본 값

자바의 변수 타입에는 primitive와 reference 타입 두가지가 있다.
프리미티브 타입의 경우 정수, 실수, 그리고 논리값 으로 이루어져있다.

이 변수의 타입들의 범위는 조금만 생각해보면 왜 저렇게 정해져있는지 알 수 있다.
연관성은 바로 이진수의 제곱 의 범위 이다.

1bit 당 가질수 있는 경우의 수는 2개이다. 0, 1 이렇게 말이다.
그러니까 비트가 늘어날 때마다 자연스럽게 제곱범위로 늘어나는 것이다.
근데 이것을 반으로 갈라서 음수부분, 양수 이렇게 나누기 때문에
범위는 아래 표와 같다. 근데 반이면 반이지 절대값으로 봤을때 1이 차이가 나는데
그 이유는 0 때문에 그렇다.

타입 설명 사이즈 값의 범위 기본값
byte 부호 있는 정숫값 8bit -128 ~ 127 0
short 부호 있는 정숫값 16bit -32768 ~ 32767 0
int 부호 있는 정숫값 32bit -2147483648~2147483647 0
long 부호 있는 정숫값 64bit -2의63승 ~ +2의63승-1 0L
char 유니코드 문자 16bit '\u0000' ~ '\uffff'/ 0~65535 '\u0000'
float IEEE754 부동소수점수 32bit (3.4 X 10-38) ~ (3.4 X 1038)의 근사값 0.0d
double IEEE754 부동소수점수 64bit (1.7 X 10-308) ~ (1.7 X 10308)의 근사값 0.0d
boolean 논리값 1bit true, false false

프리미티브 타입과 레퍼런스 타입

프리미티브 타입은 stack에 적재가 된다.

반대로 레퍼런스 타입은 Heap내에 저장이 된다.

그래서 참조 타입 변수의 주소값에는 직접적인 값이 아니라 Heap 영역에 할당된 주소가 저장이 된다.

참조형 타입에는 클래스(Class) 안에 문자열(String) 포함, 배열(Array), 열거형(Enumeration), interface 가 있다.

int a = 10;
double b = 19.2;
Car c = new Car("제네시스", "gv80");

image
{: .text-center}

이렇게 저장이 된다고 보면 된다.

참조형 타입에는 빈 객체를 의미하는 null이 존재한다.
예를 들어 객체나 배열을 Null 값으로 받으면 NullPointException이 발생하므로 변수값을 넣어야 한다.

Literal

리터럴은 변수나 상수에 저장되는 값 그 자체이다.

위에서 보면 10이나 19.2가 그에 해당한다.

고정된 값을 표현하는 리터럴은 정수, 실수, 문자, boolean, 문자열 을 표현하는 리터럴 등이 있다.

정수형 리터럴

정수를 표현하는 리터럴은 이렇게 구성되어 있다.

int a = 10;       //10진수 리터럴
int a = 012;      //8진수 리터럴
int a = 0x1a;     //16진수 리터럴
int a = 0b110101; //2진수 리터럴

실수 리터럴

실수 타입의 리터럴은 기본적으로 double 타입이고, float 타입으로 표현하려면 f를 명시적으로 붙여야한다.

double b = 1.1;
double b = 1E-1;
float b = 1.1f; 

문자 리터럴

문자 리터럴은 작은 따옴표 안에 할당할 수 있다.

char a = 'A';

이스케이프 문자열

그리고 이스케이프 문자열을 사용할 수 있다.

이스케이프 문자
{: .text-center}

참조타입인데도 불구하고 리터럴을 지원하는 문자열이다.

문자열 타입은 리터럴 을 지원하는데, 리터럴 방식으로 String 에 값을 주면 Heap 영역에서 String constant pool 이라는 특수한 영역에 값이 저장된다.

동일한 값을 쓰는 경우에 다른 일반적인 레퍼런스타입 처럼 Heap 에 또 올라가지 않고, String constant pool 에 존재하는 값을 참조하는 방식으로 작동한다.

String s1 = "java";
String s2 = "java";

이렇게 하면 둘의 주소는 같다.

스크린샷 2021-08-14 오전 11 37 52

스크린샷 2021-08-14 오전 11 38 11

그래서 위와같이 주소값은 같아지게 된다.

변수 선언 및 초기화 방법

저번 포스팅에서 봤듯이 클래스의 필드에선 자동으로 선언만 해줘도 초기화가 기본값으로 진행이 된다.

선언을 하는 순간 이 변수를 저장할거야 저장공간을 할당해줘 라고 해석된다.

메소드 내에서는 선언만하고 초기화를 안해준다면

java: variable a might not have been initialized

에러가 발생한다.

그래서 선언만 해주고 진행을 해 나가다가 무조건적으로 결국엔 값을 할당해서 초기화를 시켜주어야 한다는 것이다.

선언과 초기화

선언과 초기화를 같이 진행할 수도 있다.

String s;
s = "java";

String s = "java";

두개 다 사용이 가능한 방법

변수의 스코프와 라이프타입

scope

변수의 스코프는 해당 변수를 사용할 수 있는 영역 범위를 뜻한다.
변수의 경우 scope 에 따라 Instance 변수, class 변수, Local 변수 로 나눌 수 있다.

lifetime

해당 변수가 메모리에 언제까지 살아있는지를 의미한다.

인스턴스 변수

인스턴스 변수는 클래스안에서 생성된 변수이며 필드라고 부른다.

  • scope: static 메서드를 제외한 전체
  • lifetime: 해당 클래스를 인스턴스화 한 객체가 메모리에서 제거될 때까지

클래스 변수

class 변수는 클래스 내에 필드에 생성이 되지만 static 키워드가 붙어있는 변수

  • scope: 클래스 전체
  • lifetime: 프로그램 종료시

로컬 변수

Local 변수는 지역변수라고도 부르며 위의 두 변수를 제외한 모든 변수가 이것에 해당된다.

  • scope: 변수가 선언된 메서드 내부
  • lifetime: 프로그램이 해당 메서드를 제어하는 동안

타입 변환, 캐스팅 그리고 타입 프로모션

타입 변환

자료형이 자기 자신보다 큰 자료형에 넣게되면 그건 타입 변환이 필요없고 자동 캐스팅으로 변환이 된다.

하지만 큰 자료형이 작은 자료형으로 가려면 타입 변환이 필요하다.

int a = 1;
byte b = (byte)a; //변형이 오지 않음

만약 작은 자료형의 데이터 범위를 넘어선 변수가 타입 변환을 한다면?
데이터의 손실이나 변형이 올 수 있다.

int a = 10000;
byte b = (byte)a; //byte 최대 범위를 넘었기 때문에 변형이 생긴다.

타입 프로모션

타입 캐스팅과 반대로 크기가 더 작은 자료형을 더 큰 자료형에 대입하는 것을 의미한다. 예를 들어, byte타입의 데이터를 int타입에 대입하는 경우이다.

이 경우에는 데이터 손실이나, 변형이 오지 않음으로 캐스팅할 때 처럼 명시적으로 적지 않아도 자동으로 변환이 가능하다.

그냥 캐스팅과 프로모션은 같이 생각해야 안 헷갈릴 것 같다 구분하는 필요를 안둬도 될것 같은? 느낌

이 타입 변환들은 primitive 타입에서만 사용되는게 아니라 참조 타입에서도 가능하다.
부모클래스로의 타입변환은 자동적으로 가능하지만 자식클래스로의 타입변환은 타입캐스팅이 필요하다. 하위 클래스는 상위 클래스의 필드나 메소드를 물려받음으로 하위 타입의 객체를 상위 타입으로 바꾼다고 해서 데이터의 손실이나 변형이 일어나진 않으니까 말이다.

1차 및 2차 배열 선언하기

배열은 하나의 동일 타입을 여러개 담을 수 있다.

[]로 감싸진 0으로 부터 시작하는 index가 배열의 길이나 순서를 나타낸다.

배열도 type에 따라 기본 값(default value)로 채워진다. (명시적으로 값을 넣어주지 않는다면)

참조 타입의 경우 Null

1차원 배열

int[] a = new int[5]; //[0,0,0,0,0] 로 초기화
int[] b = {1,2,3,4,5}; // 명시적으로 값을 넣어주면 이대로 초기화가 된다.

int[] c = new int[5];
//값 넣기 이렇게도 가능하다.
c[0] = 1;
c[1] = 2;
c[2] = 3;
c[3] = 4;
c[4] = 5;

1차원 배열은 이렇게 선언하면 된다.

2차원 배열

2차원 배열은 행렬(matrix)를 생각하면 된다.

int[행][렬] = new int[행][렬]

가령 3행 3열의 행렬이 이렇게 있다고 가정하면

1 2 3
4 5 6
7 8 9

자바로 표현되는 선언식은 아래와 같다.

int[][] arr = { {1,2,3}, {4,5,6}, {7,8,9} };

지정된 값 말고 동적으로 추가하는 느낌이라면

int[][] arr = new int[3][3];

for(int i = 0; i < 3; i++) {
    for(int j = 0; j < 3; j++) {
        arr[i][j] = 넣을 변수값
    }
}

하면된다.

타입추론, var

타입 추론이란 데이터 타입을 코드에 명시하지 않아도, 컴파일 단계에서 컴파일러가 타입을 유추해 정해주는 것을 뜻한다. 1.5버전 부터 추가된 Generic 이나 자바 8에서 추가된 람다에서 타입추론이 사용된다. 그리고 자바10 에서는 이러한 타입추론을 사용하는 var 이라는 Local Variable Type-Inference 가 추가되었다.

아직 많이 사용할지는 잘 모르겠다.

  • foreach
    • 그냥 우리가 리스트 열거할 때 쓰던 for문, for(Person person : personList) 문에서 변수 선언할 때 var로 쓰면 편하다. 이클립스나 인텔리제이나 for문 쓸때, 타입 추론하기엔 열거 타입을 정의할 때까지 타입을 직접 작성하거나, 템플릿을 작성해야 해서 난감할 때를 많이 겪어봤을 것이다. 이를 var 키워드가 순식간에 해결해준다.
List<Person> people = new ArrayList<>();
people.add(new Person("a"));
people.add(new Person("b"));
people.add(new Person("c"));

for (var person : people) {
    //....
}

이렇게 작성하면 IDE 에서는 var 키워드를 Person 클래스로 인식할 수 있는 기회가 주어지게 되고, 컴파일할 때도 var 키워드를 Person 으로 변환하게 될 것이다. 타이핑이 정말 간결해진다. Object 타입으로 미리 단정지을 필요도 없다.

728x90

'Java' 카테고리의 다른 글

SOLID 원칙  (0) 2022.08.06
연산자  (0) 2022.08.06
JVM  (0) 2022.08.06
Effective Java 4장 요약  (0) 2022.08.06
728x90

정리하는 겸 유튜브 백기선님 자바 스터디를 보며 다시 상기시키는 공부를 진행한다.

이미 정식 스터디는 끝났지만 늦게 시작하여 블로그에 따로 기록하려고 한다.

📢 1주차

  • JVM이란 무엇인가
  • 컴파일 하는 방법
  • 실행하는 방법
  • 바이트코드란 무엇인가
  • JIT 컴파일러란 무엇이며 어떻게 동작하는지
  • JVM 구성 요소
  • JDK와 JRE의 차이

📌 JVM이란?

JVM이란 Java Virtual Machine(자바 가상 머신) 의 줄임말

일반적인 프로그램은 OS위에서 실행되는데 자바 프로그램 같은 경우에는
OS위의 JVM에서 실행이 된다.

JVM위에서 실행 시키는 이유는 OS에 상관없이 실행시키기 위해서이다.

각기 다른 운영체제에서 실행시키는 이점은 정말 좋다.

📌 JVM의 역할

  • 자바 애플리케이션을 클래스 로더를 통해 읽어 들여 자바 API와 함께 실행하는 것이다.
  • JVM은 자바와 OS사이에서 중개자 역할을 수행하여 OS에 독립적인 플랫폼을 갖게 해준다.
    • OS의 메모리 영역에 직접 접근하지 않고 JVM을 통해 간접적으로 접근한다.
  • JVM은 프로그램 메모리 관리를 알아서 해준다. (GC 자동)

📌 자바 실행과정

  1. 프로그램이 실행되면 JVM은 OS로부터 이 프로그램이 필요로 하는 메모리를 할당받는다. 이 메모리를 할당받고 용도에 맞게 여러 영역으로 나누어 관리한다.
  2. 자바 컴파일러(javac)가 자바 소스코드(.java)를 읽어들여 자바 바이트코드(.class)로 변환시킨다.
  3. 클래스 로더를 통해 class파일들을 JVM으로 로딩한다.
  4. 로딩된 class 파일들은 Execution engine을 통해 해석된다.
  5. 해석된 바이트코드(.class)는 Runtime Data Areas에 배치되고 수행이 이뤄지게 되는데 여기서 가비지 컬렉터 같은 관리 작업도 수행한다.

image
{: .text-center}

📌 자바 바이트코드

자바 바이트코드(.class)란 JVM이 이해할 수 있는 언어로 변환된 자바 소스 코드를 의미한다.

자바 컴파일러(javac)에 의해 변환되는 코드의 명령어 크기가 1바이트라서 자바 바이트코드라고 불리고 있다.

📌 JIT 컴파일러란?

JIT(Just-In-Time) 컴파일러는 바이트코드를 CPU로 직접 보낼 수 있는 명령어로 바꾸는 프로그램이다.

일반적으로 컴파일러는 개발자와 최종 사용자를 위한 애플리케이션의 속도를 결정하는데 있어 핵심이다.

JIT 컴파일러는 성능 최적화를 위해 애플리케이션 런타임을 향상시킬 수 있다.

다른 컴파일러와는 다르게 JIT 컴파일러의 특성은 프로그램이 시작되고 코드를 컴파일 한 후에 JIT컴파일러가 실행된다.

📌 JVM 구성요소

  • 인터프리터
    • 바이트코드(.class)를 해당 하드웨어의 환경에 맞게 변환을 하여 운영체제에 종속적이지 않은 상태를 만들어줌.
  • 클래스 로더
    • 컴파일러가 SSD 내부에 만든 바이트코드 파일을 읽는다.
  • 실행엔진(JIT 컴파일러 포함)
    • 로더에 존재하는 클래스를 읽으면서 static이 붙은것을 판단한다.
    • static이 붙은 클래스가 없다면 그대로 종료
  • GC(Garbage Collector)
    • 더 이상 사용하지 않는 메모리를 자동으로 회수

📌 JRE, JDK 차이

JRE(Java Runtime Environment) : 컴파일된 자바 프로그램을 실행시킬 수 있는 자바 환경

  • JRE는 JVM이 자바 프로그램을 동작시킬 때 필요한 라이브러리 파일들과 기타 파일들을 가지고 있다.
  • JRE는 JVM의 실행환경을 구현했다고 할 수 있다.
  • 자바 프로그램을 실행 시키기 위해선 JRE를 반드시 설치해야 한다.
  • 자바 프로그래밍 을 하기 위해선 JDK가 필요하다.

JDK(Java Development Kit) : 자바 프로그래밍에 필요한 컴파일러 등등 포함

  • 개발을 위해 필요한 도구들을 포함한다.
  • JDK를 설치하면 JRE도 같이 설치가 된다.
  • JDK = JRE + @
728x90

'Java' 카테고리의 다른 글

연산자  (0) 2022.08.06
[Java] 데이터타입, 변수, 배열  (0) 2022.08.06
Effective Java 4장 요약  (0) 2022.08.06
TDD Clean Code with Java 12기 3주차  (0) 2022.08.06
728x90

4장 클래스와 인터페이스

이펙티브 자바를 TDD, Clean Code 과정을 수강하면서 들으니 이해가 잘되면서 잘 읽힌다❗

클래스와 멤버의 접근 권한을 최소화

ㅋㅋㅋㅋㅋㅋㅋㅋ 어제 Clean Code 과정에서 뼈저리게 겪었다...

랜덤값 부터 시작해서 어설프게 설계된 컴포넌트와 잘 설계된 컴포넌트의 차이는 클래스 내부 데이터를 각각 다른 외부 컴포넌트로부터 얼마나 추상화 하였는가가 결정 짓는다.

잘 설계되었다면 구현부, API를 깔끔하게 분리할 수 있다.
MVC 패턴만 사용했어 분리하는게 조금 어려웠다. 그런데 진도 나가면서 점점 발전하는것 같다. 😁

이 챕터에서는 정보 은닉(캡슐화) 의 장점에 대해 설명하고 있다.

  • 시스템 개발 속도를 높인다.
    • 여러 컴포넌트를 병렬로 개발 가능하기 때문
  • 시스템 관리 비용을 낮춘다.
    • 다른 컴포넌트로 교체하는 부담이 적어진다.
  • 성능을 높여주진 않지만, 성능 최적화에는 도움을 준다.
    • 다른 컴포넌트에 영향을 주지 않고 해당 컴포넌트만 최적화할 수 있음.
  • 소프트웨어 재사용성을 높인다.
  • 큰 시스템을 제작하는 난이도를 갖춰준다.

접근 제어자(private, public, protected)

접근 제어자를 잘 써야한다.
사실 이제껏 private, public 두개만 사용했지 protected는 활용 빈도가 지극히 낮았던게 나의 코드였다.

여기서 말하는 원칙을 준수하려면 모든 클래스와 멤버의 접근성을 가능한 좁혀야 한다.

접근 범위가 좁은 순서

  • private : 멤버를 선언한 가장 바깥의 클래스에서만 접근 가능
  • package-private: 멤버가 소속된 패키지 안의 모든 클래스에서 접근할 수 있다.
    • 접근 제한자를 명시하지 않았을 때 적용되는 접근 수준(interface는 public)
  • protected: package-private의 접근 범위를 포함하며 상속받는 하위 클래스에서도 접근할 수 있다.
  • public: 그냥 다 접근 가능

근데 나는 여기중에서 package-private를 쓰지않고 도메인을 더 분리하여 일급 컬렉션을 사용하면서 호출하는식으로 관리하는 것이 더 좋을 것 같다는 생각.

결국 public을 많이 사용하면 할수록 점점 코드가 뚱뚱해진다.

🤷‍♂️ 그렇다는건 또 분리를 해야한다는 것 ❗❗❗

접근 제어자 범위를 늘릴 때,
테스트를 하기 위해서 접근 제어자의 범위를 늘려주는 것은 하나의 방법이 될 수 있겠지만
인터페이스를 사용해서 값을 할당하는 strategy 패턴을 고려하는게 좋다고 생각한다.
이것도 뼈저리게 느낀 이번챕터..

public 클래스의 인스턴스 필드는 되도록 public이 아니어야 한다.
public 가변 필드를 갖는 클래스라면 스레드 안전하지 않다. 그 클래스는 이 필드를 통제할 통솔력이 없다. 밖에서도 참조가 가능하기 때문에 불변을 보장할 수도 없다.
상당히 좋지 않은 방식... 그래서 DTO를 사용할 때 다 private로 기본 가져가는것이 이유중 하나일 수 있다.

이 책에서는 상수용public static final 외에는 public 필드를 가져서 안된다고 하는데 내가 생각할때는 이 마저도 남용할 수 있는 우려가 있기 때문에 private로 바꿔서 해당 클래스에서만 사용하게 해야 좋은 것 같다. 사용 빈도가 많다면 Enum을 만들어서 상수를 관리하자.

public 클래스에서는 public 필드 말고 접근제어자 메소드를 사용

이것이 dto에서 쓰는 기본 원칙이다.

class Class {
    public int x;
    public int y;
}

이러면

class Main {
    public static void main(String[] args) {
        Class class = new Class();
        class.x = 1;
    }
}

데이터에 직접 위의 코드처럼 할당이 가능해서 캡슐화의 이점을 사용할 수 없다.
그래서 필드를 private 변경하고 get, set 메서드를 사용한다.

class Class {
    private int x;
    private int y;

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }
}

이렇게 말이다.

불변 필드를 노출하게 되면 원하는 데이터가 정형화되어 나올 수 있는 환경이 파괴되기 때문에 접근제어자를 고민하면서 구현하자❗

728x90

'Java' 카테고리의 다른 글

[Java] 데이터타입, 변수, 배열  (0) 2022.08.06
JVM  (0) 2022.08.06
TDD Clean Code with Java 12기 3주차  (0) 2022.08.06
TDD Clean Code with Java 12기 2주차 피드백  (0) 2022.08.06
728x90

포스팅이 늦었다. 3주차 미션인 사다리도 끝나게 되었다.

로또에서보다 난이도가 많이 올라간 느낌이었다.

리뷰어분이 빡세게 그리고 꼼꼼하게 해주신 덕분에 나 자체도 굉장히 성장한것 같다❗

아래는 깃허브 PR 목록이다.

사다리 1주차

사다리 2주차

사다리 3주차

사다리 4주차

테스트

전체적으로 테스트코드를 고민하다가 한번 로직에 손을 대면 저절로 도메인 위주로 구현을 하게 되었다.

테스트를 항상 생각하면서 그리고 테스트를 실행함으로 인해 로직을 구현해 나가야 하는것이 조금 부족했던 챕터였다.

그래서 중간에 리뷰를 받다가 너무 로직이 답답해 보였다.

읽고있던 이펙티브 자바를 접목시켜서 조금 더 나은 로직으로 개선했다.

로직개선으로 문제를 겪었었는데, 그 문제가 바로 절차지향으로 개발했기 때문에 문제였다.

모든 로직을 한군데에 구현해놓으니까 분리하기도 쉽지않고 어떻게 돌아가는지 명확하게 알 수도 없는 그런 로직이 완성되어 있었다.

이걸 일급 컬렉션으로 포장해주고, 모든 엔티티를 작게 유지한다 라는 조건을 생각하면서

개발하게 되니까 확실히 알기도 쉬워졌고, 유지보수성이 좋게 되었다.

무엇보다 인터페이스 그리고 람다에 대해 공부를 많이 해야겠다고 생각했다.

프로그램이 뭘 하는지 어떻게 해야하는지 생각하는 Out-In방식이 아니라

In-Out방식으로 최소한의 객체에서부터 출발하는 생각을 지속적으로 해야한다.

접근방식

처음에 내가 이 사다리를 놓고 접근한 방식은 다리와 세로 기둥을 같이 넣어서 구현하려고 했던게 문제였다.

그러니까 사다리 라는 큰 틀만 놓고 일차원적으로 생각한 결과가 이렇게 된 것이다.

여기가 갈아 엎은 부분

그래서 결국 생각해낸 것은 이라는 객체가 결국 이동해서 결과를 내주는 것인데

점부터 시작해서 왼쪽 오른쪽을 판단하게끔 로직을 구현하니까 점점 조금씩 큰 컬렉션으로 나가지면서 그에 대한 테스트도 조금씩 늘릴 수가 있게 되었다.

요구사항

참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)
pobi,honux,crong,jk

실행 결과를 입력하세요. (결과는 쉼표(,)로 구분하세요)
꽝,5000,꽝,3000

최대 사다리 높이는 몇 개인가요?
5

사다리 결과

pobi  honux crong   jk
    |-----|     |-----|
    |     |-----|     |
    |-----|     |     |
    |     |-----|     |
    |-----|     |-----|
꽝    5000  꽝    3000

결과를 보고 싶은 사람은?
pobi

실행 결과
꽝

결과를 보고 싶은 사람은?
all

실행 결과
pobi : 꽝
honux : 3000
crong : 꽝
jk : 5000

조건

자바 8의 스트림과 람다를 적용해 프로그래밍한다.

  • 규칙
    • 모든 엔티티를 작게 유지한다.
    • 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.

위 요구사항에 따라 4명의 사람을 위한 5개 높이 사다리를 만들 경우, 프로그램을 실행한 결과는 다음과 같다.

728x90

'Java' 카테고리의 다른 글

JVM  (0) 2022.08.06
Effective Java 4장 요약  (0) 2022.08.06
TDD Clean Code with Java 12기 2주차 피드백  (0) 2022.08.06
TDD Clean Code with Java 12기 2주차  (0) 2022.08.05
728x90

테스트 코드

자동차 경주에 대한 라이브 피드백 시간인데
느낀점이 있어서 포스팅하게 되었다.

테스트 코드 비교할 경우

우리는 항상 getter, setter 메소드를 많이 써왔다.
그래서 나도 습관처럼 객체를 생성하고 비교를 할 경우에 아래와 같이 코드를 작성했었다.

@Test
void create() {
    Position actual = new Position(5);
    assertThat(actual.getPosition).isEqualTo(5);
}

이런식으로 get 메소드를 사용해서 값을 비교를 했는데
이 방식은 잘못되었다기 보다는 get을 사용하지 않고
객체와 객체를 비교하는 방법을 사용하는 것이 오히려 객체지향적 측면에서 좋을 것 같다.

그렇게 Position 클래스에 equals()hashCode() 를 오버라이드 해주고 객체끼리 비교하게끔 만들어준다.

public class Position {
    private Position position = new Position(0);

    public Position(int position) {
        if (position < 0) {
            throw new IllegalArgumentException("음수는 위치 값이 될 수 없음");
        }
        this.position = position;
    }
}
@Test
void create() {
    Position actual = new Position(5);
    assertThat(actual).isEqualTo(new Position(5));
}

이렇게 객체 두개가 같은지를 구현하면 테스트가 성공하게 된다.

이런식으로 어떤 객체를 생성했을때 객체끼리 비교하는 습관을 들이도록 해보자.

문자열과 원자값을 포장해서 쓰는게 객체지향에서 하기 굉장히 좋은 것이니까 지금부터라도 습관 들이자~😄

이제서야 이펙티브 자바 1장이 이해가 되는것 같다. 경험해봐야 이해가 잘되는 이 기분이 좋다.

일급 컬렉션

필드변수를 하나만 두고 사용하는 클래스이다.

일급 콜렉션을 사용하면 계속해서 객체에게 메세지를 보내서 get메소드 대신 객체에게 위임해서 데이터를 조작하게끔 만들어야 한다.

하나씩 객체들을 포장해서 관리하면 테스트 로직을 짜기에 되게 수월하게 작성할 수 있다.

객체에 메시지를 보내라 = 객체가 하게 만들어라 = 객체에게 위임해라

클래스 역할은 작게할수록, 그리고 클래스를 잘게 쪼갠다면? TDD는 쉬워진다

항상 이것들을 생각하면서 코드를 작성하도록 하자 👍텍스트

728x90

'Java' 카테고리의 다른 글

Effective Java 4장 요약  (0) 2022.08.06
TDD Clean Code with Java 12기 3주차  (0) 2022.08.06
TDD Clean Code with Java 12기 2주차  (0) 2022.08.05
[JPA] 객체 지향 쿼리 심화  (0) 2022.08.05
728x90

로또

2주차 미션은 로또 생성기였다.


Step1 - 문자열 덧셈 계산기


Step2 - 로또(자동)


Step3 - 로또(2등)


Step4 - 로또(수동)

프로그래밍 요구사항이 점점 추가되어 조금 더 제한적인 상황에서 조건문을 사용해야 한다.
주차가 늘어가면서 느끼는것이지만, 테스트 주도 개발을 하게 되니까 안하던 방법이라서 손에 익지는 않았다. 그런데 완성되는 테스트를 먼저 구현하다 보니까 오류가 나는 상황에 대해서 더 생각하고 코드를 구현할 수 있게 되는것 같다.

이 과정을 진행하면서 이펙티브 자바도 같이 읽고 있다. 정적 팩토리 메서드는 이제 꼭 쓰게 되는것 같다.😁 꼭 쓰는것은 또 아니라고 생각해야되는데 일단 무분별하게 생성자로 객체를 생성할 수는 없게 만들어 놨다.

클래스

클래스 부분에서 좀 많은 생각을 했었고 이번 과제에서는 if조건문을 추상 클래스와 추상 메소드를 활용해서 조건문을 처리한 로직이 있다.

아직 2주차 미션임에도 불구하고 예전 코드와 좀 많이 달라졌다는게 눈에 보인다.
단순 로직만 구현을 바꾸는 것도 좋겠지만 그 안의 복잡도도 고쳐가면서 코드를 구현해 나가야겠다.

강의를 정말 신청하길 잘했다는 생각이 들고 이 과정을 완주하는것이 목표니까 최선을 다 해보도록 해야겠다.

아래는 2주차 미션의 요구사항이다.

요구사항

기능 요구사항
로또 구입 금액을 입력하면 구입 금액에 해당하는 로또를 발급해야 한다.

로또 1장의 가격은 1000원이다.
구입금액을 입력해 주세요.
14000
14개를 구매했습니다.
[8, 21, 23, 41, 42, 43]
[3, 5, 11, 16, 32, 38]
[7, 11, 16, 35, 36, 44]
[1, 8, 11, 31, 41, 42]
[13, 14, 16, 38, 42, 45]
[7, 11, 30, 40, 42, 43]
[2, 13, 22, 32, 38, 45]
[23, 25, 33, 36, 39, 41]
[1, 3, 5, 14, 22, 45]
[5, 9, 38, 41, 43, 44]
[2, 8, 9, 18, 19, 21]
[13, 14, 18, 21, 23, 35]
[17, 21, 29, 37, 42, 45]
[3, 8, 27, 30, 35, 44]

지난 주 당첨 번호를 입력해 주세요.
1, 2, 3, 4, 5, 6

당첨 통계
---------
3개 일치 (5000원)- 1개
4개 일치 (50000원)- 0개
5개 일치 (1500000원)- 0개
6개 일치 (2000000000원)- 0개
총 수익률은 0.35입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임)

힌트

  • 로또 자동 생성은 Collections.shuffle() 메소드 활용한다.
  • Collections.sort() 메소드를 활용해 정렬 가능하다.
  • ArrayList의 contains() 메소드를 활용하면 어떤 값이 존재하는지 유무를 판단할 수 있다.

프로그래밍 요구사항

  • 모든 기능을 TDD로 구현해 단위 테스트가 존재해야 한다. 단, UI(System.out, System.in) 로직은 제외
  • 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다.
  • UI 로직을 InputView, ResultView와 같은 클래스를 추가해 분리한다.
  • indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다.
    • 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
    • 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메소드)를 분리하면 된다.
  • 함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다.
    • 함수(또는 메소드)가 한 가지 일만 잘 하도록 구현한다.
  • 모든 로직에 단위 테스트를 구현한다. 단, UI(System.out, System.in) 로직은 제외
  • 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다.
  • UI 로직을 InputView, ResultView와 같은 클래스를 추가해 분리한다.
  • 자바 코드 컨벤션을 지키면서 프로그래밍한다.
    • else 예약어를 쓰지 않는다.
    • 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다.
      else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.
728x90

'Java' 카테고리의 다른 글

TDD Clean Code with Java 12기 3주차  (0) 2022.08.06
TDD Clean Code with Java 12기 2주차 피드백  (0) 2022.08.06
[JPA] 객체 지향 쿼리 심화  (0) 2022.08.05
Effective Java 1장  (0) 2022.08.05

+ Recent posts