> ## Content Index
> Fetch the complete content index at: https://www.popit.kr/llms.txt
> Use this file to discover other available public pages before exploring further.

# JAVA 8 에서 케이크 패턴(Cake Pattern)을 사용해보자
- URL: https://www.popit.kr/cake-pattern-in-java-8/
- Published: 2018-04-03T05:34:48.000Z
- Updated: 2019-08-01T04:23:03.000Z
- Author: 이 상우
- Tags: #소프트웨어, Java, java 8, cake pattern, default method, #개발자

[지난 글](https://www.popit.kr/dependency-injection-for-testability/)에서 의존성 주입(Dependency Injection)에 대한 이야기를 했었다. 이번 글에서는 다른 방식의 의존성 주입인 케이크 패턴을 소개한다.  
스칼라의 케이크 패턴에 대한 자세한 내용은 [여기](https://www.cakesolutions.net/teamblogs/2011/12/19/cake-pattern-in-depth?ref=popit.kr)를 참고 바란다.

### 케이크 패턴

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

### 공통 구성

로그 기록을 담당하는 Logger, 설정 로딩을 담당하는 Configuration 가 있다.  
Logger 는 File 에 기록하는 LoggerUsingFile, DB에 기록하는 LoggerUsingDB 가 있다.  
Configuration 는 File 에서 읽어오는 ConfigurationFromFile, DB 에서 읽어오는 ConfigurationFromDB 가 있다.  
Rabbit 이 Logger 와 Configuration 둘을 사용하는 구조라고 하고, File 을 사용하는 Rabbit 과 DB 를 사용하는 Rabbit 의 인스턴스를 각각 생성해보자.  
우선 최상위 인터페이스 코드는 다음과 같다.  

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

public interface Configuration {
  void load();
}

```

### 순수 인터페이스

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

```java
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();
  }
}
```

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

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

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

```

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

### 구현을 가진 인터페이스

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

```java
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 을 구성하면 다음과 같은 코드가 나온다.  

```java
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();
  }
}

```

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

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

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

```

### 마치며

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

자바에서 사용할 수 있는 케이크 패턴은 스칼라의 그것에 비교해서 많이 부족하지만, 용도에 맞게 사용하면 유용할 것이다. 인터페이스 제약과 동시에 기본 구현을 가질 수 있다는 것은 유용한 기능이다. 다른 대안에 대한 내용도 있으니 [여기](https://www.quora.com/What-is-the-Scala-Cake-pattern?ref=popit.kr)를 참고 바란다.

sample code : [https://github.com/prostars/cake-pattern.git](https://github.com/prostars/cake-pattern.git?ref=popit.kr)

(원글 : [http://prostars.net/235](http://prostars.net/235?ref=popit.kr))