2005/12/15 (목) 12:32에 엠파스 블로그에 작성한 글에서 일부 발췌합니다.

'대립'이나 '양분'을 염두하고 두 가지 주장을 볼 때 보다는
양자의 균형점이나 각자의 이유를 기반으로 사고하는 것이 더 많은 아이디어를 주었다.
그 결과 다음과 같은 그림을 그릴 수 있었다.


개발자 편의성을 개별 모듈로 갈 수록 높아져야 하고
따라서 더욱 Humane 측면이 강조되어야 하고
많은 개발자가 공유하기 때문에 범용성이 강조되어야 할 경우는
범용적으로 것으로 Minimal 을 지향해야 하지 않을까?

옮기면서)
개별 모듈과 프로그래밍 언어라는 양단의 축 보다는
Domain-Specific과 Generic Programming으로 다시 그려보는 것이 나을 것 같다.

이올린에 북마크하기(0) 이올린에 추천하기(0)

* 이글은 엠파스 블로그에 2005/12/11 (일) 01:55에 작성했던 내용입니다.

Martin Fowler 의 글, Humane Interface에 대한 반응을 보다가 Elliotte Rusty Harold 의 글을 보게 되었다: http://www.cafeaulait.org/#news2005December7

그는 동일한 기능을 수행한다면 메소드 숫자를 줄여야 한다는 점을 강조했는데, 예를 든 것이 환상이었다.
Steve Jobs 의 컨퍼런스 과정에서 소개된 내용을 차용한 것이다.
MS 의 미디어센터를 조작하기 위한 두 개의 리모콘 제품과
iMac 의 리모콘이다.
40 여개 vs 6 개의 버튼
와우~
무비를 반쯤 보았다: Steve Jobs
영어로 진행하는 긴 내용이지만... 그의 말처럼 iMac 의 인터페이스는 Fantastic 하다.
현재로써는 충분히 Ultimate Desktop 이라고 할만도 하다.
리모컨이 보여주는 환상적인 인터페이스뿐 아니라
Front Row 라는 기본적인 조작 방식
아기자기한 소프트웨어들의 인터페이스

하드웨어/소프트웨어를 아우르는 인터페이스 원칙이 한결같다.
애플 스타일이라고 하는...
겉으로 드러나는 형상뿐 아니라
감각적으로 느껴지는 부분에 있어서까지..
다시 생각해보면 Fowler 의 예는 정말 잘못된 것으로 볼 수 있는 것 같다.
인터페이스의 메소드 숫자를 줄이는 일이란
as simple as possible 까지만


구글과 첫눈의 인터페이스도
Humane Interface 와 Mimimal Interface 의 정확한 의미는 모르지만
양자를 대립관계로 놓고 보기는 무리가 있는 것 같다.
흠... 뭔가 꼬집어 말할 순 없지만
API 설계에 있어서도 잘만들어진 UI를 통해 영감을 받아야 할 것 같다.
이올린에 북마크하기(0) 이올린에 추천하기(0)

2006/01/11 (수) 23:50 에 작성된 글을 엠파스 블로그에서 옮겨옵니다.

Martin Fowler 의 글, Fluent Interface를 읽고 번역할 즈음에 수행하던 프로젝트에서 라이브러리를 구현하게 되었습니다. 용도가 적절하다 싶어 적용해보고 싶은 마음이 들더군요. 적용 대상으로 선정한 부분은 자바 기반의 웹 프로그램과 특정 라이브러리를 사용하는 제품 사이의 데이터 형식 변환이었습니다. 해당 제품이 제공하는 라이브러리 API 를 써야 하는데 편의성에 약간의 문제가 있었죠.

그래서, 이를 래핑하는 과정에서 Fluent Interface를 적용했습니다. 해당 제품의 API는 객체의 데이터를 Dataset 이라는 일종의 컨테이너 클래스에 담아서 전송하게 되어 있었습니다. 이를 자동으로 생성하는 코드를 만도는 것은 적절하지 못한 상황이었습니다. 미봉책이나마 개발자들의 편의성을 높여야 했습니다. 그러다보니 '정형화 된 속성 값을 설정하고, 값을 담는 일' 이기에 Fluent Interface를 적용할만 했던 것이죠.

Dataset dsCustomer= new Dataset("dsCustomer");
dsCustomer.addColumn("id", ...);
dsCustomer.addColumn("name",  ... );
dsCustomer.addColumn("address",  ... );
dsCustomer.addColumn("customerTypeId", ... );
dsCustomer.addColumn("customerTypeDescription",  ... );
dsCustomer.addColumn("description", ... );
dsCustomer.addColumn("groupId", ... );
dsCustomer.addColumn("groupName", ... );

위와 같은 설정을 보면 속성이 많아지고, 컴포지트(composite) 형태의 객체를 평면화(객체지향 아닌 기술과 데이터를 주고 받을 때 포함하는 객체까지 전부 끌어내는 현상을 제가 부르는 이름..ㅡㅡ;)하면.. 위와 같은 지루한 코드가 상당히 길게 작성됩니다. 실제로는 이런 류의 클래스마다 값을 설정하는데 수 십줄이 필요하게 됩니다.
Fluent Interface 형태를 써서 연쇄적(Cascading)으로 설정을 한다면 훨씬 간결해지죠.

Dataset dsCustomer= new Dataset("dsCustomer")
  .addColumn("id", ...)
  .addColumn("name",  ... )
  .addColumn("address",  ... )
  .addColumn("customerTypeId", ... )
  .addColumn("customerTypeDescription",  ... )
  .addColumn("description", ... )
  .addColumn("groupId", ... )
  .addColumn("groupName", ... );

대개 설정에 관여되는 Setter 유형의 메소드는 반환값이 void 이거나 성공/실패 여부를 반영하기 쉽상인데, 그런 방식이 아니라 생성된 객체를 다시 반환해주면 릴레이로 생성할 수 있죠. 계주를 떠올리면 딱이네요.. ^^
만일, addColumn 말고도 다른 것들이 많이 설정되야 하는 것이 아니라면 메소드를 더 축약할 수도 있겠죠. Dataset 에는 컬럼만 추가된다거나 하는 상황이라면..

Dataset dsCustomer= new Dataset("dsCustomer")
  .with("id", ...).with("name",  ... ).with("address",  ... )
  .with("customerTypeId", ... ).with("customerTypeDescription",  ... )
  .with("description", ... ).with("groupId", ... ).with("groupName", ... );

위와 같이 더 축약하실 수도 있죠.

그러나, 이러한 방식이 도움이 되는 경우는 한정되어 있습니다.
- 자동화가 어려운 상황에서 빈번한 설정이 요구되는 작업을 간편하게 하고자 할 때 유용함.
- 설정하는 데이터가 많으면서, 유형은 단순할 수록 효과가 커짐
이올린에 북마크하기(0) 이올린에 추천하기(0)

Published InterfaceRefactoring 에서부터 클래스를 정의한 코드 외부에서 사용하는 인터페이스를 지칭하기 위해 쓴 용어다. 단순히 Java 의 public 이나 C#의 interal 이 아닌 public 의 의미만을 나타내는 것은 아니다.) 이글을 통해서 public 과 published의 public과 private 차이 이상으로 그 차이가 중요함을 주장한 일이 있다.

그 이유는 published interface 가 없다면, 하나의 코드베이스에 인터페이스와 호출하는 코드가 있기 때문에, 변경이 필요한 인터페이스가 있으면 해당 인터페이스를 변경하고, 이를 호출하는 코드를 갱신할 수 있다. 리팩토링 도구를 활용하면 이름을 바꾸는 일은 쉽게 수행할 수 있다. 그러나, published 인터페이스의 경우에는 호출하는 코드를 제어할 수 없기 때문에 보다 많은 것을 고려할 필요가 생긴다.
이올린에 북마크하기(0) 이올린에 추천하기(0)

역자 주: 이 글은 두 차례 갱신되었습니다. 인터페이스를 구현한 명시적인 클래스(Implementation)-예를 들어 java 환경에서라면 .class 파일이되겠죠-와 명시적으로 만들지 않아도 이미 전제가 되는 암묵적(implicit) 구현체 사이의 분류는 상당히 현학적으로 들릴 수 있습니다. 왜 이런 쓸데없는 고민을 하느냐고 물으실 분도 있을 것 같습니다. 짐작컨데 테스트를 중시하는 TDD 기반으로 개발을 하다보면, 다수의 인터페이스를 구현하는 클래스를 만들게 되고, 그러다보면 부딪히게 되는 고민일 것 같습니다.

Java 와 C# 모두는 순수한 인터페이스 형식을 고수한다. 만일 Mailable 이라는 인터페이를 선언하면 자바에서 class Customer implements Mailable 라는 문장으로 이를 구현할 수 있다. 하나의 클래스는 몇 개의 인터페이스라도 구현할 수 있다.

이러한 방식의 인터페이스 구현은 클래스를 정의할 때 생겨나는 암묵적인 인터페이스(implicit interfaces)를 고려하지 않는다. Customer 클래스에 정의된 모든 public members 는 Customer 의 암묵적 인터페이스가 된다. (내가 본 모은 객체지향언어에서는 이러한 암묵적인 인터페이스를 갖고 있다.)

Java 나 C# 은 암묵적 인터페이스를 구현할 방법을 제공하지 않는다. 즉, class ValuedCustomer implements Customer 와 같은 것을 허용하지 않는다. 암묵적 인터페이스를 구현한다는 것은 무엇을 의미할까?  ValuedCustomer 클래스는 Customer 에 선언된 모든 public 메소드를 구현하지만, 반드시 이들 메소드의 구현체를 갖지는 않겠다는 것이다. 즉, public 메소드의 몸체 부분이나 public 이 아닌 메소드나 데이터를 구현하지 않을 수 있다는 것을 의미한다. 다시 말하면, 인터페이스를 상속하나 구현을 상속하지는 않겟다는 것을 의미한다.

어떤 경우에 이러한 기능이 요구될까? 현재와 같은 Collection 프레임워크가 추가되지 이전의Java 초기의 기억을 떠올려보겠다. 우리는 Vector 클래스를 자체적인 구현 클래스로 대치하고 싶었지만, Vector 는 클래스이기 때문에 상속하는 것만이 가능했다. 많은 사람들이 종종 라이브러리가 인터페이스를 제공하지 않아서, 불필요한 기능을 떠안고 상속을 해본 경험이 있을 것이다.

특히 최근에 테스팅 과정에서 이런 일이 비일비재하다. 어떤 클래스의 스텁(stub)을 만들려고 할 때, 인터페이스가 없는 클래스라면 까다로와지거나 아예 불가능한 경우가 생긴다. 테스트 전용으로 인터페이스를 정의해야 하는 일이 발생한다. 인터페이스와 구현 쌍(nterface Implementation Pair) 를 기본 접근 방식으로 활용하지 않는 상황이라면 이런 일은 상당히 꺼려질 것이다. 암묵적인 인터페이스 구현 방식이 보다 간명하게 적용할 수 있을 것이다.

그런데, 프로그래밍 언어가 왜 이들을 지원하지 않을까? 나는 언어 설계자가 아니기에 그 이유를 알 수 없다. Anders Heljsberg(Turbo Pascal, Delphi, C#, 닷넷 프레임워크 등의 설계자)에게 이를 물어볼 기회가 있었고, 명시적으로 멤버를 virtual 로 선언한 경우에 한해서 오버라이딩을 하는 것을 선호한다는 정도로밖에는 답변을 듣지 못했다. 그러나, 이는 주로 상위 클래스의 정의 내용에 변경이 가해지는 하위 클래스의 정의에 관한 것이었고, 상속을 다룬 포괄적인 이야기에 일부였던 관계었다. 게다가 저녁 시간의 짧은 대화였기에 충분한 의미를 부여하기도 애매하다.

이하는 새로 갱신된 내용입니다.

이글을 쓰고 난 후 나의 오랜 동료 Mike Rettig 가 이러한 방식 즉, 하나의 클래스가 암묵적으로 다수의 인터페이스를 구현하는 것에 대해 한가지 문제점을 지적했다. 자바의 예를 들면, Customer 라는 클래스가 있다고 할 때, 이는 암묵적으로 public, protected, package 그리고 private 이라고 하는 네 개의 인터페이스를 갖는다. Customer 객체와 협업하는 객체들은 이 중에 하나의 인터페이스를 쓰게 된다. Customer 객체의 다른 인스턴스는 private feature(프로퍼티나 오퍼레이션)를 쓸 수도 있다. 만일 암묵적인 인터페이스를 구현해야 한다고 가정하면, 이들 네 가지 모두를 구현해거나 그에 상응하는 무언가를 해야 한다. 나는 그것이 얼마나 어려운 작업인지 알 수도 없고, 암묵적으로는public 인터페이스만으로도 충분하다는 사례는 도처에서 발견할 수 있다.

Ian Griffiths
는 클래스와 인터페이스를 섞는 과정에서 발생할 수 있는 문제를 지적했다. Microsoft 의 COM 기술은 실제로는 이들 둘은 명확하게 구분한다:  "COM 기반의 객체를 사용하려면 인터페이스를 활용해야 한다. 그래야 항상 고유의 구현 클래스를 보장할 수 있다." VB6 에서는 COM 인터페이스가 개발자가 인식하지 못하는 가운데서 생성되기 때문에 이에 대해 자유로울 수 있다.

동적 타입 기반의 언어(스크립트)에서 이러한 문제가 없다. 다른 클래스의 인터페이스를 구현하고 싶으면, 단지 동일한 메소드를 구현하고, 객체를 생성해서 쓰면 된다. 자바에서도 동적인 프록시(dynamic proxies)를 써서 이를 구현하는 것이 보편화 되어 있지만, 암묵적 인터페이스 구현을 지원하는 것이 보다 효과적인 프로그래밍을 짜게 할 것이다.

이러한 것이 왜 문제가 되는가? 나는 주로 이 문제가 테스트에 관련되어 있다고 생각한다. 만일 새로운 구현 클래스를 사용할 수 없는 상황이라면 테스트 전용 객체(Test Double)를 추가하는데 어려움을 겪을 것이고, 부모클래스가 실제 데이터베이스와의 연결을 필요로 하는 상황이라면 이를 상속한 클래스가 테스트 전용 객체 역할을 구현하는데 쉽지 않을 것이다. 이 문제는 단순히 테스트에 국한된 것일 수 있다 -  Robert Conley 의 말에 따르면, 그는 테스트에서만 사용할 것들에 대해서는 VB6 (앞서 언급한 측면) 를 많이 사용한다고 한다.
이올린에 북마크하기(0) 이올린에 추천하기(0)