스터디를 진행하다 원시값을 포장하고, 일급 컬렉션을 사용해야할 것 같다는 피드백을 받았다. 원시값 포장, 일급 컬렉션... 들어는 봤는데 정확히 그게 뭐지? 객체지향에 관련하여 이런 데에서 기초가 부족함을 다시 한 번 느낀다. 내가 이해한 대로 정리해보자!
객체지향 체조 원칙에 모든 원시값과 문자열을 포장하라
가 있다.
여기서 원시값을 포장한다라는 게 무슨 뜻일까?
원시 타입(Primitive Type)의 변수를 이야기하는데,
원시타입은 정수, 실수, 문자, 논리 리터럴등의 실제 데이터 값을 저장하는 타입을 말한다. boolean, char, int 등
( String
은 원시자료형처럼 쓰이는 참조 타입
)
나이age 속성을 그냥 원시타입int의 멤버변수로 선언했다.
public class Student {
private int age;
public Student(int age) {
this.age = age;
}
}
이 경우 age에 대해 validate를 해야한다고 했을 때 이 Student 클래스에서 유효성 검사를 해야한다.
만약 속성이 age 이외에 이름, 주소, 성적 등등 늘어난다고 했을 때 각 속성별 validate 관리가 어렵다.
이렇게 되면 Student 클래스는 Student 관리 역할만 하는 게 아니라, 각 속성별 관리를 해줘야하는, 역할 분담이 애매해지는 문제가 발생한다.
나이age 속성을 그냥 원시타입int의 멤버변수로 선언하지 않고,
멤버변수를 자신의 속성(int age) 하나만 갖는 Age 객체로 일급 컬렉션을 만들어줬다.
그렇게 되면, 나이에 대한 유효성 검사도 Student 클래스가 아닌 Age 클래스에 위임해줄 수 있다.
역할 분담이 명확하게 되었다고 할 수 있다.
public class Student {
private Age age;
public Student(string age) {
this.age = new Age(age);
}
}
public class Age {
private int age;
public Age(String input) {
this.age = Integer.parseInt(input);
if(age < 0) {
throw new RuntimeException("오류: 0보다 작은 나이");
}
}
}
간단히 말해, 어떤 객체에 멤버 변수가 하나 밖에 없는 상태
를 이야기한다.
List<String, String> studentList = new arrayList<>();
studentList.add("a");
studentList.add("b");
studentList.add("c");
위 코드를 아래처럼 Wrapping하는 것을 말한다.
public class Students {
private List<String> studentList;
public Fruits(List<String> studentList) {
this.studentList = studentList;
}
}
Collection(list, map 등)을 Wrapping해서, 다른 멤버 변수가 없는 상태가 되는 걸 이야기한다.
// 내 이해를 돕기 위해 적기...
아아 student들이 여러명 있는 list<Student>로 관리할 수도 있지만 그렇게하지 않고 그 자체의 객체를 만든 것을 말하는 건가?
Students의 생성자 using Field느낌으로 List<String> studentList를 그 객체의 유일한 멤버변수(= 나자신)으로 쓰는 것을 말하는 느낌이다!
비지니스에 종속적인 자료구조
Collection의 불변성을 보장
상태와 행위를 한 곳에서 관리
이름이 있는 컬렉션
이 네가지가 장점으로 꼽히는데 하나씩 이해해봐야겠다.
우선 이해하기로는, 객체지향, 리팩토링의 문제가 가장 많이 언급되는 것 같다.
이름 길이 검증, 중복 검증 등의 로직을 생성자에 넣음으로써 Student의 list에 관련된 로직은 Students에 묶는다.가 된다.
public class Students {
private List<Student> studentList;
public Fruits(List<Student> studentList) {
validateStudentName(studentList);
validateDuplicateStudent(studentList);
this.studentList = studentList;
}
...
}
Students 즉, studentList의 재할당은 불가능하다. getter, setter가 따로 없이 내부 값의 변경이 불가능하게 되어있는 특징을 보인다.
final은 재할당(new ~)만 안되지 put, set 다 가능한다.
Students에 관련해서 처리할 때 여러 학교의 studentList의 평균 점수를 내려고 한다면 여러개의, 아무곳에나 평균 점수 구하기 메소드를 만드는 게 아니라 해당 메소드를 일급 컬렉션인 Student 안에 만들어 호출해서 쓰면 Students라는 상태와 students의 평균 점수를 구하는 행위를 한 곳에서 관리할 수 있다.
또 위의 validateDuplicateStudent
같은 검증을 예를들어 메인로직에서 하고, valid 로직에서 또 하고 이렇게 중복되지 않게 일급컬렉션 내에만 존재시켜서 실행시키게 할 수 있다.
List<Student> middleSchool = new ArrayList<>();
List<Student> highSchool = new ArrayList<>();
위 방식을 아래 처럼 관리했을 때,
MiddleSchool middleSchool = new MiddleSchool();
HighSchool highSchool = new HighSchool<>();
이렇게 이름을 만들어 일급컬렉션을 사용하면 다른 사람이 봤을 때에도 위 변수들을 이해하기가 쉬워진다.
사실 원시값을 포장한다 = 속성이 여러개이면 객체화해서 쓴다. 라고 이해했는데 절반만, 아니 어떻게 보면 잘못 이해하고 있었다고 볼 수 있다. 당장 풀어야하는 문제들에 치여 대충 이해하고 넘어갔던 것 같은데, 시간 내어서 찾아보고 정리하니 이제서야 제대로 이해가 가고, 또 어떻게 코드를 개선할 수 있을지 감이 잡힌다.
객체지향이 뭔지 계속해서 알아가는 과정에 있는데 어떤 걸 찾아봐야하나 하는 생각이 들었었다. 돌아보니 객체지향 체조 원칙부터, 그냥 아 저 원칙들을 따라야겠다가 아니라 하나 하나 원칙들이 뭔지 제대로 알고, 왜 써야하는지를 쫓다보면 자연스레 객체지향에 가까워질 수 있을 것 같다는 생각이 들었다! 새로운 공부 방향 겟!