Menu
Coddy logo textTech

인터페이스 vs 추상 클래스

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

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

특징 인터페이스 추상 클래스
메서드 메서드 시그니처만 포함 추상 메서드와 구체적인 메서드 모두 포함
속성 상수만 포함 속성을 가질 수 있음
다중 상속 클래스는 여러 개를 구현할 수 있음 클래스는 하나만 상속할 수 있음
생성자 __construct 시그니처를 선언할 수 있음 (본문 없음) 본문이 있는 생성자를 정의할 수 있음

서로 관련이 없는 클래스들이 충족할 수 있는 계약을 정의하고 싶을 때 인터페이스를 사용하세요. 예를 들어, 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 클래스를 생성합니다. 상단에 필요한 두 파일을 포함하세요. 생성자는 브랜드를 인자로 받아 부모 생성자에 전달합니다. drive()를 구현하여 "[brand] car is driving"을 반환하도록 하세요. refuel($amount)를 구현하여 연료 수준에 금액을 더하고 "[brand] refueled to [fuelLevel] liters"를 반환하도록 하세요.
  • main.php — Car 파일을 포함합니다. 브랜드 이름과 연료량(정수로 변환)이라는 두 개의 입력을 받습니다. 해당 브랜드로 Car 객체를 생성합니다. 첫 번째 줄에 drive()의 결과를 출력하고, 두 번째 줄에 연료량을 인자로 전달한 refuel()의 결과를 출력하세요.

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

직접 해보기

<?php
require_once 'Car.php';

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

// TODO: brand를 사용하여 Car 객체 생성

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

// TODO: 두 번째 줄에 fuelAmount를 사용하여 refuel()의 결과 출력
?>
quiz icon실력 점검

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

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