인터페이스를 설계를 할때 여러가지 면에서 고민을 해야 한다.

관련한 포스팅도 많이 했다.
http://www.ologist.co.kr/tag/interface

또 인터페이스 얘기를 꺼내는 것은 사례를 바탕으로 한 고민을 같이 해 보자는 취지이다.

AttachDAO라는 인터페이스가 있다. 이것은 일반적인 포스트의 첨부파일을 넣는 DAO이다. 기본적으로 CRUD를 하는 메소드들이 정의가 되어 있고, 그것을 구현해서 AttachDAOImpl이라는 concrete클래스를 만든다. 지금까지는 일반적으로 layerd architecture상에서의 코딩이다.

근데, 위에서 사용한 첨부파일 정보를 가지고 다른 테이블에 데이터를 넣는 DAO를 만들어야 한다. 이 시점에서 선택을 한다. AttachDAO 인터페이스를 이용해서 OtherAttachDAOImpl 다른 concrete클래스를 만들어서 사용을 한다. 쓸데없는 인터페이스를 줄일수 있으니깐, happy하지 않을까? 내 생각은 아니다.

인터페이스라는 것은 어떤 클래스의 signature이기도 하지만, inteface를 implements를 하는 것 자체가 하나의 책임을 주는 것이다. 같은 책임을 가지는 concrete클래스도 아닌데, 같은 인터페이스를 사용하는 것은 혼란의 여지가 많다. 전혀 다른 의도라는 것이 키포인트가 될수가 있다.

다음과 같은 AttachBO.java가 있다. AttachDAO인터페이스를 구현한 concrete클래스인 AttachDAOImpl를 DI해줄 경우가 원하는 비즈니스 객체였다. 다른 concrete객체인 OtherAttachDAOImpl는 같은 인터페이스를 구현한 것이므로 아래 비즈니스 객체에 넣어주면 전혀 다른 의도로 작동을 할 것이다.

public AttachBO{
  AttachDAO attachDAO;

  public setAttachDAO(AttachDAO attachDAO){
         this.attachDAO = attachDAO;
  }
}


위와 같은 코드에서 인터페이스가 같다고 해서 다형성의 의미가 있는 것도 아닐뿐더러 서로 관계가 없는 테이블에 처리를 하는 작업이므로 엄연하게 인터페이스를 나누는 것이 좋아 보인다. 그럼 아래처럼 2가지의 다른 책을 가진 DAO를 DI를 하게 되는 것이다.

public AttachBO{
  AttachDAO attachDAO;
  OtherAttachDAO otherAttachDAO;

  public setAttachDAO(AttachDAO attachDAO){
         this.attachDAO = attachDAO;
  }
  public setOtherAttachDAO (OtherAttachDAO otherAttachDAO){
         this.attachDAO = attachDAO;
  }
}



물론, 정답은 일부분만을 보고 얘기를 하기는 어려운 부분이지만, contract by interface하면 클라이언트 클래스에 사용을 할때 오해가 없게 사용을 하는 것이 좋겠다.

"인터페이스를 모두(always) 만드는 것에 대해서 어떻게 생각하세요?"라는 얘기를 pistos님에게 질문을 받은적이 있다. 술자리여서 깊게 얘기는 할수 없었지만, 한번 정리를 해보고 싶은 이야기였다.

클래스를 작성을 할때 무조건!!! 인터페이스를 두는 방법은 그다지 좋은 방법이라는 생각이 들지는 않는다. 항상 느끼는 거지만, 무조건이라는 말을 하기는 쉽지 않다. (그렇지만, 난 극단적인 표현을 사용하는 사람들의(Holub on pattern, prefactoring) 이야기를 즐기는 편이다. 장단점을 확연히 알수 있는 계기가 되기때문이다.)

특정한 layer에서 I/F하는 부분에서는 강제화하는 것은 layered architecture차원에서 있을수 있는 일(?)이지만, 그렇다고 모든 클래스를 인터페이스를 만들고, 구현체를 구현하는 것은 바보 같은 일이다. 모든 코드를 유연하게 만드는 만큼 시스템을 복잡하게 만드는 일은 없는 것이다. 어느 시점에는 강제화(상속,concrete)을 해야 한다.

C/C++에서 구현을 하는 헤더처럼 모든 클래스의 인터페이스를 구현하는 것은 너무 많은 클래스를 만들게 하고, 불필요한 작업을 하게 만든다. (단, 클래스 관계가 아닌 구현에 너무 집착하는 개발자에게는 가능한 인터페이스를 좀더 깊게 생각해보라고 얘기를 하고 싶다.)

InterfaceImplementationPair를 읽다가 보면 inteface를 많이 만드는 버릇은 C/C++에서 온 버릇때문인듯하다. 사실 나도 다시 한번 생각을 해보니, 그렇거 같기도 하다..^^

The practice of taking every class and pairing it with an interface. So as a result you see pairs of things - maybe ICustomer and Customer or Customer and CustomerImpl. In many ways it echoes the C/C++ habit of header files for each class, although in this case the interfaces and implementations are actually separate types.

얼마전에 팀내에서 만성피로님이 interface를 사용하지 않겠다고 해서 논란이 있었지만, 적정한 합의점(?)은 layer간만이라도 최소한 interface로 만들자는 의견이었다. 사실 여러 개발자들이 따지고 들어가다가 보면 정답은 없기때문에 합의점을 찾는 것은 중요하다.

MF아저씨는 InterfaceImplementationPair에서 얻는 이득은 아래와 같이 얘기를 했다. 분명히 이득히 있다는 것이다.

The advantage of this approach is that you can completely substitute anything at any point by providing another implementation of the interface.

하지만, MF는 그런 기술을 그다지 좋아하지 않는다고 얘기를 한다.

This isn't, however, a technique that I've ever much liked. Using interfaces when you aren't going to have multiple implementations is extra effort to keep everything in sync (although good IDEs help). Furthermore it hides the cases where you actually do provide multiple implementations.

2가지 이유를 이야기를 한다. 우리가 불편하게 느끼는 2가지를 얘기를 하는 것이다. 사실 이 부분은 IDE가 MF가 얘기를 할때보다 더 좋아진게 분명하다.

이클립스에서 구현체로 바로 갈수 있는 플러그인이다.
http://eclipse-tools.sourceforge.net/implementors/download.html

2가지 이유중의 한 가지는 없어졌다
although good IDEs help, 싱크를 맞추는 문제는 상대적으로 노력이 적은 편이다.

또, 반대의 관점에서 보자. Concrete클래스 위주로 작성시이다.
과거에는 수작업으로 Extract Interface를 해야하기때문에 미리 만드는 수고를 해도 추후에 큰 빛을 볼수도 있었지만, IDE가 발달을 해서 eclipse IDE를 이용하면 extract interface를 할때 안전하게 할수가 있다. 구현체에 있던 주석까지도 안전하게 inteface로 옮겨준다. 수작업으로 하는 것은 좋지 않은 방법이며, IDE를 믿자.

구현중심의 클래스를 활용하다가 인터페이스가 필요한 시점이 생기면, 바로 refactoring을 하자. 다른 구현체로 교체하거나 인터페이스 기반으로 코드를 바꾸어야 할때 유용한 refactoring중의 하나이다.

Trade-Off가 발생을 한다. 인터페이스를 미리 만들고, 행위가 추가될때 CTRL 1으로 행위를 추가를 할 것인가? Concrete클래스에서 Multiple구현이 필요할때 Extract Interface를 할 것인가?

interface를 쓰던 concrete클래스를 쓰던간에 정확한 목표는 있어야 할 것이다. 개발을 할때 test코드와 production코드를 나누어서 작성을 할 것이냐? 둘다 가능한 코드로 작성을 할것이냐는 내 선택은 하나의 코드로 둘다(test code, production code) 가능하게 하는 것 이 베스트라고 생각을 한다. 중복코드가 없고, 변화가 생겼을때 유연하게 대처할수만 있다면, 어떤 한 것을 선택을 해도 그 방법이 절대 아니다라는 말을 할수가 없다.

테스트의 중요성과 TDD의 인기로 인터페이스로 코드를 작성하는 것이 일반시 되고 있다.
이 말은 Mock을 활용하기 위해서 multi구현이 가능한 형태로 되어야 한다는 이야기다. 인터페이스당 최소 구현체 2개라는 이야기다.

Concrete클래스는 Mock을 이용해서 테스트하기가 어렵다. 최근에는 이 마져도 여러가지 확장 라이브러리들(easymock-extenion, jmock-extention)이 손쉽게 할수 있는 방법을 제시를 해주고 있다. 여러가지 따지고 들어가다 보니, IDE의 발달로 정답이 없는 문제가 되어 버렸다. Trade-Off가 비슷하다는 생각이 든다.

비슷하다면, 보통 자신의 스타일에 맞는 것을 선호를 하게 된다. 여기서 미묘한 개인적인 취향으로 번지는 것이다. 코딩하는 스타일과도 밀접한 관계를 가질수도 있다.

많은 개발자들이 있고, 여러 lab,team이 존재를 할때는 가끔 강제적인 Rule이 필요하기도 하지만, 그게 가끔은 답답한 경우도 있다.

그렇지만, 항상 화두가 존재한다는 것이 재미있기도 하다. 모든 사람이 OOP를 잘하고, 정답이 하나였다면, 지루했을 것이다.

난 그래서 개발자가 Domain Expert에 가까워질수록 좋은 디자인의 코드가 나온다는 Eric Evans말에 올인한다. 시스템의 domain영역도 중요하지만, 회사스타일과 정책의 변화도 무시 못할 스키마중의 하나이다.

용어정리
- 인터페이스라는 말이 너무 다양하게 쓰여서 위에 글은 아래와 같은 용어로 정의를 하였다.
interface : 자바에서 사용하는 interface
I/F : 외부와 통신하는 interface


PS. 이런 너무 글을 길게 써버렸다. 정답을 찾기 힘든 문제라 말이 길어진 듯하다. 예지력을 키워나가야 겠다.

Role Interface

2006/12/24 11:37
MF아저씨가 Role인터페이스를 좋아해서가 아니라, 난 사실 RoleInterface가 좋다.

On the whole I much prefer role interfaces, so I suggest pushing towards them as much as you can. There is work involved in doing it, but my belief has always been that you should only use interfaces when you really need substitutability, and if you do need interfaces you should think hard about what the consumer of that interface needs

내가 RoleInterface를 좋아하는 이유중의 하나는 SRP와 상관이 있다.

상대적으로 HeadInterface같은 경우는 아무래도 책임이 명확치 않은 Heavy한 클래스를 만들어질 위험이 더 많은 듯하다. 마치 procedural한 C Style과 유사하다.

적당한 책임을 가지고 있어야 할 클래스에 아무 생각없이 인터페이스를 추가를 하고 구현하는 일이 많기때문이다.

새로운 행위를 추가할 시점이 오면, 한번만 더 고민을 해서 적당한 곳에 위치를 해야만 좀더 깔끔한 코드를 유지할수 있을 것이다.

* HeadInterface

public interface Activity ...
  MfDate earliestStart();
  MfDate earliestFinish();
  MfDate latestFinish();
  MfDate latestStart();

class ActivityImpl...
  List<Activity> predecessors() ...
  List<Activity> successors() ...

* RoleInterface

public interface Successor {
  MfDate latestStart();
}
public interface Predecessor {
  MfDate earliestFinish();
}

class Activity
  List<Predecessor> predecessors() ...
  List<Successor> successors() ...


HeaderInterface
http://martinfowler.com/bliki/HeaderInterface.html


RoleInterface
http://martinfowler.com/bliki/RoleInterface.html

Normal vs Fluent interface


* Normal Style
private void makeNormal(Customer customer) {
       Order o1 = new Order();
       customer.addOrder(o1);
       OrderLine line1 = new OrderLine(6, Product.find("TAL"));
       o1.addLine(line1);
       OrderLine line2 = new OrderLine(5, Product.find("HPK"));
       o1.addLine(line2);
       OrderLine line3 = new OrderLine(3, Product.find("LGV"));
       o1.addLine(line3);
       line2.setSkippable(true);
       o1.setRush(true);
  }


* Fluent Style
  private void makeFluent(Customer customer) {
       customer.newOrder()
               .with(6, "TAL")
               .with(5, "HPK").skippable()
               .with(3, "LGV")
               .priorityRush();
  }

오늘은 DDD에 대한 소식을 듣고, 예전에 봤던 글들을 다시 한번 봤다. 또, 느낌이 틀리다.
전에 봤던 그 느낌하고, 지금 느낌은 또 틀리다. 이 글을 또 나중에 한번 더 봐야겠다.

JMock's contains a very nice fluent API
mock.expects(once()).method("m").with( or(stringContains("hello"),
                                         stringContains("howdy")) );

JMock은 martin fowler가 생각하는 매우 나이스한 fluent API의 모습이다.

Joda Time에서 얘기하는 장점중의 일부를 간추렸다. 3가지를 추렸는데, fluent API의 장점하고 같다.  
  • Easy To use
  • Easy To Extend
  • Good Test Coverage
클라이언트 클래스에 API를 제공할때는 많은 고민을 하게 된다. 항상 적장한 클래스를 만들고 문제를 해결하는 것과 클라이언트 클래스에서 사용하기 쉬운 것에서 선택을 하게 된다.

주로, 사용자 관점에서의 API는 좀더 편하게 사용할수 있는 인터페이스가 더 좋은 API라는 생각을 한다. 그런 관점에서는 fluent API들은 너무도 직관적이고, 이해하기가 쉽다.

DomainSpecificLanguage
http://www.martinfowler.com/bliki/DomainSpecificLanguage.html

DslBoundary
http://www.martinfowler.com/bliki/DslBoundary.html

Generating Code for DSLs
http://www.martinfowler.com/articles/codeGenDsl.html

EvansClassification
http://www.martinfowler.com/bliki/EvansClassification.html

FluentInterface
http://www.martinfowler.com/bliki/FluentInterface.html

TimeAndMoney Java Code Library
http://timeandmoney.domainlanguage.com/

Joda Time - Java date and time API
http://joda-time.sourceforge.net/

March is Not a Number
http://www.eaipatterns.com/ramblings/40_marchnan.html

EDSL
http://altlang.org/fest/%EC%96%B8%EC%96%B4%EC%86%8D%EC%96%B8%EC%96%B4

오늘은 김창준님이 대안언어축제했던 발표문서를 보다가 정말 여러가지 건졌다.
아주 재미있는 글들이 많았다.

요즘 한창 잘나가는 eric evans와 우리의 영원한 영웅 martin fowler의 사진을 올려본다.
두 분땜시 참 여러가지로 도움이 많이 된다.

User inserted imageUser inserted image

BLOG main image
OOP and Java by ologist

공지사항

카테고리

All (649)
private!! (106)
WEB & IT (140)
Developer (400)