개요

오늘은 객체 지향 프로그래밍(OOP)의 핵심 개념인 상속(Inheritance)과 복합(Composition)에 대해 설명해드리겠습니다. 이 두 개념은 코드 재사용과 유지보수성을 높이는 중요한 설계 방식입니다. 예제와 함께 특징을 살펴보며 공부해도록 하겠습니다.

 

 

 

상속

상속은 OOP에서 가장 기본개념중에 하나입니다. 부모 - 자식 클래스로 정의하여 자식클래스에서 부모 클래스의 메소드를 override, 재사용 할 수 있다는 특징이 있습니다.

 

@AllArgsConstructor
public class Car {

    private final String brand;
    private final String model;
    private final int year;

    public void start() {
        System.out.println("차량이 시동됩니다.");
    }

    public void stop() {
        System.out.println("차량이 정지합니다.");
    }

}

 

public class ElectricCar extends Car {

    private int batteryCapacity;
    private int batteryLevel;

    public ElectricCar(String brand, String model, int year, int batteryCapacity) {
        super(brand, model, year);
        this.batteryCapacity = batteryCapacity;
        this.batteryLevel = 100;
    }

    @Override
    public void start() {
        System.out.println("전기차가 조용히 시동됩니다.");
    }

    public void charge() {
        System.out.println("배터리를 충전합니다.");
        this.batteryLevel = 100;
    }
    
}

 

ElectricCar 클래스는 Car 클래스를 상속받고있습니다. 따라서 Car와 ElectricCar는 부모-자식 관계입니다.

ElectricCar는 Car의 기능을 재사용하거나 새로운 기능을 손쉽게 추가할 수 있고, 때에 따라서 Override 할 수 있습니다. 그리고 Car 에서 변경된 사항은 ElectricCar에도 그대로 반영됩니다.

 

 

 

복합

@AllArgsConstructor
public class Engine {

    private final String type;
    private final int hp;

    public void start() {
        System.out.println(type + " 엔진이 가동됩니다.");
    }

}

 

@AllArgsConstructor
public class GasCar {

    private final Car car; // 복합관계
    private final Engine engine; // 복합관계

    public void start() {
        car.start();
        engine.start();
    }
}

 

Engine 클래스와 GasCar 클래스를 구현했습니다. GasCar는 Car와 Engine을 필드에서 참조하고있습니다.

이처럼 GasCar가 Car를 필드로 가지는 것이 복합관계라고합니다. GasCar를 통해 Car 메소드를 호출하는 방식이죠.

 

 

상속의 장단점

장점

  1. 코드 재사용성
    • 부모 클래스의 기능을 그대로 물려받아 사용할 수 있어 코드 중복을 줄일 수 있습니다.
    • 공통 기능을 부모 클래스에 구현하면 모든 자식 클래스가 활용할 수 있습니다.
  2. 간단한 확장성
    • 기존 클래스를 수정하지 않고도 새로운 기능을 추가할 수 있습니다.
  3. 다형성 구현
    • 부모 타입으로 자식 객체를 참조할 수 있어 유연한 프로그래밍이 가능합니다.
Car car = new ElectricCar("Tesla", "Model 3", 2024, 75);

 

단점

  1. 강한 결합도
    • 부모 클래스의 변경이 모든 자식 클래스에 영향을 미칩니다.
    • 부모 클래스의 내부 구현을 자식 클래스가 알아야 할 수 있습니다.
  2. 취약한 기반 클래스 문제
    • 부모 클래스의 변경이 예상치 못한 자식 클래스의 동작 변경을 일으킬 수 있습니다.
  3. 단일 상속 제한
    • Java는 다중 상속을 지원하지 않아 한 클래스만 상속할 수 있습니다.

 

복합의 장단점

장점

 

  1. 유연한 설계
    • 런타임에 구성요소를 변경할 수 있습니다.
    • 다중 복합이 가능합니다.
  2. 캡슐화 향상
    • 내부 구성요소의 구현을 완전히 숨길 수 있습니다.
    • 인터페이스를 통한 통신이 가능합니다.
  3. 느슨한 결합도
    • 각 클래스가 독립적으로 동작하여 유지보수가 용이합니다.
// Engine을 쉽게 교체할 수 있음
public void updateEngine(Engine newEngine) {
    this.engine = newEngine;
}

 

 

 

단점

  1. 코드 가독성
    • 복잡한 구성의 경우 코드 추적이 어려울 수 있습니다.
  2. 구현의 복잡성
    • 여러 클래스를 조합하여 사용하므로 초기 설계가 더 복잡할 수 있습니다.
// 복합 관계에서는 위임 메서드를 많이 작성해야 할 수 있음
public class GasCar {
    private Car car;
    private Engine engine;
    
    public void start() { car.start(); }
    public void stop() { car.stop(); }
    public void accelerate() { engine.accelerate(); }
    // ... 더 많은 위임 메서드들
}

 

 

결론

상속과 복합에 대한 글을 읽다보면 'IS-A' 일때는 상속, 'HAS-A' 일때는 복합을 사용하라는 글이 많습니다.

'전기차는 차다' -> 상속,

'차는 엔진을 갖고있다' -> 복합,

이렇게 이해하면 조금더 와닿을것 같습니다.

 

최근의 객체지향 설계에서는 "상속보다는 복합을 사용하라"는 원칙이 널리 받아들여지고 있습니다. 복합이 더 유연하고 안전한 설계를 제공하기 때문입니다. 하지만 각각의 상황에 맞는 적절한 선택이 중요합니다.

 

마지막으로 위의 예제를 상속과 복합관계를 이용하여 CasCar를 조금 더 깔끔하게 바꿔보면서 글을 마치겠습니다.

 

@AllArgsConstructor
public class Car {

    private final String brand;
    private final String model;
    private final int year;
    private final Engine engine;

    public void start() {
        System.out.println("차량이 시동됩니다.");
        engine.start();
    }

    public void stop() {
        System.out.println("차량이 정지합니다.");
    }

}

public class GasCar extends Car {

    private final int fuelCapacity;
    private double currentFuel;

    public GasCar(String brand, String model, int year, Engine engine, int fuelCapacity) {
        super(brand, model, year, engine);
        this.fuelCapacity = fuelCapacity;
        this.currentFuel = fuelCapacity;
    }

    public void refuel(double amount) {
        currentFuel = Math.min(currentFuel + amount, fuelCapacity);
    }

}

 

차는 엔진을 가지고있다 -> private final Engine engine

가솔린차는 차이다 -> GasCar extends Vehicle

 

+ Recent posts