[CS-JAVA] 컴포지션

Jiyeong·2023년 9월 14일
0

🙌 상속이란,

상속(Inheritance)란 하위 클래스가 상위클래스의 특성을 재정의한 것을 말한다. 부모 클래스의 메서드를 오버라이딩하여 자식에 맞추어 재사용하는 등, 상당이 많이 쓰이면서도 활용성이 높다.

상속의 단점

구현 상속(인터페이스 상속이 아닌)은 코드 재사용이 가능한 강력한 수단이면서도, 잘못 사용하면 객체의 유연성 / 캡슐화의 위반/ 다중 상속 불가 등의 결과를 초래한다.

상속의 오류 가능성이 있는 사례

/*
* HashSet 의 요소를 몇번 삽입했는지 갯수를 출력하기 위해만든 클래스
* */
public class CustomHashSet<E> extends HashSet {
    private int addCount = 0;
    public CustomHashSet(){}
	public CustomHashSet(int initCap, float loadFactor){
        super(initCap,loadFactor);
    }
	@Override
    public boolean add(Object o) {
        addCount++;
        return super.add(o);
    }
    @Override
    public boolean addAll(Collection c) {
        addCount+=c.size();
        return super.addAll(c);
    }
    public int getAddCount() {
        return addCount;
    }
}
public class Main {
    public static void main(String[] args) {
        CustomHashSet<String> customHashSet = new CustomHashSet<>();
        List<String> test = Arrays.asList("아","이","스");
        customHashSet.addAll(test);
        System.out.println(customHashSet.getAddCount());
    }
}

--> 제대로 된 결과라면 ['아','이','스']로 3이 나와야 하지만 6이 나온다. 이유는 HashSet의 부모클래스를 살펴보면 된다.

public boolean addAll(Collection<? extends E> c) {
    boolean modified = false;
    for (E e : c)
        if (add(e))
            modified = true;
    return modified;
}

부모클래스에서 addAll메소드는 Collection의 객체 수만큼 add메소드를 수행한다. 우리가 커스텀한 클래스에서도 add메소드를 오버라이딩했으므로 총 6번의 add가 수행되면서 addCount의 값이 6으로 나온다.

컴포지션

상속의 단점인 기존의 클래스를 확장하는 것이 아니라, 새로운 클래스를 만들고 private필드로 기존 클래스의 인스턴스를 참조한 방식(Forwarding(전달))이다. 그래서 클래스의 구성요소로 쓰인다는 의미에서 Composition이라고 한다.

새로운 클래스는 기존의 클래스의 구현방식을 벗어나고, 기존 클래스는 새로운 메소드가 추가되더라도 전혀 영향을 받지 않는다.

✔ 컴포지션을 사용하는 경우

  • 하나의 Object가 다른 Object를 has또는 is part of되는 경우, 컴포지션을 사용한다.
    ex) A car has a battery, a battery is part of a car.

💭 예시

public class CompositionExample{
	public static void main(String.. houseComposition){
    	new House(new Bedroom(), new LivingRoom());
        // The house is composed with a Bedrood and a LivingRoom
    }
	static class House{
    	Bedroom bedroom;
        LivingRoom livingRoom;
        
        House(Bedroom bedroom, LivingRoom livingRoom) {
        	this.bedroom = bedroom;
            this.livingroom = livingroom;
        }
        static class Bedroom{}
        static class LivingRoom{}
    	
    }
    

}

💭 적합하지 않은 상속의 예시와 컴포지션으로 개선해보기

import java.util.HashSet;

public class CharacterBadExampleInheritance extends HashSet<Object> {

    public static void main(String... badExampleOfInheritance) {
        BadExampleInheritance badExampleInheritance = new BadExampleInheritance();
        badExampleInheritance.add("Homer");
        badExampleInheritance.forEach(System.out::println);
    }

위의 예제는 매우 부적합한 상속의 예시이다.

  • HashSet을 상속해놓고 BadExampleInheritance에 대한 인스턴스를 생성 후 사용한다.
  • 즉 하위클래스인 CharacterBadExampleInheritance는 사용하지 않은 많은 메서드를 상속받으므로 혼란스럽고 서로 결합도가 높은 코드가 생성된다.

컴포지션으로 바꿔보면

import java.util.HashSet;
import java.util.Set;

public class CharacterCompositionExample{
	static Set<String> set = new HashSet<>();
    
    public static void main(String.. goodExmapleOfComposition){
    	set.add('Homer');
        set.forEach(System.out::println);
    
    }
}
  • 컴포지션으로 사용하면 HashSet을 상속하지 않고 HashSet의 메서드 중 단지 두 가지만 사용하게 된다. 유지하기 쉬운 결합도가 낮은 코드로 될 수 있다.

*참고 : https://github.com/gyoogle/tech-interview-for-developer/blob/master/Language/%5BJava%5D%20%EC%BB%B4%ED%8F%AC%EC%A7%80%EC%85%98(Composition).md , https://dev-cool.tistory.com/22, https://velog.io/@vino661/%EC%83%81%EC%86%8D%EA%B3%BC-%EC%BB%B4%ED%8F%AC%EC%A7%80%EC%85%98%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C

profile
Drill처럼 파고들자 🔥

0개의 댓글