❐ Description
오늘 (2024.10.29) 스터디원들과 코드리뷰를 진행했다. 코드 리뷰 과정에서 내가
Position을 일급 객체로 사용하면 좋을 것 같습니다!
라고 피드백을 남겼다. 다시 생각해보면 일급 객체랑 VO랑 비슷하다고 생각해서 이렇게 말한 거 같다.
이번 기회에 개념을 명확히 잡고, 다음 피드백에서는 동료들에게 보다 정확한 정보를 바탕으로 피드백을
해야 할 것이다.
❐ First Class (일급 객체)
다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체라고 한다.
- 변수에 할당할 수 있다.
- 함수의 인자로 전달 될 수 있다.
- 함수의 반환 값으로 사용될 수 있다.
그리고 일급 객체에서 사용되는 "객체"라는 단어는 Java에서 생각하는 그런 객체가 아니다!
여기서 "객체"란, 프로그래밍 언어에서 다룰 수 있는 모든 데이터나 값(value)을 의미한다. 다시 말해,
일발적으로 일급 객체라는 개념은 객체지향 언어의 객체 개념에 한정되지 않고, 값 처럼 자유롭게 다룰
수 있는 존재라는 의미로 사용된다.
1. 일급 객체에는 어떤 것들이 있을까?
우리가 흔히 사용하고 있는 String, Integer 또한 일급 객체에 속한다.
#️⃣ Integer
public Integer firstClassInteger() {
// 변수에 할당할 수 있다.
Integer num = 100;
// 함수의 인자로 전달 될 수 있다.
String numToString = String.valueOf(num);
// 함수의 반환 값으로 사용될 수 있다.
return num * 2;
}
Integer의 경우에는 일급 객체가 되기위한 필요조건을 모두 충족한다.
#️⃣ String
public String firstClassString() {
// 변수에 할당할 수 있다.
String source = "12345";
// 함수의 인자로 전달 될 수 있다. & 함수의 반환 값으로 사용될 수 있다.
return String.format("%s", source);
}
String 또한 일급 객체가 되기위한 필요조건을 모두 충족한다.
2. 일급 객체가 아닌것은?
#️⃣ Java의 메서드
Java의 메서드는 직접 변수에 할당하거나 인자로 전달할 수 없다.
#️⃣ Java의 기본자료형 (int, long 등등)
Java에서는 int, double, boolean 등 기본 자료형은 객체로 취급되지 않고, 참조 타입이 아니다.
기본 자료형은 객체와 같은 방식으로 다룰 수 없으므로 일급 객체가 아니다.
하지만 Integer, Double, Boolean과 같은 래퍼 클래스를 사용하면 기본 자료형을 객체처럼 다룰 수 있다.
#️⃣ Java의 클래스 자체
Java에서 클래스는 클래스 자체로 변수에 할당하거나 함수의 인자로 전달할 수 없다.
클래스의 인스턴스를 만들어야, 그제서야 일급 객체로 사용할 수 있다.
#️⃣ Java의 제어문 (if, for, while 등등)
if, for, while 같은 제어문은 변수로 할당하거나 인자로 전달할 수 없다.
제어문은 실행 흐름을 제어하는 조건 구문이므로 일급 객체가 될 수 없다.
❐ 그럼 Lambda 표현식은 ?
그럼 이번엔 Lambda 표현식 일급 객체인지 확인해보자.
public class LambdaPractice {
public static void main(String[] args) {
// 변수에 할당할 수 있다. → bothEven
BiPredicate<Integer, Integer> bothEven = (a, b) -> (a % 2 == 0) && (b % 2 == 0);
// 메서드의 인자로 전달할 수 있다. → bothEven
checkCondition(4, 8, bothEven);
// 메서드의 반환값으로 사용할 수 있다. → isEven(...)
BiPredicate<Integer, Integer> even = isEven(4, 8);
}
public static BiPredicate<Integer, Integer> isEven(int value1, int value2) {
return (a, b) -> (a % 2 == 0) && (b % 2 == 0);
}
public static boolean checkCondition(int a, int b, BiPredicate<Integer, Integer> predicate) {
return predicate.test(a, b);
}
}
일급 객체가 되기 위한 필요 조건을 모두 만족한다.
그런데 stackoverflow에서 이런 글을 봤다.
위 글에 달린 댓글에는 두 가지 주장이 있다.
1. 조건은 충족, 하지만 반환 값은 "함수형 인터페이스의 인스턴스"
람다는 일급 객체의 필요조건을 모두 충족하지만 함수형 인터페이스의 인스턴스로 반환되기 때문에,
순수한 함수가 아닌 객체의 특성을 일부 가진다는 의견이다. 이 의견은 60개의 vote를 받았다.
public class LambdaPractice {
public static void main(String[] args) {
Function<Integer, Integer> function1 = x -> x + 10;
Function<Integer, Integer> function2 = x -> x - 10;
// [필요 조건 모두 충족] 변수 할당, 인자로 전달, 반환값으로 사용
System.out.println(apply(10, function1));
System.out.println(apply(10, function2));
// 객체가 가지고 있는 메소드 사용 (hashCode(), equals())
System.out.println(function1.hashCode());
System.out.println(function2.hashCode());
System.out.println(function1.equals(function2));
}
public static int apply(int x, Function<Integer, Integer> function) {
return function.apply(x);
}
}
위 예제를 보면 일급 객체가 되기 위한 필요조건은 모두 만족하지만, 객체가 가지고 있는 메소드를
사용하는 것을 확인할 수 있다. 따라서 Java의 람다는 완전한 일급객체라고 할 수 없다.
2. 조건은 충족, 그러면 일급 객체지
이 의견을 낸 사람은 조건을 충족하면 일급객체지 뭔 말을 하냐 이런 주장이다.
❐ First Collection(First-class Collection)
일급 컬렉션은 컬렉션 객체를 감싸는 일종의 래퍼 클래스로, 컬렉션과 관련된 로직을 해당 클래스로
모두 캡슐화하여 관리하는 객체이다. 일급 컬렉션을 사용하면 컬렉션의 상태와 컬렉션을 다루는 모든 로직을
하나의 클래스에 집중시킬 수 있어, 코드의 가독성과 유지보수성이 향상된다.
일급 컬렉션의 특징은 다음과 같다.
#️⃣ 오직하나의 멤버 변수
일급 컬렉션은 오직 컬렉션 자체만을 가지고 있어야 한다.
추가적인 속성이나 필드를 가지지 않도록 하여 순수하게 컬렉션과 관련된 로직을 관리한다.
#️⃣ 불변성 보장
일급 컬렉션은 가능한 한 컬렉션을 변경하지 않는 불변 객체로 다루는 것이 좋다.
즉, 컬렉션에 요소를 추가하거나 삭제하는 행위를 외부에서 직접 수행하지 못하게 하고,
필요한 작업을 메서드로 제공하여 관리한다.
#️⃣ 로직의 캡슐화
컬렉션의 모든 조작(ex. add, delete, sorting...) 관련 로직은 일급 컬렉션 내부에서 관리한다.
이로 인해 컬렉션을 사용하는 곳에서 불필요한 중복 코드나 컬렉션 관련 비즈니스 로직이 분산되는
것을 방지할 수 있다.
#️⃣ 일관성 유지
컬렉션을 직접 노출하지 않고, 컬렉션과 관련된 모든 작업이 일급 컬렉션 내부에서 이루어지도록
하여 일관된 상태를 유지한다.
First class는 언어가 제공하는 개념적 의미에 가깝고, 일급 컬렉션은 코드를 불변성, 캡슐화 등
코드 관리 방식을 개선하기 위한 설계의 한 부분이라고 생각을 정리할 수 있는거 같다.
❐ VO (Value Object)
VO(값 객체)는 특정 값 자체를 표현하기 위해 불변성을 가지는 객체다. 주로 특정 개념이나 속성 값을
하나의 객체로 캡슐화하여 표현할 때 사용한다. VO는 도메인 주도 설계(DDD)에서 중요한 역할을 하며,
Java에서는 일반적으로 불변 객체로 설계하여 값을 안전하게 다룬다.
VO를 사용하게 되면 다음의 장점을 누릴 수 있다.
- 불변성을 통한 안전성 보장
- 코드의 가독성과 유지보수 개선
- 중복 코드 방지
- 값 기반 동등성 비교
❐ VO는 일급 객체?
VO는 일급 컬렉션과 마찬가지로 일급 객체의 필요 조건을 충족하지만, 일급 객체와 완전히 동일한 개념은 아니다.
일급 객체는 언어적 차원에서의 개념이고, VO는 일급 컬렉션과 같은 설계적 차원이다.
그럼 VO가 일급 객체 후보가 될 수 있는지 예제를 통해 알아보자.
public class Money {
private final long value;
public Money add(Money money) {
return new Money(this.value + money.value)
}
}
public class MoneyExample {
public static void main(String[] args) {
// Money VO를 변수에 할당
Money money = new Money(100);
// 함수의 인자로 Money VO 전달 및 반환값으로 사용
Money money = money.add(new Money(200);
}
위 예제에서도 VO(Money)는 일급 객체가 되기 위한 필요 조건을 모두 만족한다.
따라서 VO 또한 일급 객체의 후보가 될 수 있다.
❐ 오늘의 결론
스터디 시간에 언급된 것을 트리거로 개념이 흔들렸음을 인지했고 오늘 다시 정리하게 됐다.
결과적으로 코드 리뷰할 때 "일급 객체를 사용하면 좋겠다"와 같은 피드백은 너무 추상적이였다고 생각한다.
그리고 람다는 일급 객체이다 아니라라는 논쟁에 대해서 그냥 일급 객체의 후보 정도로 생각하면 될 듯하다.
만약 관련해서 이야기를 하게 되면 동료들과 소통에 문제가 없을 정도로 이야기만 하면 될 듯 하다.
스터디원의 좋은 정리
Lamda 표현식이 만들어내는 '함수형 인터페이스의 인스턴스'가 일급 객체의 특성을 가진다.
암튼, 앞으로 일급 객체는 언어적 차원의 개념이기 때문에 사용을 지양하고,
되도록 설계적 차원의 단어(vo, class, first-collection, lambda)를 사용하도록 해야겠다.
'우테코 7기 > 2주차' 카테고리의 다른 글
[Refactoring] 제어할 수 있는 코드를 작성하자! (0) | 2024.10.27 |
---|---|
[Refactoring] 패키지가 무엇을 제공하는지 전달하자! (0) | 2024.10.27 |
2주차 회고 (0) | 2024.10.27 |
자동차 경주 (0) | 2024.10.21 |