내일배움캠프 31일차 TIL : Java - Enum 실무 활용, Lambda

김원기·2024년 6월 5일

TIL

목록 보기
35/42
post-thumbnail

내일배움캠프 31일차 TIL : Java - Enum 실무 활용, Lambda

예전에 다뤘었나...? 강의를 봤으니 복습을 할 겸 다시 ㄱㄱ

Enum

자바에서 Enum은 특정 값들의 집합을 나타내는 특별한 데이터 타입으로 열거형이라고도 부른다.

Enum 자체를 보면 말 그대로 단순히 데이터를 나타내지만 Java에서 다양하게 활용된다.

public int getResult(int type, int data) {
	int result = 0;

	if (type == 1) {
		result = data - (data * 0.1);
	}
	else if (type == 2) {
		result = data - 5000;
		if (result < 0) {
			result = 0;
		}
	}
	
	return result;
}

이러한 코드를

private static final int DISCOUNT_TYPE_RATE = 1;
private static final int DISCOUNT_TYPE_PRICE = 2;
private static final int EDIT = 2;
private static final int DELETE = 2;

public int getResult(int type, int data) {
	if (type == DISCOUNT_TYPE_RATE) {
		return data - (data * 0.1);
	}
	else if (type == DISCOUNT_TYPE_PRICE) {
		int result = data - 5000;
		if (result < 0) {
			result = 0;
		}
		return result;
	}
}

/* 사용 */
getResult(-100, 40_000);
getResult(DISCOUNT_TYPE_PRICE, 40_000);
getResult(DELETE, 40_000);

이렇게 보기 편하게 사용할 수 있다.

다만 해당 코드에서는 몇가지 문제점이 발생되는데

  1. type 안정성을 보장할 수 없습니다.

    상수로 RATE, PRICE를 정의했지만 매개변수의 정의가 int이므로 여기에는 -1, 100, 3 등 많은 값이 들어갈 수 있다.

  2. 상수 중복

    위의 EDIT, DELETE를 보면 실수로 같은 값을 정의 했지만, 문법 오류가 전혀 없기 때문에 컴파일러는 이 오류를 잡아 낼 수 없다.

  3. 의미 불명확성

    여기서 매개변수 type만 보면 어떤 type을 의미하는지 전혀 예상할 수 없기에 if문 안에 사용한 코드를 봐야만 의미를 유추할 수 있다.

Enum 활용

자바 8 부터 Enum을 지원하게 되었는데 앞의 코드를 수정하게 되면

public enum DistcountType {
	RATE, PRICE
}

public enum Method {
	CREATE, EDIT, DELETE
}

public int getResult(DistcountType type, int price) {
	if (DistcountType.RATE.eqauls(type)) {
		return price - (price * 0.1);
	}
	else if (DistcountType.PRICE.eqauls(type)) {
		int result = price - 5000;
		if (result < 0) {
			result = 0;
		}
		return result;
	}
}

이렇게 수정할 수 있고 해당 코드의 장점은 위에서 봤던 문제점을 해결할 수 있다.

  • 타입 안정성

    getResult() 메소드의 매개변수 type은 DiscountType으로 제한므로 Method는 넣을 수 없다.

  • 데이터 제약

    DiscountType에 정의된 enum 값만 넘길 수 있으므로 예상하지 못 한 값이 넘어오지 않는다.

  • 가독성

    의미를 가지는 단어이므로 어디에 활용되는 값인지 유추할 수 있다.

Lambda

lambda를 지원하기 전의 java는 값과 식 으로만 매개변수로 전달이 가능했다.

매개변수로 넘길 수 있는 값은 일급시민이며 일급시민의 조건은 다음과 같다.

  1. 변수에 담을 수 있다.
  2. 함수의 인자로 전달할 수 있다.
  3. 함수의 반환값으로 전달할 수 있다.

Enum에 Lambda를

여기는 코드로 설명하도록 하겠다.

public enum DiscountEvent {
    NONE,
    SUMMER,
    WINTER,
    BLACK_FRIDAY,
    NEW_YEAR;
}

public class Coupon {
	public int calcPrice(DiscountEvent event, int price) {
        int benefit;
        
        switch (event) {
            case NONE -> benefit = price;
            case SUMMER -> benefit = (int) (price * 0.1); // 10% 할인
            case WINTER -> benefit = (int) (price * 0.2); // 20% 할인
            case BLACK_FRIDAY -> benefit = (int) (price * 0.3); // 30% 할인
            case NEW_YEAR -> benefit = (int) (price * 0.5); // 50% 할인
            default -> benefit = 0;
        }
        
        return price + benefit;
    }
}


public class Product {
  /* 만약 상품에서도 동일한 할인율을 적용하려면 동일하게 계산해주는 코드가 필요하다 */
	public int calcPrice(DiscountEvent event, int price) {
        int discount;
        
        switch (event) {
            case NONE -> result = price;
            case SUMMER -> result = (int) (price * 0.1); // 10% 할인
            case WINTER -> result = (int) (price * 0.2); // 20% 할인
            case BLACK_FRIDAY -> result = (int) (price * 0.3); // 30% 할인
            case NEW_YEAR -> result = (int) (price * 0.5); // 50% 할인
            default -> result = 0;
        }
        
        return price - discount;
    }
}

위의 코드는 람다를 적용하지 않은 Enum을 활용하는 예제다

위의 예제를 보면 할인 이벤트와 할인율이 동일함에도 각각 구현을 해줘야 하는 문제가 발생한다.

이런 문제를 해소하기 위해 람다를 사용하여 할인 정책을 한 곳으로 모아두면 동일한 정책을 유지할 수 있고, 어떤 할인 정책을 가지는지 보기 편해진다.

public enum DiscountEvent {
  NONE((price) -> price),
  SUMMER((price) -> (int) (price * 0.1)),
  WINTER((price) -> (int) (price * 0.2)),
  BLACK_FRIDAY((price) -> (int) (price * 0.3)),
  NEW_YEAR((price) -> (int) (price * 0.5));

  private final Function<Integer, Integer> expression;

  DiscountEvent(Function<Integer, Integer> expression) {
      this.expression = expression;
  }
    
  public Integer calc(int price) {
      return this.expression.apply(price);
  }
}
public class Coupon {
	private final String name;
  private final int couponPrice;

  public Coupon(String name, int couponPrice) {
    this.name = name;
    this.couponPrice = couponPrice;
  }
  
	public int calcPrice(DiscountEvent event, int price) {
    return price + event.calc(price)
  }
}


public class Product {
  private final String name;
  private final int price;

  public Product(String name, int price) {
      this.name = name;
      this.price = price;
  }
  
	public int calcPrice(DiscountEvent event, int price) {
        return price - event.calc(price);
    }
}

이런식으로 Enum에 람다이든 다른 연관 데이터이든 연결을 시켜주게 되면 일관적인 데이터를 제공할 수 있게 된다.

장점을 정리해보면,

  1. 일관성: 정책이 바뀌더라도 계산식 자체가 한 곳에 있어 참조된 모든 곳에 일괄 적용이 된다.
  2. 가독성 : 할인 이벤트가 얼만큼의 할인율을 가지는지 Enum 안에서 바로 확인 가능하다.
  3. 코드 간결성: 사용하는 곳에서 매번 if나 switch를 사용하지 않고 enum에서 바로 계산식을 꺼내 쓸 수 있어 코드가 간결해지며, 의미 파악이 쉬워진다.
  4. 유지보수성 : 추가 되는 정책이 있더라도 enum에서 데이터만 추가하면 사용하는 곳은 전혀 수정할 필요가 없다.

끝!

profile
혼자 공부하는 블로그라 부족함이 많아요 https://www.notion.so/18067a27ac7e4f4790dde645fb3bf3d3?pvs=4

0개의 댓글