Menu
Coddy logo textTech

인터페이스 vs 추상 클래스

Coddy PHP 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 91개 중 30번째.

이제 인터페이스와 추상 클래스를 모두 배웠으므로, 각각을 언제 사용해야 하는지 명확히 해보겠습니다. 이들은 비슷해 보일 수 있지만, 서로 다른 목적을 수행하며 뚜렷한 기능을 가지고 있습니다.

특징 인터페이스 추상 클래스
메서드 메서드 시그니처만 가능 추상 메서드와 구체 메서드 모두 가능
속성 상수만 가능 속성을 가질 수 있음
다중 상속 하나의 클래스가 여러 개를 구현할 수 있음 하나의 클래스는 하나만 상속할 수 있음
생성자 허용되지 않음 생성자를 가질 수 있음

서로 관련이 없는 클래스들이 충족할 수 있는 계약을 정의하고 싶을 때 인터페이스를 사용하세요. 예를 들어, PrinterLogger는 공통 조상을 공유하지 않더라도 둘 다 Writable 인터페이스를 구현할 수 있습니다.

공통 코드를 공유하는 관련된 클래스들이 있을 때 추상 클래스를 사용하세요. DogCat 클래스 모두 동일한 eat() 메서드 구현이 필요한 경우, 추상 Animal 클래스를 사용하면 해당 코드를 한 번만 작성할 수 있습니다.

두 가지 방식을 결합할 수도 있습니다. 클래스는 여러 인터페이스를 구현하면서 동시에 추상 클래스를 상속받을 수 있습니다:

<?php
abstract class Animal {
    protected $name;
    
    public function eat() {
        return $this->name . " is eating";
    }
}

interface Swimmable {
    public function swim();
}

class Duck extends Animal implements Swimmable {
    public function __construct($name) {
        $this->name = $name;
    }
    
    public function swim() {
        return $this->name . " is swimming";
    }
}

핵심 포인트: 서로 관련이 없는 클래스들 간의 기능을 정의할 때는 인터페이스를 선택하세요. 관련된 클래스들 간에 코드를 공유해야 할 때는 추상 클래스를 선택하세요.

challenge icon

챌린지

쉬움

추상 클래스와 인터페이스를 언제 사용해야 하는지, 그리고 두 방식을 어떻게 효과적으로 결합하는지 보여주는 차량 시스템을 구축해 보겠습니다.

상속을 통해 공통 속성을 공유하면서도 인터페이스를 통해 추가적인 기능을 얻을 수 있는 시스템을 만들 것입니다. 이는 실제 설계 결정을 반영합니다. 자동차는 차량의 "일종(is-a)"이지만(상속), 동시에 연료를 재충전할 수 있는 "기능(has the capability)"도 가지고 있습니다(인터페이스).

코드를 다음 네 개의 파일로 구성하세요:

  • Refuelable.php — 단일 메서드 시그니처인 refuel($amount)를 가진 Refuelable 인터페이스를 정의합니다. 이 기능은 상속 계층 구조에 관계없이 연료 재충전이 필요한 모든 클래스에 적용될 수 있습니다.
  • Vehicle.php — 모든 차량에 공유되는 기능을 제공하는 추상 클래스 Vehicle을 정의합니다. 보호된(protected) $brand 속성과 보호된 $fuelLevel 속성(0으로 시작)을 가져야 합니다. 생성자는 브랜드 이름을 인자로 받습니다. 브랜드를 반환하는 구체적인 getBrand() 메서드, 현재 연료 수준을 반환하는 구체적인 getFuelLevel() 메서드, 그리고 자식 클래스에서 반드시 구현해야 하는 추상 메서드 drive()를 포함하세요.
  • Car.phpVehicle을 확장하고 Refuelable을 구현하는 Car 클래스를 만듭니다. 상단에 필요한 두 파일을 모두 포함(include)하세요. 생성자는 브랜드를 인자로 받아 부모 생성자에 전달합니다. drive()를 구현하여 "[brand] car is driving"을 반환하도록 합니다. refuel($amount)를 구현하여 연료 수준에 금액을 더하고 "[brand] refueled to [fuelLevel] liters"를 반환하도록 합니다.
  • main.php — Car 파일을 포함합니다. 브랜드 이름과 연료량(정수로 변환)이라는 두 개의 입력을 받게 됩니다. 해당 브랜드로 Car 객체를 생성합니다. 첫 번째 줄에 drive()의 결과를 출력하고, 두 번째 줄에 해당 연료량으로 refuel()을 호출한 결과를 출력하세요.

이 챌린지는 핵심적인 차이점을 설명합니다. 추상 클래스인 Vehicle은 코드를 공유하고 "is-a" 관계를 설정하는 반면, Refuelable 인터페이스는 차량이 아니어도 될 다른 관련 없는 클래스(발전기나 보트 등)에도 적용될 수 있는 기능을 추가합니다.

치트 시트

인터페이스와 추상 클래스는 서로 다른 목적을 수행합니다:

기능 인터페이스 추상 클래스
메서드 메서드 시그니처만 포함 추상 메서드와 구체적인 메서드 모두 포함
속성 상수만 포함 속성을 가질 수 있음
다중 상속 클래스는 여러 개를 구현할 수 있음 클래스는 하나만 상속할 수 있음
생성자 허용되지 않음 생성자를 가질 수 있음

서로 관련이 없는 클래스들에 대한 계약을 정의하려면 인터페이스를 사용하세요. 관련된 클래스들 간에 공통 코드를 공유하려면 추상 클래스를 사용하세요.

클래스는 여러 인터페이스를 구현하는 동시에 하나의 추상 클래스를 상속할 수 있습니다:

<?php
abstract class Animal {
    protected $name;
    
    public function eat() {
        return $this->name . " is eating";
    }
}

interface Swimmable {
    public function swim();
}

class Duck extends Animal implements Swimmable {
    public function __construct($name) {
        $this->name = $name;
    }
    
    public function swim() {
        return $this->name . " is swimming";
    }
}

직접 해보기

<?php
require_once 'Car.php';

// 입력 읽기
$brand = trim(fgets(STDIN));
$fuelAmount = intval(trim(fgets(STDIN)));

// TODO: 브랜드로 Car 객체 생성

// TODO: 첫 번째 줄에 drive() 결과 출력

// TODO: 두 번째 줄에 연료량과 함께 refuel() 결과 출력
?>
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨