Adapter Pattern in PHP

Programming 2006/12/20 23:03
사용자 삽입 이미지

Adapter Kit








Head First Design Pattern[각주:1] 에서는 어댑터 패턴을 아래와 같이 정의하고 있습니다.

어댑터 패턴 (Adapter Pattern) - 한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환합니다. 어댑터를 이용하면 인터페이스의 호환성 문제 때문에 같이 쓸 수 없는 클래스들을 연결해서 쓸 수 있습니다.

디자인 패턴에 관한 책을 읽어보긴 했어도, 실제 예가 없어서인지 감이 잘 오지 않았었습니다. 이번에 devshed 에 나온 아티클[각주:2] 을 읽고서 다시 패턴책을 보니 훨씬 이해가 잘 되는 것 같습니다.

어댑터 패턴은 다음과 같은 경우에 적용될 수 있습니다.
  1. 기존의 클래스를 확장해야 하는데, 상속을 할 수 없는 경우.
  2. 하나의 인터페이스를 다른 인터페이스로 변환
1.
devshed 의 경우 첫번째 경우를 PHP 코드로 설명하고 있습니다. 먼저 추상 클래스와 이를 상속받아 구현한 클래스를 선언합니다.

abstractclass AbstractDirectoryProcessor {
   private $dirPath;
   abstract public function fetchDirContent();
   abstract public function getDirInfo();
}

class DirectoryProcessor extends AbstractDirectoryProcessor {
   public function __construct($dirPath) {
       if(!is_dir($dirPath)) {
           throw new Exception('Invalid directory path!');
       }
       $this->dirPath=$dirPath;
   }
         
   public function fetchDirContent() {
       if(!$dp=opendir($this->dirPath)) {
           throw new Exception('Error opening selected directory!');
       }        
       $dircont='';        
       while($file=readdir($dp)) {
           $dircont.=$file.'<br />';     
       }         
       fclose($dp);        
               
       return $dircont;
   }
        
   public function getDirInfo() {
       $pathinfo=pathinfo($this->dirPath);        
       return 'Name of selected directory is '.$pathinfo['dirname'].' and its base name is the following: '.$pathinfo['basename'];
   }
}

'->fetchDirContent()' 메쏘드는 해당 디렉토리에 있는 파일들의 이름을 하나씩 출력하고, '->getDirInfo()' 메쏘드는 해당 디렉토리에 대한 정보를 간단하게 출력합니다. 자, 이제 이 클래스를, 상속을 이용하지 않고 어댑터 패턴을 이용하여 기능을 확장하도록 하겠습니다.

abstract class AbstractDirectoryProcessorAdapter {
     private $dirProcessor;
     abstract public function getDetailedDirInfo();
}

class DirectoryProcessorAdapter extends AbstractDirectoryProcessorAdapter {
   public function __construct(DirectoryProcessor $dirProcessor) {
       $this->dirProcessor=$dirProcessor;
   }
 
   public function getDetailedDirInfo() {
       return '<h1>Detailed information about selected directory is as follows:</h1><h2>Contents of selected directory is as  follows:</h2>'.$this->dirProcessor->fetchDirContent().'<h2>Data about selected directory is as follows:</h2>'.$this->dirProcessor->getDirInfo();
   }
}

트릭은 생성자에 있습니다. 어댑터의 생성자에서 어댑티 (인터페이스가 변활될 클래스) 를 자신의 속성으로 지정하여, 이후 확장된 메쏘드들에서 어댑티의 메쏘드를 사용합니다.

2.
Head First Design Pattern 의 어댑터 패턴 부분에 보면, 어댑터 패턴을 통해 기능을 확장하기 보다는 기존 클래스의 메쏘드들을 그대로 재정의하는 예제가 있습니다.

class MallardDuck {
   public function quack() {
       echo 'Quack';
   }

   public function fly() {
       echo "I'm flying";
   }
}

class WildTurkey {
   public function gobble() {
       echo 'Gobble gobble';
   }

   public function fly() {
       echo "I'm flying a short distance";
   }
}

class TurkeyAdapter {
   public function __construct(WildTurkey $turkey) {
       $this->turkey = $turkey;
   }

   public function quack() {
       $this->turkey->gobble();
   }

   public function fly() {
       for ($i=0; $i<5; $i++) {
            $this->turkey->fly();
       }
   }
}

만약 devshed 예제 코드를 이용해서 DirectoryProcessor 를 인자로 받는 어떤 함수를 새로 정의했다고 해보겠습니다. 하지만 어떤 이유에서인지 이 함수에서는 '->getDirInfo()' 메쏘드를 사용하는 대신에 '->getSimpleDirInfo()' 라는 메쏘드를 호출합니다. 자, DirectoryProcessor 도 이미 다른 파일들에서 사용중이므로 메쏘드 이름을 바꿀 수는 없고, 함수 내용도 바꿀 수가 없습니다. 이럴때 어댑터 패턴을 사용할 수 있습니다. 어댑터 패턴에서는 간단히 '->getSimpleDirInfo()' 메쏘드를 정의하고, 이 메쏘드에서는 어댑티의 '->getDirInfo()' 를 호출해 주면 됩니다.


Head First Design Pattern 에서는, 이 외에도 유사한 패턴으로 다음 패턴들이 있다고 합니다.
  1. 데코레이터 패턴 - 인터페이스는 바꾸지 않고 책임(기능)만 추가
  2. 퍼사드 (Facade) 패턴 - 인터페이스를 간단히 바꿈
  1. 스토리가 있는 패턴 학습법 - Head First Design Pattern, 에릭 프리먼 외, 서환수 역, 한빛미디어, 2005 [본문으로]
  2. The Basics of Implementing Adapter Objects with PHP, Alejandro Gervasio [본문으로]

'Programming' 카테고리의 다른 글

Symfony Adjacent List  (0) 2007/01/03
AJAX Post-It 만들기  (1) 2006/12/29
Building Protein List using BeautifulSoup and BioPython  (0) 2006/12/27
Symfony Form Helper - object_select_tag  (0) 2006/12/24
Adapter Pattern in PHP  (0) 2006/12/20
Symfony v1 beta 2 has been released  (0) 2006/12/20
Trackback 0 : Comment 0