티스토리 뷰
정말 오랜만에 포스팅인데, 이번 포스팅에서는 Effective JAVA 와 같은 책에서 정말 많이 언급되는 Composition, Aggregation, Delegation 에 대해서 정리해보려고 한다. 이 세가지를 함께 정리하는 이유는 굉장히 비슷하면서도 다르면서도.. 그런 개념들이기 때문이다!
개념에 대해서 설명하기에 앞서 내가 프로젝트에서 Delegation 을 활용한 사례에 대해서 소개하려고 한다.
최근에 프로젝트에서 외부 업체로부터 SDK 를 제공받아 개발을 하게 되었는데, 해당 SDK 의 기능중에서 한 가지는 byte[] 타입의 인증서를 EckaCertificate 객체로 파싱해주고 이를 통해 인증서 정보를 읽어올 수 있도록 하는 것이었다.
하지만 해당 객체의 메소드중 하나인 eckaCertificate.getPublicKeyData().getKey() 에서 에러가 발생하는 이슈가 있었는데, SDK 를 제공해주는 업체에서 해당 이슈가 내부적으로 해결하기 어려우니 직접 파싱해서 사용해줄수 없겠냐는 요구사항이 있었다.
처음에는 아무생각없이 '그럼 EckaCertificate 클래스를 활용하지말고 나만의 클래스를 만들어서 기능을 모두 구현하자.' 로 접근했다. 그래서 직접 byte[] 타입의 인증서를 직접 일일히 파싱하고 필요한 메소드를 일일히 구현했다. 하지만 이렇게 구현을 하면서 정말 개발 코스트가 높다는것을 느꼈고, EckaCertificate 클래스의 모든 메소드중에 하나때문에 이렇게 한다는게 조금 비효율적이라고 생각했다.
그러다 문득 깨달음을 얻는데... 그게 Composition 과 Delegation 을 사용하자는 것이었다.
public class SeAttestationCert {
private final EckaCertificate eckaCertificate;
public SeAttestationCert(byte[] seAttestationCert) {
this.eckaCertificate = new EckaCertificate(seAttestationCert);
}
public byte[] getCaIdentifier() {
return eckaCertificate.getCAIdentifier();
}
public byte[] getSubjectIdentifier() {
return eckaCertificate.getSubjectIdentifier();
}
public byte[] getMessage() {
return eckaCertificate.getMessage();
}
public byte[] getPublicKey() {
byte[] eckaPublicKey = eckaCertificate.getEckaPublicKey().toByteArray();
byte[] publicKey = new byte[eckaPublicKey[4]];
System.arraycopy(eckaPublicKey, 5, publicKey, 0, publicKey.length);
return publicKey;
}
public byte[] getSignature() {
return eckaCertificate.getSignature();
}
}
위 코드와 같이 Composition & Delegation 을 통해서 이슈가 발생했던 getPublicKey() 메소드에 대해서만 내가 원하는 형태로 파싱을 해서 이슈를 해결할 수 있었다.
물론 상속을 통해서도 구현할 수 있었을 것 같지만 EckaCertificate 클래스에서 사용되지 않는 메소드들까지 모두 노출되는 것이 싫었고 내가 사용할 메소드들만 제한하여 노출시킬 수 있다는 장점이 있었기 때문에 Composition & Delegation 방식을 사용했다.
Composition
Composition 은 Has-a 관계를 구현하기 위해 사용되는 설계기술이다. 다음 포스팅은 Effective Java 에 소개된 상속과 Composition 을 비교한 내용을 정리한 포스팅이다.
https://ch4njun.tistory.com/247?category=871177
간단하게 정리하면 상속과 비교해 다음과 같은 장점이 있다.
- 상속으로 구현하게 되면 상위 클래스의 모든 public 메서드가 클라이언트에게 공개된다. 하지만, 컴포지션을 사용해 구현하게되면 개발자가 원하는 메서드만 클라이언트에게 공개할 수 있다.
- 상위 클래스의 내부구현을 숨길 수 있다.
- JAVA 에서 지원하지 않는 다중상속의 목적을 달성할 수 있다.
- 상위 클래스에서 제공하는 메서드를 더 나은 버전으로 개선할 수 있다. (충분히 유연하다)
Override 는 아니지만 동일한 기능을 제공할 수 있다. - 참조하고 있는 인스턴스 변수를 변경해 프로그램을 동적으로 변경할 수 있다.
- 상위 클래스의 메서드 형태와 관계없이 유연하게 하위 클래스의 메서드를 정의할 수 있다.
상위 클래스에서는 String 반환 값을 가지지만 하위 클래스에서는 Integer 를 가지도록 할 수 있다.
상속을 쓰지말고 Composition 을 쓰라는게 아니라 반드시 상황에 맞게 적합한 설계방법을 사용해야한다는 것이다.
엄연히 두 설계방법은 목적이 다르다. 상속은 Is-a 관계를 구현하기 위해서 사용되고 Composition 은 Has-a 관계를 구현하기 위해서 사용된다. 하지만 Composition 을 사용해야 하는 상황에서도 상속을 사용하고 있는 사례가 많기 때문에 항상 상속을 사용하기에 앞서 고민을 하는 것이 좋다고 생각한다.
앞서 소개한 프로젝트에서의 사례에서도 Composition 이 활용됐다.
참고로 Composition 과 Delegation 은 대립된 개념이 아니다. 위 사례에서는 Delegation 을 구현하는데 있어서 Composition 이 사용된 것이다. 항상은 아니어도 대부분의 경우 Delegation 을 구현하기 위해 Composition 이 사용된다. 해당 내용은 마지막 정리부분에서 다시한번 이야기한다.
Aggregation
Aggregation 은 Composition 과 비슷한 개념이다. 하지만 Composition 은 A, B 관계에서 A 가 B 를 포함하고 있고, A 가 사라진다면 포함하고 있는 B 도 같이 사라지는것에 반해, Aggregation 은 A 가 B 의 집합의 개념이기 때문에 A 가 사라지더라도 B 는 사라지지 않는다.
즉, Composition 은 Has-a 관계이지만, Aggregation 은 Is-Part-Of 관계라고 할수 있다.
예를들면, 레고로 만들어진 집이 있다고할 때 이 집이 없어진다고해서 레고까지 없어지는게 아니기 때문에 이 상황을 Aggregation 으로 구현하기 적합하다고 할 수 있다.
public class ToyHouse{
private List<Block> blocks;
public ToyHouse(Block... blocks) throws Exception{
if (blocks == null) {
throw new Exception("Toy house needs blocks");
}
blocks = new ArrayList<Block>();
for(Block b : blocks){
blocks.add(b);
}
}
}
위 예제가 Aggregation 을 구현한 예제이고, Block 객체들이 ToyHouse 내부에서 생성되고 포함되는 것이 아니라, 이미 외부에서 존재하고 그 것들을 인자로 받아 구성하는 것을 확인할 수 있다.
Delegation
Delegation 은 위임이라는 뜻으로 특정 클래스의 역할을 또 다른 클래스에게 위임하는 구조를 말한다. 먼저 위에서 소개한 예시 코드를 다시한번 살펴보자.
public class SeAttestationCert {
private final EckaCertificate eckaCertificate;
public SeAttestationCert(byte[] seAttestationCert) {
this.eckaCertificate = new EckaCertificate(seAttestationCert);
}
public byte[] getCaIdentifier() {
return eckaCertificate.getCAIdentifier();
}
public byte[] getSubjectIdentifier() {
return eckaCertificate.getSubjectIdentifier();
}
public byte[] getMessage() {
return eckaCertificate.getMessage();
}
public byte[] getPublicKey() {
byte[] eckaPublicKey = eckaCertificate.getEckaPublicKey().toByteArray();
byte[] publicKey = new byte[eckaPublicKey[4]];
System.arraycopy(eckaPublicKey, 5, publicKey, 0, publicKey.length);
return publicKey;
}
public byte[] getSignature() {
return eckaCertificate.getSignature();
}
}
EckaCertificate 클래스의 역할을 SeAttestationCert 에 위임한 것을 볼 수 있다. 이렇게 위임함으로써 얻을 수 있는 장점은 EckaCertificate 클래스에 포함된 메소드의 결과를 통해 또 다른 결과를 도출해내거나 부가적인 기능을 수행할 수 있다.
이 과정에서 당연히 EckaCertificate 클래스의 코드에 영향을 받거나 영향을 주지 않는다는 것도 기억하자.
정리하자면 Composition 과 Aggregation 은 비슷한 개념으로 각각 Has-a, Is-Part-Of 를 구현하기 위해 사용되는 설계방법이다.
그리고 이들과 Delegation 은 밀접한 관계를 가진다.
Composition 은 클래스간에 Has-a 관계를 구현하고, 그 속에서 Overriding 과 같은 효과를 구현하기 위해서는 일반적으로 Delegation 을 활용하게 된다.
반대로 생각해보면 Delegation 을 하기 위해서는 Has-a 관계를 가지고 있어야 해당 클래스에게 위임이 가능하기 때문에 Composition 이 구현된 상황에서 주로 사용된다.
해당 포스팅에서 소개한 개념이 개념적으로 복잡하거나 어려운 내용은 아니라고 생각한다. 하지만 이러한 개념을 인지하고 개발하느냐 그렇지 않느냐에 따라서 전혀 다른 결과를 만들어내는 것 같다. 앞으로 무언가 공부할때 '이게 의미있는 개념일까..? 안쓸거같은데..' 라고 생각하는 것이 아니라 어느 상황에서 사용될 수 있을지 고민하면서 공부해야겠다고 다시한번 다짐한다..!
'개발 > Concept' 카테고리의 다른 글
[Concept] API 성능테스트 (feat. nGrinder 를 이용한 성능 테스트) (1) | 2021.10.31 |
---|---|
[Concept] Configure Service (0) | 2021.07.07 |
[Git] Git 좀 세울줄 아는 남자-2 (0) | 2021.07.05 |
[Git] Git 좀 세울줄 아는 남자-1 (1) | 2021.07.04 |
[Concept] JWT(Json Web Token)? (0) | 2021.06.06 |