인터페이스를 통한 다형성
Coddy PHP 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 91개 중 34번째.
이전 레슨에서는 자식 클래스가 부모 메서드를 재정의하는 상속을 통해 다형성을 살펴보았습니다. 인터페이스는 특히 공통 동작을 공유하는 서로 관련 없는 클래스와 작업할 때 다형성을 구현하는 또 다른 강력한 방법을 제공합니다.
interface를 사용하면 다형성은 계약 자체에서 비롯됩니다. interface를 구현하는 모든 class는 상속 계층 구조와 관계없이 특정 method를 갖고 있음을 보장합니다:
<?php
interface Notifiable {
public function send(string $message);
}
class EmailNotifier implements Notifiable {
public function send(string $message) {
return "Email: " . $message;
}
}
class SMSNotifier implements Notifiable {
public function send(string $message) {
return "SMS: " . $message;
}
}
class PushNotifier implements Notifiable {
public function send(string $message) {
return "Push: " . $message;
}
}
function notify(Notifiable $notifier, string $message) {
return $notifier->send($message);
}
echo notify(new EmailNotifier(), "Hello") . "\n";
echo notify(new SMSNotifier(), "Hello") . "\n";
echo notify(new PushNotifier(), "Hello");
출력:
Email: Hello
SMS: Hello
Push: Hellonotify() 함수는 모든 Notifiable 객체와 함께 작동합니다. 이러한 클래스들은 부모 클래스를 공유하지 않으며, 동일한 인터페이스를 구현한다는 점을 제외하면 서로 완전히 관련이 없습니다. 이것이 상속 기반 다형성에 비해 갖는 핵심적인 장점입니다. 즉, 클래스의 계보가 아니라 기능에 따라 서로 관련 없는 클래스들을 그룹화할 수 있습니다.
핵심 요점: 인터페이스 기반 다형성을 사용하면 서로 관련이 없는 클래스도 공유된 동작을 바탕으로 서로 바꿔 사용할 수 있으므로, 코드를 더 유연하게 만들고 특정 구현에 대한 결합도를 낮출 수 있습니다.
챌린지
쉬움인터페이스 기반 다형성을 보여 주는 저장 시스템을 만들어 보겠습니다. 파일 시스템과 데이터베이스라는 서로 다른 저장 백엔드를 만들 것입니다. 이 둘은 동일한 저장 인터페이스를 구현한다는 점 외에는 공통점이 없습니다. 하나의 함수가 어떤 저장 유형과도 작동하도록 하여, 인터페이스가 전혀 관련 없는 클래스 간에 다형성을 어떻게 가능하게 하는지 보여 줍니다.
코드를 네 개의 파일로 구성합니다.
Storable.php: 두 가지 메서드 시그니처인save($key, $data)와retrieve($key)를 포함하는Storable인터페이스를 Define합니다. 이 계약은 내부적으로 어떻게 구현되었는지와 관계없이 모든 저장 시스템이 데이터를 저장하고 retrieve할 수 있도록 보장합니다.FileStorage.php:Storable을 implements하는FileStorageclass를 Create합니다. 인터페이스 파일을 포함합니다. class에는 constructor를 통해 설정되는 private$directoryproperty가 있어야 합니다.save($key, $data)를 Implement하여"Saving '[data]' to file [directory]/[key].txt"를 return합니다.retrieve($key)를 Implement하여"Reading from file [directory]/[key].txt"를 return합니다.DatabaseStorage.php: 역시Storable을 implements하는DatabaseStorageclass를 Create합니다. 인터페이스 파일을 포함합니다. class에는 constructor를 통해 설정되는 private$tableNameproperty가 있어야 합니다.save($key, $data)를 Implement하여"Inserting '[data]' into table [tableName] with key [key]"를 return합니다.retrieve($key)를 Implement하여"Selecting from table [tableName] where key = [key]"를 return합니다.main.php: 두 storage 파일을 모두 포함합니다.storeData라는 function을 Create합니다. 이 function은Storableparameter, key, 그리고 data를 accepts합니다. function은 key와 data를 사용해save()를 calling한 result를 return해야 합니다.fetchData라는 또 다른 function을 Create합니다. 이 function은Storableparameter와 key를 accepts하고,retrieve()를 calling한 result를 return합니다.
네 가지 input을 받습니다. directory path, table name, key, 그리고 저장할 data입니다. directory를 사용해 FileStorage를 Create하고 table name을 사용해 DatabaseStorage를 Create합니다. storeData() function을 사용하여 먼저 file storage로 data를 save한 다음 database storage로 save합니다. 각 result를 한 줄에 하나씩 print합니다. 그런 다음 fetchData()를 사용해 file storage에서 data를 retrieve하고 해당 result를 print합니다.
storeData()와 fetchData()가 두 storage type 모두에서 동일하게 작동하는 방식을 확인해 보세요. 이 function들은 object가 Storable을 implements한다는 점만 중요하게 여깁니다. file system과 database는 내부 구현이 완전히 다르지만, interface 계약을 통해 서로 교체하여 사용할 수 있습니다.
직접 해보기
<?php
require_once 'FileStorage.php';
require_once 'DatabaseStorage.php';
// 입력 읽기
$directory = trim(fgets(STDIN));
$tableName = trim(fgets(STDIN));
$key = trim(fgets(STDIN));
$data = trim(fgets(STDIN));
// TODO: Storable, key, data를 받는 storeData 함수를 만드세요
// key와 data로 save()를 호출한 결과를 반환해야 합니다
// TODO: Storable와 key를 받는 fetchData 함수를 만드세요
// key로 retrieve()를 호출한 결과를 반환해야 합니다
// TODO: directory로 FileStorage 인스턴스를 만드세요
// TODO: table name으로 DatabaseStorage 인스턴스를 만드세요
// TODO: storeData()를 사용해 파일 스토리지로 데이터를 저장한 다음 결과를 출력하세요
// TODO: storeData()를 사용해 데이터베이스 스토리지로 데이터를 저장한 다음 결과를 출력하세요
// TODO: Use fetchData() to retrieve from file storage, then print the result
?>이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: 온라인 PHP 컴파일러