| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
- Volatile
- lombok
- 일급 컬렉션
- synchronized
- builder
- spring security
- OAuth 2.0
- Spring
- factory
- Dependency Injection
- 일급 객체
- java
- Google OAuth
- Today
- Total
HJW's IT Blog
일급 객체와 일급 컬렉션 본문
들어가며
개발에 있어 일급 객체와 컬렉션은 OOP 와 함수형 프로그래밍 둘 다에서 매우 중요한 개념이다. 일급 객체/컬렉션은 단순히 기술적인 개념을 넘어, 코드의 가독성, 유지보수성, 확장성, 협업의 생산성 등을 높일 수 있다. 객체의 책임을 분리 하거나, SOLID 원칙 준수, 로직의 명확성 등의 유지 보수성이 향상될 수 있다.
일급 객체란 무엇인가?
일급 객체의 3가지 조건
First-Class Citizen 은, 다음 3가지 특성을 가지는 객체를 말한다 :
- 변수나 데이터 구조에 할당할 수 있어야 한다.
- 함수의 매개변수로 전달할 수 있어야 한다
- 함수의 반환값으로 사용할 수 있어야 한다.
JAVA의 일급 객체
JAVA에선 함수가 독립적인 일급 객체로 존재하지 않았었다. 이들은 메서드를 변수에 할당하거나 인자로 전달할 수 없었다. 하지만, JAVA 8 부터 도입된 람다 표현식, 함수형 인터페이스 등을 통해 메서드를 일급 객체처럼 다룰 수 있게 되었다.
그렇다면 어떻게 람다 표현식, 혹은 인터페이스를 이용해 메서드를 일급 객체처럼 다룰 수 있을까?
이걸 이해하기 위해서 우선 Wrapper Class 에 대해 이해해야 한다. Java의 int, string, char 과 같은 자료형은 primitive type 이라 부른다. 이러한 자료형들은 객체가 아니기 때문에 객체의 특성을 사용할 수 없다. 이러한 자료형들은 type 변환, 컬렉션 프레임워크에 사용할 수 없는 등의 제약 사항이 있다.
이러한 점을 극복하기 위해 Wrapper Class 를 사용한다. Wrapper Class 는 기본 타입의 값을 내부에 두고 “포장” 하는 형태로, 객체이다. Wrapper Class 를 사용함으로써, null 에 대한 처리, 유틸리티 메서드, 컬렉션 프레임워크 등을 사용할 수 있다.
람다 표현식이 일급 객체로 작동하는 원리
자바의 람다 표현식은 함수형 인터페이스의 구현체로 취급된다. 함수형 인터페이스는 단 하나의 추상 메서드만을 가지는 인터페이스이다. 예를 들어,
@FunctionalInterface
interface LambdaFunction {
int sum(int a, int b);
}
LambdaFunction lam = (int x, int y) -> x + y;
여기서, 람다 표현식 (int x, int y) -> x + y 는 LamdaFunction의 sum 메서드를 구현한다.
또한 람다표현식은 변수에 할당될 수 있으며, 다른 함수의 인자로 전달될 수 있는데, 이는 아까 언급했던 일급객체의 주요 특성이다.
Consumer<String> c = (t) -> System.out.println(t);
public static void print(Consumer<String> c, String str){
c.accept(str);
}
print((t)->System.out.println(t), "Hello World");
마지막으로, 함수의 반환값으로 사용할 수 있다
public static Consumer<String> hello() {
return (t) -> System.out.println(t);
}
일급 컬렉션
위에서 일급 객체의 특성에 대해 알아보았다. 일급 컬렉션이란, 이러한 일급 객체를 컬렉션으로 감싸 관련된 로직을 한 곳에 응집시키는 디자인 패턴이다. 이를 함으로, 개발자는 캡슐화를 만족시키고, 컬렉션을 사용하는 코드의 책임을 명확하게 분리하여 SRP 를 만족할 수 있다. 특히 일급 컬렉션은, 불변성을 유지하면서도 데이터의 상태를 관리하는데 유리하다.
일급 컬렉션의 특징
- 컬렉션을 감싸는 단일 클래스
- 일급 컬렉션은 컬렉션을 감싸는 단일 클래스로 구성된다. 예를 들어 List 혹은 Set 과 같은 컬렉션 자체를 그대로 사용하지 않고, 이를 포장하는 클래스를 만들어 해당 컬렉션에 관련된 모든 로직을 캡슐화 한다.
- 컬렉션과 관련된 로직의 응집
- 컬렉션을 다루는 로직은 일급 컬렉션 내부에 응집된다. 이렇게 하면 비즈니스 로직이 한군데에 모여있고, 컬렉션의 관리와 책임을 명확하게 정의할 수 있다.
- 불변성 유지
- 일급 컬렉션은 기본적으로 불변 객체로 설계하는것이 권장되는데, 이는 내부 데이터를 외부에 노출하지 않음으로써 컬렉션의 상태 병경을 방지하고, 데이터의 일관성을 유지할 수 있게 도와준다.
- 도메인 중심 설계
- 컬렉션을 단순 데이터 구조로 사용하는 대신, 컬렉션을 도메인 모델의 일부로써 의미 있는 동작을 포함시켜, 가독성과 유지보수성을 향상 시킬 수 있다.
다음은 예시이다
사용자 도메인 클래스
public class User {
private final String name;
private final boolean active;
public User(String name, boolean active) {
this.name = name;
this.active = active;
}
public boolean isActive() {
return active;
}
public String getName() {
return name;
}
}
사용자 목록 관리
public class Users {
private final List<User> users;
public Users(List<User> users) {
validate(users);
this.users = new ArrayList<>(users); // 불변성 유지
}
private void validate(List<User> users) {
if (users == null || users.isEmpty()) {
throw new IllegalArgumentException("Users list cannot be null or empty");
}
}
public List<User> getActiveUsers() {
return users.stream()
.filter(User::isActive)
.collect(Collectors.toUnmodifiableList());
}
public int count() {
return users.size();
}
}