Skip to content

Latest commit

 

History

History
193 lines (140 loc) · 5.82 KB

18.상속보다는 컴포지션과 전달을 사용하자. 래퍼클래스가 더 강력하다.md

File metadata and controls

193 lines (140 loc) · 5.82 KB

상속이란?

아래의 예시에 나온 Dog과 Animal의 관계가 바로 상속의 예시이다.

```java
class Animal {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

class Dog extends Animal {
    void sleep() {
        System.out.println(this.name+" zzz");
    }
}

public class Sample {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.setName("poppy");
        System.out.println(dog.name);  // poppy 출력
        dog.sleep();  // poppy zzz 출력
    }
}
```

Dog는 Animal에 포함되기 때문에, “Dog is a Animal”이라고 말할 수 있는 관계를 상속관계라고 이야기한다. 이는 코드를 재사용하고, 계층적인 관계를 구성하는 것에 있어서 효과적으로 사용된다.

구현상속 vs 인터페이스 상속

구현상속: 다른 클래스를 추가로 확장하여 사용하는 의미.

인터페이스 상속 : 확장의 의미보단 인터페이스에 정의된 멤버들을 클래스에서 대신 구현하겠다는 의미.

→ 해당 아이템에서 이야기하는 ‘상속’은 구현 상속에 한정된다.

컴포지션이란?

컴포지션 = 새로운 클래스를 만들어 기존 클래스가 새로운 클래스의 구성요소로 사용되는 설계.

새로운 클래스의 역할

- private 필드로 기존 클래스의 인스턴스 참조
- 기존 클래스의 대응하는 메서드를 호출해 결과 반환 = 전달(forwarding)
- 새 클래스의 메서드는 전달메서드라고 부른다.
  → 이는 내부 구현 방식에서 독립적으로 구성할 수 있다.

상속 vs 컴포지션

  • 상속은 캡슐화를 꺠트리고, 상위 클래스에 의존성 관계를 가지고 있기 때문에 상위클래스의 변화에 따라 하위클래스가 변경될 가능성이 있다. (OCP 규칙에 위반)
  • 메소드는 언제든지 재정의될 가능성이 있음
  • 다른 패키지의 구체클래스를 상속하는 일은 위험하고, 같은 패키지 내에서라도 문제를 야기할수 있다.
  • 보통 IS-A 관계일떄 상속을 사용하고, HAS-A 관계일때 컴포지션을 사용하라고 하지만 이는 잘못된 것( But 관점의 차이일뿐임 CAR IS-A AUTOMOBILE 이기도 하지만, CAR HAS-A ENGINE, WHEEL 관계이기도 함)

예시

  • 상속 코드

    class Animal{
    
        void eat();
    }
    
    class Cat extends Animal{
        void meow();
    }
    
    class Dog extends Animal{
        void bark();
    }
    
    class Robot{
        void move();
    }
    
    class CleaningRobot extends Robot{
        void clean();
    }
    
    class KillingRobot extends Robot{
        void kill();
    }

    위와 같은 코드에서 만약 요구사항이 바뀌어 KillingRobotDog이 필요하게 되면 어떻게될까?

    → 상속은 객체의 특성을 단정짓기 때문에, 상속으로 구성된 구현은 요구사항의 변경에 유연하게 대처하지 못한다.

  • 컴포지션 코드

    아래 코드는 컴포지션 관계로 해당 코드를 변경한 예시이다.

    class Dog{
        BarkingStrategy bark;
        EatingStrategy eat;
    }
    
    class ManyEatingDog{
        BarkingStrategy bark;
        EatingStrategy eat;// eat 구현체를 많이 먹는 것으로 갈아낀다 !
    }
    
    class Cat{
        MeowingStrategy meow;
        EatingStrategy eat;
    }
    
    class CleaningRobot{
        CleaningStrategy clean;
        MovingStrategy move;
    }
    
    class KillingRobot{
        KillingStrategy kill;
        MovingStrategy move;
    }
    
    class KillingRobotDog  {
        KillingStrategy kill;
        MovingStrategy move;
        BarkingStrategy bark;
    }

    행동을 하는 부분을 객체화시키고, 그 안의 Killing Strategy 같은 클래스 내부에서 구현하는 방식으로 진행하는 것이다.

  • 교재의 예시

    public class InstrumentedHashSet<E> extends ForwardingSet<E> {
          
        //추가된 원소의 수
        private int addCount = 0;
          
        public InstrumentedHashSet(Set<E> s){
            super(s);
        }
          
        @Override 
        public boolean add(E e) {
            addCount++;
            return super.add(e);
        }
          
        @Override
        public boolean addAll(Collection<? extends E> c) {
            addCount += c.size();
            return super.addAll(c);
        }
          
        public int getAddCount() {
            return addCount;
        }
          
     }
    public class ForwardingSet<E> implements Set<E> {
        private final Set<E> s;
        public ForwardingSet(Set<E> s) { this.s= s;}
          
        public void clear() {s.clear();}
        public boolean contains(Object o) { return s.contains(o);}
        public boolean isEmpty() { return s.isEmpty();}
        public int size() { return s.size();}
        public Iterator<E> iterator() { return s.iterator(); }
        public boolean add(E e) { return s.add(e); }
        public boolean addAll(Collection<? extends E> c) { return s.addAll(c); }
         
      
    }

래퍼클래스

아래에서 set 인스턴스를 감싸고 있다는 뜻에서 바로 위에 제시된 교재 내의 예시인 InstrumentedSet 과 같은 클래스를 래퍼 클래스라고 하며, 이는 데코레이터 패턴이라고도 불린다.

이런 래퍼 클래스는 단점이 거의 없으나, callback (콜백) 프레임워크와 어울리지 않는다는 점만 주의하면 된다.

정리

  • 상속은 강력하지만 캡슐화를 해치고 + 내부 구현을 불필요하게 노출할 수 있으므로, 확실하게 IS-A 관계일 때에만 사용하자.
  • 컴포지션과 전달을 사용하자.
  • 래퍼 클래스는 충분히 강력한 성능을 가지고 있다.