Dev

JAVA 8 에서 케이크 패턴(Cake Pattern)을 사용해보자

prostars 2018. 4. 1. 22:06

지난 에서 의존성 주입(Dependency Injection) 대한 이야기를 했었다. 이번 글에서는 다른 방식의 의존성 주입인 케이크 패턴을 소개한다.

스칼라의 케이크 패턴에 대한 자세한 내용은 여기 참고 바란다.

케이크 패턴

스칼라의 케이크 패턴을 자바에서 사용해보자케이크 패턴은 의존성 주입의 방법이다자바 8에서는 인터페이스가 구현 코드를 담은 디폴트 메소드를 가질 수 있게 되었다 디폴트 메소드를 이용하면 스칼라의 trait 비슷해진다비록 trait 셀프 타입을 사용할 수는 없지만어차피 자바는 스칼라처럼 with 사용하여 객체 생성 단계에 와이어링을 없으니 상관없다.

공통 구성

로그 기록을 담당하는 Logger, 설정 로딩을 담당하는 Configuration 있다

Logger File 기록하는 LoggerUsingFile, DB 기록하는 LoggerUsingDB 있다.

Configuration File 에서 읽어오는 ConfigurationFromFile, DB 에서 읽어오는 ConfigurationFromDB 있다.

Rabbit Logger Configuration 둘을 사용하는 구조라고 하고, File 사용하는 Rabbit DB 사용하는 Rabbit 인스턴스를 각각 생성해보자.

우선 최상위 인터페이스 코드는 다음과 같다.

public interface Logger {
void log(String message);
}

public interface Configuration {
void load();
}

순수 인터페이스

구현이 없는 순수 인터페이스만으로 Rabbit 구성하면 다음과 같은 코드가 나온다.

public class RabbitImplUsingFile implements ConfigurationFromFile, LoggerUsingFile {
@Override
public void load() {
System.out.println("load config from File.");
}
@Override
public void log(String message) {
System.out.println("write log to File.");
}
public void prepare() {
log("load config");
load();
}
}

public class RabbitImplUsingDB implements ConfigurationFromDB, LoggerUsingDB {
@Override
public void load() {
System.out.println("load config from DB.");
}
@Override
public void log(String message) {
System.out.println("write log to DB.");
}
public void prepare() {
log("load config");
load();
}
}

그리고 다음과 같이 인스턴스를 각각 생성할 있다.

RabbitImplUsingFile rabbitUsingFile = new RabbitImplUsingFile();
rabbitUsingFile.prepare();

RabbitImplUsingDB rabbitUsingDB = new RabbitImplUsingDB();
rabbitUsingDB.prepare();

순수 인터페이스만 사용하면 종속이 걸려 있는 구현을 모든 인터페이스를 구현하는 클래스에서 처리해야 한다.

구현을 가진 인터페이스

이런 상황을 디폴트 메소드를 사용해서 정리하자종속이 걸린 구현들을 인터페이스의 디폴트 메소드로 올리자.

public interface LoggerUsingFile extends Logger {
default void log(String message) {
System.out.println("write log to File.");
}
}

public interface ConfigurationFromFile extends Configuration {
default void load() {
System.out.println("load config from File.");
}
}

public interface LoggerUsingDB extends Logger {
default void log(String message) {
System.out.println("write log to DB.");
}
}

public interface ConfigurationFromDB extends Configuration {
default void load() {
System.out.println("load config from DB.");
}
}

이제 디폴트 메소드를 가진 인터페이스를 준비했으니 Rabbit 클래스를 구현하자구현을 가진 인터페이스로 Rabbit 구성하면 다음과 같은 코드가 나온다.

public class RabbitUsingFile implements ConfigurationFromFile, LoggerUsingFile {
public void prepare() {
log("load config");
load();
}
}

public class RabbitUsingDB implements ConfigurationFromDB, LoggerUsingDB {
public void prepare() {
log("load config");
load();
}
}

그리고 다음과 같이 인스턴스를 각각 생성할 있다.

RabbitUsingFile rabbitUsingFileWithDefault = new RabbitUsingFile();
rabbitUsingFileWithDefault.prepare();

RabbitUsingDB rabbitUsingDBWithDefault = new RabbitUsingDB();
rabbitUsingDBWithDefault.prepare();

마치며

어떤가? 조금 달라 보이는가?

Logger, Configuration 대한 구현 코드들이 Rabbit 클래스에서 사라졌다그리고 추후에 Logger, Configuration 구현 내용이 변경되더라도 Rabbit 클래스를 수정할 필요가 없다디폴트 메소드를 사용하지 않았다면 Logger, Configuration 구현 내용을 변경하기 위해서 모든 구현 내용을 가지고 있는 Rabbit 클래스를 수정해야 한다.


자바에서 사용할 있는 케이크 패턴은 스칼라의 그것에 비교해서 많이 부족하지만, 용도에 맞게 사용하면 유용할 것이다. 인터페이스 제약과 동시에 기본 구현을 가질 있다는 것은 유용한 기능이다. 다른 대안에 대한 내용도 있으니 여기 참고 바란다.



sample code : https://github.com/prostars/cake-pattern.git









반응형

'Dev' 카테고리의 다른 글

IntelliJ 의 JShell Console 을 활용하자  (1) 2018.04.30
NHN Ent 경력 공채  (0) 2018.04.19
Setting ZeroMQ for MacOS  (0) 2018.01.12
Source Tree for Mac 에서 password reset  (2) 2017.12.28
Google Mock 사용을 위한 간단한 정리  (1) 2017.08.10