상속과 합성

bbookng·2024년 8월 7일
0

바야흐로 올해 상반기 보았던 모 대기업 면접에서 질문으로 나왔던 내용이다.
"상속과 합성에 대해 설명해주세요."
당시 나는 합성이라는 개념을 처음 들어봤었기 때문에 당연히 대답하지 못했었다 😂
아무튼 덕분에 새로운 개념을 공부하게 되었다 !!


✨ 상속과 합성

  • 객체 지향 프로그래밍에서 가장 널리 사용되는 코드 재사용 기법

1. 상속

  • 슈퍼클래스를 서브클래스가 상속받아 사용하는 것.
  • 슈퍼클래스에서 사용된 필드, 메서드를 서브클래스에서도 동일하게 사용하고 확장할 수 있음.
  • 상위 클래스의 코드를 재사용.
  • Is-a 관계라고 표현.
public class Car {public void go() {  
        System.out.println("go!");  
    }  
}public class SportCar extends Car {public void boost() {  
        System.out.println("boost on!");  
    }  
}public class Main {  
    public static void main(String[] args) {  
        SportCar sportCar = new SportCar();  
        sportCar.go();  
        sportCar.boost();  
          
        // 실행 결과  
        // go!  
        // boost on!  
    }  
}
  • SportCar 클래스는 Car 클래스를 상속받아 기능을 확장.
  • 스포츠 카는 카이다. 이기 때문에 Is-a** 관계로 표현

2. 합성

  • 다른 클래스의 인스턴스를 자신의 클래스의 필드로 가지는 것.
  • 서로 직접적인 관계가 없지만 관계를 맺어주어야 할 때 사용.
  • Has-a 관계로 표현.
  • 두 클래스가 독립적이지만 서로 협력하여 강력한 기능을 구현할 수 있음.
public class Car {  
    private Engine engine;public Car(Engine engine) {  
        this.engine = engine;  
    }public void go() {  
        engine.startEngine();  
        System.out.println("go!");  
    }  
}public class Engine {  
    private String engineType;public Engine(String engineType) {  
        this.engineType = engineType;  
    }public void startEngine() {  
        System.out.println("start engine!");  
    }  
}public class Main {  
    public static void main(String[] args) {  
        Car car = new Car(new Engine("gasolineEngine"));  
        car.go();  
          
        // 실행 결과  
        // start engine!  
        // go!  
    }  
}
  • Car 클래스는 Engine 클래스를 합성하여 필요에 따라 Engine 클래스의 기능을 가져다 쓰고 있음.
  • 카는 엔진을 가지고 있다 이기 때문에 Has-a 관계로 표현.
public class Car {  
    private Engine engine;public Car(Engine engine) {  
        this.engine = engine;  
    }public void go() {  
        engine.startEngine();  
        System.out.println("go!");  
    }  
}public class SportCar extends Car {public SportCar(Engine engine) {  
        super(engine);  
    }public void boost() {  
        System.out.println("boost on!");  
    }  
}public class Engine {  
    private String engineType;public Engine(String engineType) {  
        this.engineType = engineType;  
    }public void startEngine() {  
        System.out.println("start engine!");  
    }  
}public class Main {  
    public static void main(String[] args) {  
        Car car = new Car(new Engine("gasolineEngine"));  
        car.go();SportCar sportCar = new SportCar(new Engine("gasolineEngine"));  
        sportCar.go();  
        sportCar.boost();  
          
        // 실행 결과  
        // start engine!  
        // go!  
        // start engine!  
        // go!  
        // boost on!  
    }  
}
  • 위 코드를 보면, Car 클래스를 상속받은 SportCar 클래스는 Car 클래스의 go 메서드를 활용하며, 자신만의 boost 메서드를 추가하여 기능을 확장한다. 이는 SportCar 가 기본적인 Car 의 기능을 유지하면서도 SportCar 에 특화된 기능을 갖춘하는 것을 의미.

  • Car 클래스는 Engine 클래스와 합성되어 go 메서드가 실행되며 Engine 클래스의startEngine 메서드를 호출한다. 이를 통해 Car 클래스는 Engine 클래스의 기능을 유연하게 활용하며 재사용한다.

  • 위 예시를 통해 상속과 합성은 서로를 대체하는 개념이 아닌, 각기 다른 상황에 맞게 적절하게 함께 사용할 수 있음을 볼 수 있음.

"상속보다는 합성을 사용하라"

  • 상속의 주요 목적 : 다형성

    • 동일한 인터페이스나 부모 클래스를 공유하는 서로 다른 객체들이 각기 다른 방식으로 행동할 수 있도록 하는 기능
    • 상속을 통해 자연스레 코드 재사용의 이점을 얻을 수 있음
  • 그러나 단순히 코드 재사용을 위해 상속을 사용하는 것은 권장되지 않음. 이 경우 합성이 더 적합.

    • 상속이 가져오는 강한 결합 문제가 있기 때문.
    • 부모 클래스의 변경이 자식 클래스에 영향을 미침.
    • 다중 상속 문제를 야기할 수 있음.
  • 그렇다면 코드 재사용을 위한 상속이란 ?

    • 구현 상속 : 클래스의 전체적인 기능을 상속받는 것
    • 인터페이스 상속
      • 인터페이스의 메서드 시그니처만을 상속받으며, 실제 구현은 상속받지 않음.
  • 인터페이스 상속과 합성을 함께 사용하면, 상속의 본래 목적인 다형성을 활용하고 느슨한 결합을 달성할 수 있음.

  • 결론 : 재사용을 위한 상속보다는 합성을 사용하라.


0개의 댓글