2 Week

5 분 소요




enum

열거형이라고 부르며, 서로 연관된 상수들의 집합이다.
모든 열거형은 Enum클래스의 자손이며 Enum클래스의 멤버를 상속받는다.


특징과 장점

  • enum은 컴파일 후 public static final 필드이며 객체로 제공된다.
  • 상수들은 JVM 메서드 영역에 자리한다.
  • Enum클래스는 상수와 같이 인스턴스가 단 한번만 생성된다.
  • 컴파일 시점에 타입안정성이 보장되기 때문에 private 생성자만을 가진다.
    즉, 인스턴스 생성을 제어하며 싱글톤을 일반화 한다. (싱글톤 구현의 방법으로도 사용됨)
  • 그렇기 때문에 주소값을 비교하는 == 연산자의 사용이 가능하다.
    즉, 객체의 값을 비교하는 equals()보다 빠르고 컴파일 단계에서 검사되기 때문에 실수를 방지할 수 있다.


enum method

  • values() : enum클래스의 모든 상수를 배열로 반환한다.
// values 활용
public static LottoRank countOfMatchNumber(int countOfMatchNumber) {
	return Arrays.stream(values())
			.filter(LottoRank -> LottoRank.countOfMatchNumber == countOfMatchNumber)
			.findAny()
			.orElse(MISS);
			// 또는 orElseThrow(new IllegalArgumentException(error message))
}
  • valueOf() : String을 매개변수로 받아 일치하는 상수를 반환한다. (없으면 런타임 에러 발생)
  • ordinal() : 상수들의 index를 반환한다.


  • 단점
    enum자체가 가지는 비용이 적지 않다. static final 상수로도 충분하다면 enum의 사용 여부는 고려해보자





싱글톤패턴

객체의 인스턴스가 오직 1개만 생성되는 패턴이다.
하나의 인스턴스로 공유되며 접근, 사용할 수 있다

  • 가장 단순한 코드레벨의 싱글톤 패턴
// 동시성 이슈를 배제한 Lazy initialization 방식
// Thread Safety 하지 않다.
public class Person {
    private static Person instance = null;
    
    private Person() {
    }
    
    public static Person getInstance() {
        if (instance == null) {
            instance = new Person();
        }
        return instance;
    }
}
  • 장점
    • 인스턴스가 하나기 때문에 매번 새로 인스턴스를 생성할 필요가 없다.
    • 싱글톤 패턴으로 만들어진 클래스는 전역 인스턴스이기 때문에 접근하기 편하다.
  • 단점
    • 클라이언트가 구체클래스에 의존 -> DIP OCP 위반
    • 멀티스레드 환경에서 인스턴스가 하나이기 때문에 문제가 발생할 수 있다.
    • 테스트가 어렵다. 인스턴스가 하나기 때문에 테스트를 하려면 매번 초기화를 시켜주어야 한다.


활용

  • synchronized : 사용하지 말자. 락을 걸어버리는 방법으로 동기화 문제를 간단하고 확실하게 해결하지만 그렇기 때문에 멀티스레드의 이점을 모두 막아버린다.

  • Lazy initialization + Double-checked Locking : 이것도 좋지 않다.

// 조건문으로 먼저 인스턴스를 확인한 다음 synchronized를 동기화 시켜 인스턴스를
// 생성하는방법, 처음 생성이후 synchronized를 실행하지 않아서 성능저하 완화 가능
public class Person {
	private static volatile Person instance;
	
	private Person() {
	}
	
	public static Person getInstance() {
		if (instance == null) {
			synchronized (Person.class) {
				if (instance == null) {
					instance = new Person();
				}
			}
		}
		return instance;
	}
}
  • Initialization on demand holder idiom : 제일 많이 사용되는 방법
// 클래스 안에 클래스(holder)를 두어 JVM의 클래스 로더 매커니즘과 클래스가 로드되는
// 시점을 이용한 방법
public class Person {
	private Person() {
	}
	
	private static class LazyHorder {
		public static final Person INSTANCE = new Person();
	}

	public static Person getInstance() {
		return LazyHorder.INSTANCE;
	}
}
  • 스프링 컨테이너를 사용하는게 가장 좋다.
    • 상태를 유지(stateful)하면 안되고 무상태(stateless)로 설계해야 한다.
    • 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다
    • 읽기만 가능해야 한다.
    • 필드 대신에 자바에서 공유되지 않는, 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.
    • @Configuration, @Bean을 같이 사용한다.





인터페이스

컴퓨터 사이언스에서 인터페이스란 서로간의 약속, 규약 등의 개념으로 사용하게 끔 오픈된 것을 말한다.
일종의 서로를 이어주는 메시지 창구 역할을 하며, 대표적인 소프트웨어 인터페이스로는 API가 있다.
자바 에서 인터페이스란, 다른 클래스의 기본이 되는 틀을 제공하면서
다른 클래스 사이의 중간 매개역할을 하는 일종의 추상클래스 이다.
자바8 이전에는 오로지 추상메서드와 상수만을 포함할 수 있었는데
자바8이후 default method, static method가 추가되었다.

// 은행시스템을 운영하려면 이 금융결제원 인터페이스에 맞게 구현해야 한다고 치자
public interface Bank {

	// 상수 (하루 최대 인출 금액)
	public int MAX_INTEGER = 10000000;
	
	// 추상메서드 (인출)
	void withDraw(int price);
	
	// 추상메서드 (입금)
	void deposit(int price);
	
	// defualt 메서드 (고객의 휴면계좌 찾아주는 메서드 : 필수구현은 선택사항)
	default String findDormancyAccount(String custId){
		System.out.println("**금융개정법안 00이후 고객의 휴면계좌 찾아주기 운동**");
		System.out.println("**금융결제원에서 제공하는 로직**");
		return "00은행 000-000-0000-00";
	}
	
	// 정적 메서드 (블록체인 인증)
	static void BCAuth(String bankName){
		System.out.println(bankName+" 에서 블록체인 인증을 요청합니다.");
		System.out.println("전 금융사 공통 블록체인 로직 수행");
	}
}
  • 상수 : 컴파일 단계에서 정해지는, 인터페이스가 제공하는 변하지 않는 값
    • public static final을 명시하지 않아도 컴파일 단계에서 자동 선언 된다.
  • 정적메서드 : 인터페이스에서 제공하는 변하지 않는 메서드
    • public을 명시하지 않아도 컴파일 단계에서 자동 선언 된다.
  • 추상메서드 : 구현클래스에서 구현해야만 하고 거기서 실행된다.
    • public abstract를 명시하지 않아도 컴파일 단계에서 자동 선언 된다.
  • 디폴트메서드 : 인터페이스에서 제공해주지만, 구현클래스에서 재구현 할 수 있고 안써도 된다.
    • public을 명시하지 않아도 컴파일 단계에서 자동 선언 된다.
    • 구현클래스에 인스턴스 메서드로 추가됨. 즉, 구현객체가 있어야 사용할 수 있다.


  • 특징
    • 인터페이스는 직접 인스턴스를 생성할 수는 없고 추상메서드를 구현해 줄 클래스를 작성해야 한다.
    • 자바에서 다중상속은 지원하지 않지만 다중 인터페이스는 구현할 수 있다.


쓰는 이유?

  • 추상메서드와 상수를 통해 구현클래스에 동일한 동작을 보장한다.
    • 클래스의 설계 또는 표준화를 유도할 수 있다.
    • 자바8 이후 default method로 유연성을 추가로 확보했다.
  • 다형성을 이용하여 타입변환을 한다.
    • 회사에서 입출금 업무를 하는 로직이 있다면 인터페이스를 이용할 시
      새로운 은행에 대한 코드만 작성해서 인스턴스만 바꿔 끼면 끝난다.
  • 개인적으로 상속보다 좋아보인다..
    • has-a : “~를 할 수 있는”


문제점

  • 수정이 어렵다.
    • 자바8 이후 대대적인 개선으로 많이 좋아졌다고 생각한다. (default method)
  • 잘사용해야한다.. 불필요하게 어플리케이션의 복잡도만 증가시킬 수 있다.


default method

이미 잘 운영중인데 휴면계좌 서비스를 점진적으로 도입하기로 정책이 바뀌었다고 치자.
추상메서드로 새로 추가하려고 하면 금융결제원의 Bank 인터페이스를 implements한
각 은행사의 모든 클래스에서 무조건 구현을 해야한다.
은행사 마다 개발, 운영환경이 다르고 개발기간도 다르기 때문에 쉽지않다.
만약 default method로 정의하고 기본적인 구현부를 제공하면 각 은행에서 자신들만의 로직으로 재구현 할 수도 있고 사용을 안할 수도 있어서 점진적으로 도입하기로 한 정책을 실행할 수 있을것이다.
그대로 쓰거나, 재구현 하거나, 안쓰거나 할 수 있는것이다.
이것은 구현클래스가 자신만의 메서드를 각자 추가하는 것과는 엄연히 다르다.
즉, 운영중인 시스템에 효과적으로 유연함을 제공한다.
또한 default methodfunctional 하다.
이 말은 에러에 최소화 되었다는 의미 이기도 하다.


함수형 인터페이스

추상메서드가 단 하나뿐인 인터페이스 이다.

/* 
어노테이션의 기능은 단일 추상메서드인지 컴파일 단계에서 확인하기 위함이다.
@FunctionalInterface 가 붙지 않아도 함수형인터페이스로 사용될 수 있으나
직관성을 위해 붙이는걸 추천한다
*/
@FunctionalInterface
interface Calculate {
	int cal(int a, int b);
}


쓰는 이유?


자바의 람다식은 함수형 인터페이스로만 접근할 수 있기 때문이다.
람다식과 인터페이스는 1:1로 연결될 수 있기 때문에 이 추상메서드의
매개변수의 개수와 타입, 리턴타입과 동일한 람다식을 할당해서 사용할 수 있다.
단, static methoddefault method의 개수는 제약이 없다.

FunctionalInterface func = new FunctionalInterface() {
	@Override
	public void doSomething(String text) {
		System.out.println(text);
	}
};
func.doSomething("do something");





추상클래스

하나 이상의 추상메서드를 포함하는 클래스 이다.
추상클래스를 상속받는 클래스는 추상메서드를 반드시 재정의 해야한다.
추상클래스는 인스턴스를 생성할 수 없으며, 자식클래스가 인스턴스를 생성할 수 있다.

public abstract class Person {
	public String name;
	public Int age;

	public abstract void say();
}


쓰는이유?

  • 상속받는 클래스에게 추상메서드를 반드시 구현하도록 강제하기 위해
    • 공통적인 부분에 대한 어느정도의 규격을 잡아 놓는다. (미완성 설계도)
    • 공통된 필드와 메서드를 통일할 목적 - 중복 제거
    • 다형성
  • 구조를 재사용하기 위해서
  • 특정필드를 재사용하고 싶고 공개하고 싶지 않아서





둘의 차이

인터페이스는 정의된 메서드를 각 클래스의 목적에 맞게 기능 구현을 하는 느낌.
(구현 객체가 같은 동작을 한다는것을 보장하기 위해)
추상클래스는 자신의 기능들을 하위클래스로 확장시키는 느낌으로 다가가자.
(같은 부모클래스가 가진 기능들을 구현하기 위해)

  • 상태의 차이
    • non-static 멤버 즉, 상태 값 을 가지는가에 대한 차이
  • 다중 상속
    • 인터페이스는 다중으로 구현이 가능하지만 추상클래스는 단일 상속만 가능하다.





댓글남기기