[JAVA] JAVA에서 Interface를 사용하는 이유
1. Cooperation (협업)
Interface 미 사용시
한국인을 위한 번역기를 업무를 분담하여 만든다고 가정하자. 아래와 같은 결과물이 나왔다.
팀원1 의 작업물
/**
* @author 팀원1
*/
public class EnglishTrans {
public String koreanToEnglish(String str) {
//TODO: 한국어를 영어로 번역
}
public String englishToKorean(String str) {
//TODO: 영어를 한국어로 번역
}
}
팀원2 의 작업물
/**
* @author 팀원2
*/
public class ChineseRendering {
public String translateChinese(String str) {
//TODO: 한국어를 중국어로 번역
}
public String translateKorean(String str) {
//TODO: 중국어를 한국어로 번역
}
}
개인의 개성이 적나라하게 반영되었다.
메서드명, 클래스명 모두 다르다. 번역 기능을 만드는 일도 벅차서, 새로 팀원을 영입해 “우리의 클래스들을 참조하여 화면에 제공할 API를 만들어줘”하고 시켰다. 새 팀원은 클래스명을 받고 개발을 시작했다.
EnglishTrans trans = new EnglishTrans();
String str = trans.koreanToEnglish(str);
// TODO: Business Login
...
ChineseRendering rendering = new ChineseRendering();
String str = rendering.translateChinese(str);
// TODO: Business Login
...
Interface 사용시
public interface Translation {
// 한국어를 번역하다
public String translate(String str);
// 한국어로 번역하다
public String translateInto(String str);
}
위는 공통적으로 implements할 인터페이스이다. 각자 만들 클래스에서 Translation 인터페이스를 implements 한다. 그럼 강제로 해당 메소드를 구현해야 한다.
각 팀원 1과 2의 작업물
/**
* @author 팀원1
*/
public class EnglishTranslation implements Translation {
@Override
public String translate(String str) {
//TODO: 한국어를 번역
}
@Override
public String translateInto(String str) {
//TODO: 한국어로 번역
}
}
/**
* @author 팀원2
*/
public class ChineseTranslation implements Translation {
@Override
public String translate(String str) {
//TODO: 한국어를 번역
}
@Override
public String translateInto(String str) {
//TODO: 한국어로 번역
}
}
아래는 호출하는 부분이다.
Translation trans = new EnglishTranslation();
String str = trans.translate(str);
// TODO: Business Login
...
Translation trans = new ChineseTranslation();
String str = trans.translate(str);
// TODO: Business Login
...
사용할 때 인터페이스를 이용해서 원하는 언어에 따라 클래스를 골라서 객체를 생성해서 쓰면 되고 통째로 로직을 북붙해서 재활용하기도 편하다.
물론 인터페이스를 만들지 않아도 서로 약속하고 맞출 수도 있지만 오타가 발생하거나 작업량이 많아지면 잊어버릴 수 있다.
굳이 협업이 아니더라도 위와 같은 구조를 가지고 가는 것이 좋겠다.
Easy to replace (교체 용이)
이번엔 암호화 기능을 개발한다고 가정한다.
public interface Crypto {
// 암호화
public String encrypt(String str);
// 복호화
public String decrypt(String str);
}
위와 같이 인터페이스로 표준을 잡고 아래와 같이 구현체를 개발한다.
public class SHA512Crypto implements Crypto {
@Override
public String encrypt(String str) {
// TODO: SHA512 방식으로 암호화
}
@Override
public String decrypt(String str) {
// TODO: SHA512 방식으로 복호화
}
}
SHA512 방식은 더 이상 안전하지 않는 것 같다. 다른 방식으로 바꾸고 싶다면 클래스를 개발해서 이름만 교체하면 된다.
교체할 때에는 객체를 생성하는 클래스만 변경해주면 아래의 로직은 일체 수정할 일이 없다.
Crypto crypto = new SHA512Crypto(); // 기존 SHA512 방식
String enc = crypto.encrypt("1234");
교체
Crypto crypto = new SuperPowerCrypto(); // 신규 개발한 암호화 방식
String enc = crypto.encrypt("1234");
1. Multiple inheritance (다중 상속)
이 부분은 실제 자바에서 사용하고 있는 부분을 볼게요.
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
}
기본적으로 Map
이니깐 Map
인터페이스를 구현했다.
Cloneable
로 인스턴스가 복제 가능하도록 하였고 Serializable
로 직렬화했다.
기본적으로 Cloneable
과 Serializable
은 구현할 필요없이 자바가 내부적으로 해준다.
필요한 인터페이스를 상속받아서 구현하면 여러 기능들을 구조적으로 포함할 수 있다.
결론
이외에도 인터페이스를 사용하는 여러 디자인 패턴이 있다. 상세히 알고 싶으시다면, 이 부분은 자바 디자인 패턴을 따로 공부하시면 된다.
인터페이스는 잘 사용하면 프레임워크나 배포되는 공용 컴포넌트, 유틸 등을 개발할 때 아주 효과적이다.
다만, 잘 사용해야된다.
반대로, 일반 웹 개발 프로젝트에서는 오히려 복잡도만 증대시키는 상황이 많다. 많은 사람들이 불편해하고 필요성을 못느낀다면 과감히 버리는게 맞다.
결론은? 잘 사용해야된다.
댓글남기기