インターフェイス

クラスとオブジェクト

インターフェイスとは?

 状態:-  閲覧数:6,366  投稿日:2010-05-29  更新日:2013-04-18  
・「インターフェイス」と「抽象メソッド」のみ定義可能なクラス
※インターフェイス定数 … クラス定数の様なもの
定義できるメソッドが抽象メソッドのみなので(通常メソッドは定義できない、クラスメソッド定義はできる)、「abstract修飾子」は要省略(書くとエラー)。

また、可視性は特性上常に「パブリック」なため、アクセス修飾子には「public」しか指定できない。



直接インスタンスを生成できないため、継承利用するが、インターフェイスの場合、継承とは呼ばず、「実装」と呼ぶ。 → 「インターフェイス」は、クラスを“継承”するのではなく“実装”する。
“実装”は「extends」ではなく「implements」を利用。
複数「インターフェイス」も指定できる。



実装したクラスは、全てのメソッドをオーバーライド&実装しなくてはならない。また、引数の数も名前も完全に一致している必要がある。



 
interface IProduct{
   public function applyPriceDown();

   public function getPrice();    

   public function setPrice($price);
}

class Product implements IProduct{
   public function applyPriceDown(){
       // 処理を記述
   }

   public function getPrice(){
       // 処理を記述
   }    

   public function setPrice($price){
       // 処理を記述
   }
}


▼結果(定義しただけなので、出力結果なし)

/demo/interface_01.html






インターフェイスが、「メソッド存在を保証する役目」を果たしている例。

カートクラスで定義している二つのメソッド(addProduct、getTotalPrice)は、商品クラスインスタンスに二つのメソッド(applyPriceDown、getPrice)が定義されている前提で成り立っている。



商品クラスには、これらのメソッド(applyPriceDown、getPrice)が確実に存在することが保証されていなければならない(存在しなければ、エラーとなるため)



商品クラスがインターフェイス(この場合はIProduct)を実装していれば、そのインターフェイスに定義されているメソッドは100%存在することになる。
 
interface IProduct{
   public function applyPriceDown();

   public function getPrice();    

   public function setPrice($price);//利用しない
}

class DiscountDVD implements IProduct{// ディスカウントDVDクラス
   private $price = 1000;//価格は、1,000円固定

   public function applyPriceDown(){
       $this->price = $this->price * 0.7;// 常に3割引
   }

   public function getPrice(){
       return $this->price;
   }

   public function setPrice($price){//利用しない
}

}

class MonthlyDVD implements IProduct{// マンスリーDVDクラス
   private $price = 1000;//価格は、1,000円固定

   public function applyPriceDown(){
       if ('29' == date('d')) {// 29日なら半額
           $this->price = $this->price / 2;
       }
   }

   public function getPrice(){
       return $this->price;
   }

   public function setPrice($price){//利用しない
}

}

class DVD implements IProduct{// DVDクラス
   private $price = 1000;//価格は、1,000円固定

   public function applyPriceDown(){// 値引きなし
   }

   public function getPrice(){
       return $this->price;
   }

   public function setPrice($price){//利用しない
}

}

class Cart{//カートクラスを定義
   private $products = array();// 商品インスタンス保持用配列

   public function addProduct($product){//商品追加用のメソッド(引数は、商品クラスのインスタンス)
       $product->applyPriceDown();//このインスタンスのapplyPriceDownメソッドを実行して値引きの適用を行う
       $this->products[] = $product;//プロパティである配列に追加
   }

   public function getTotalPrice(){//カート内商品の合計価格を取得するメソッド
       $total = 0;
       foreach ($this->products as $product) {//productsプロパティに格納されている全ての商品に対して
           $total += $product->getPrice();//getPriceメソッドによって価格を取得し、合計している
       }
       return $total;
   }        
}

$cart = new Cart();// カート

// 商品
$discountdvd = new DiscountDVD();
$monthlydvd = new MonthlyDVD();
$dvd = new DVD();

// カートに商品追加
$cart->addProduct($discountdvd);
$cart->addProduct($monthlydvd);
$cart->addProduct($dvd);

$total = $cart->getTotalPrice();// 現在の合計価格を取得

var_dump($total);//結果、DiscountDVDが三割引きされ、700(DiscountDVD) + 1000(MonthlyDVD) + 1000(DVD) = 2700 という結果になる。もし、29日に実行すれば、700 + 500 + 1000 = 2200 という結果になる。if ('29' == date('d')) 部分を今日の日付へ変更しても、2200という結果を得られる。


▼結果

/demo/interface_02.html






「インターフェイス」とそれを実装したクラスは、継承関係にあるわけではないので、インターフェイス定数を「parent::I1_CONST」で参照することはできない。  

 
   interface I1{//「I1」「インターフェイス」を定義
       //protected $property; //「インターフェイス」ではプロパティを定義できない ⇒ Fatal error: Interfaces may not include member variables
       const I1_CONST = "I1インターフェイス定数";
       //abstract public function m1();//「インターフェイス」では、「abstract修飾子」は要省略 ⇒ Fatal error: Access type for interface method I1::m1() must be omitted
public function m1();//定義出来るメソッドが抽象メソッドのみなので、「abstract修飾子」は要省略
       //abstract protected function protected_method();//「パブリック」以外の可視性は不可)⇒ Fatal error: Access type for interface method I1::protected_method() must be omitted
       //public function not_interface_method(){}//「インターフェイス」では、通常のメソッド定義(処理部を記述)はできない)⇒ Fatal error: Interface function I1::not_interface_method() cannot contain body
   }
   
   interface I2{
       function m2();
       function m3($arg1, $arg2 = "");
       static function m4();
   }
   
   #クラスは、全てのメソッドをオーバーライドして実装しなくてはならない ⇒ Fatal error:  Class C0 contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (I2::m3, I2::m4)
   //class C0 implements I2{
   //    public function m2(){}
   //}
       
   class C1{
       const C1_CONST = "C1クラス定数";
   }
   
   #「複数インターフェイス実装」 + 「クラス継承」
   class C2 extends C1 implements I1, I2{
       //const I1_CONST = "インターフェイス定数1";//継承することはできません ⇒ 「インターフェイス定数」は「クラス定数」として再定義できない ⇒ Fatal error:  Cannot inherit previously-inherited constant I1_CONST from interface I1
       const C1_CONST = "C2で再定義した「C1クラス定数」";
       public function __construct(){
           echo "self::C1_CONST    : ", self::C1_CONST, "\n";
           echo "parent::C1_CONST  : ", parent::C1_CONST, "\n";
           echo "C1::C1_CONST    : ", C1::C1_CONST, "\n";
           echo "self::I1_CONST   : ", self::I1_CONST, "\n";
           echo "parent::I1_CONST : "/*, parent::I1_CONST*/, "\n";//未定義のクラス定数 ⇒ Fatal error:  Undefined class constant 'I1_CONST'
           echo "I1::I1_CONST  : ", I1::I1_CONST, "\n";
       }
       public function m1(){
           //
       }
       public function m2(){
           //
       }
       public function m3($a1, $a2 = "air"){
           //
       }
       static public function m4(){
           //
       }
       private function air_method(){
           //
       }
   }
   
   new C2;


▼結果

/demo/interface1.html







「インターフェイス」は「extends」を使って、他の「インターフェイス」を継承して拡張することができる。また、その場合は、「,(コンマ)」で区切って複数指定できる。

 
   interface I1{
       const I1_CONST = "インターフェイス1定数";
       function m1();
   }
   
   interface I2{
       const I2_CONST = "インターフェイス2定数";
       function m2();
   }
   
   #「インターフェイス」を継承
   interface I3 extends I1, I2{
       // const I1_CONST = "ダミー"; //「インターフェイス定数」再定義は不可 ⇒ Fatal error:  Cannot inherit previously-inherited constant I1_CONST from interface I1
   }
   
   #「インターフェイス」実装
   class I4 implements I1, I2{
       public function m1(){
           //
       }
       public function m2(){
           //
       }
   }
   
   #「インターフェイス」実装
   class C1 implements I3{
       public function m1(){
           //
       }
       public function m2(){
           //
       }
   }


▼結果

/demo/interface2_extends.html







「インターフェイスを実装したクラス」を継承したクラスも、「インターフェイスを継承したインターフェイス」を実装したクラスも、そのクラスは「継承元のインターフェイスを実装」していると見做される。
 
   #「インターフェイス」の定義
   interface I1{
       //
   }
   interface I2 extends I1{//インターフェイスを継承したインターフェイス
       //
   }
   
   #「インターフェイス」の実装
   abstract class C1Base implements I1{//インターフェイスを実装したクラス
       //
   }
   class C2 extends C1Base{//「インターフェイスを実装したクラス」を継承したクラス
       //
   }
   class C3 implements I2{//「インターフェイスを継承したインターフェイス」を実装したクラス
       //
   }
   
   #オブジェクトが実装する「インターフェイス」を調べる
   $imp_arr["C2] = class_implements(new C2);//「class_implements()」関数 ⇒ 「指定したオブジェクトが実装するインターフェイス名を配列に格納して返す」
   $imp_arr["C3] = class_implements(new C3);
   
   print_r($imp_arr);


▼結果

/demo/interface3.html







オブジェクト引数のインターフェイス指定

「関数」や「クラスのメンバ関数」の定義時には、「オブジェクト引数のクラス指定」だけではなく「オブジェクト引数のインターフェイス指定」もできる。この場合、渡されたオブジェクトが、指定された「インターフェイス」を実装しているか否かがチェックされる。
 
   interface I1{}
   interface I2{}
   interface I3 extends I1, I2{}//複数インターフェイスを継承したインターフェイス
   interface I4{}
   
   abstract class C1Base implements I3{}//インターフェイスを実装したクラス
   class C1 extends C1Base{}//「インターフェイスを実装したクラス」を継承したクラス
   
   function f1(C1Base $obj){//②
       echo '$obj は、\'C1Base\'のインスタンスじゃなきゃダメ。', "\n";
       echo '$obj は、\'', get_class($obj), "'のインスタンス。\n\n";
   }
   function f2(I1 $obj){//④
       echo '$obj は、\'I1\'インターフェイスの実装じゃなきゃダメ。', "\n";
       echo '$obj implements...', "\n";
       print_r(class_implements($obj));
   }
   function f3(I4 $obj){
       echo '$obj must implement interface \'I4\'.', "\n";
       echo '$obj implements...', "\n";
       print_r(class_implements($obj));
   }
   
   f1(new C1);//①
   f2(new C1);//③
   //f3(new C1); //インターフェイス「I4」を実装していなければならない ⇒ Catchable fatal error:  Argument 1 passed to f3() must implement interface I4, instance of C1 given, called


▼結果

/demo/interface4_object.html






定義済みのインターフェイス
PHPには予め幾つかの「インターフェイス」が定義されている。
定義済みの「インターフェイス」を確認してみる。
 
   #定義済み「インターフェイス」と定義されるメソッド名を取得・出力
   foreach(get_declared_interfaces() as $interface_name)//get_declared_interfaces  ? 宣言されている全てのインターフェースの配列を返す
       $interfaces[$interface_name] = get_class_methods($interface_name);//get_class_methods ? クラスメソッドの名前を取得
   
   print_r($interfaces);
   
   echo "
";
   
   #定義済み「インターフェイス」を実装する定義済みクラス名を抽出・出力
   foreach(get_declared_classes() as $class_name){
       $buf_arr = array();
       $class_abj = new ReflectionClass($class_name);
       $imp_arr = $class_abj->getInterfaces();
       foreach($imp_arr as $imp_obj){
           $imp_arr_ = get_object_vars($imp_obj);
           $buf_arr[] = $imp_arr_["name];
       }
       if(count($buf_arr))
           $arr[$class_name] = $buf_arr;
   }
   
   print_r($arr);


▼結果

/demo/interface5_already.html




抽象メソッド/抽象クラス

カプセル化

コメント投稿(ログインが必要)



週間人気ページランキング / 11-16 → 11-22
順位 ページタイトル抜粋 アクセス数
1 ブラウザを閉じたらセッションデータはどうなるの? | セッション 8
2 Parse error: syntax error, unexpected 'public' (T_PUBLIC) | Parse error(エラーメッセージ) 5
3 Fatal error: Access level to ▲::$△ must be protected (as in class ●) or weaker | Fatal error(エラーメッセージ) 4
3 Warning: strlen() expects parameter 1 to be string, array given in ○○.php on line △△ | Warning(エラーメッセージ) 4
4 PHPで定数を定義する方法は2種類ある / 配列定数の定義 3
4 インポートするデータを受信できませんでした。ファイル名が送信されていないか、ファイルサイズが PHP の設定で許可された最大値を超えています。FAQ 1.16 をご覧ください | エラーメッセージ 3
4 ブラウザを閉じたらセッションデータはどうなるの? | セッション 3
4 クラスの継承 | クラスとオブジェクト 3
5 curl で Cookie を使用する 2
5 例外処理 | 制御構造 2
5 Fatal error: Uncaught Error: Call to a member function modify() on string | Fatal error(エラーメッセージ) 2
5 関数定義内での「外部ファイル読込」 | 制御構造 2
5 set_error_handler | 例外処理(制御構造) 2
5 ( ! ) Fatal error: Uncaught PDOException: SQLSTATE[HY093]: Invalid parameter number: parameter was not defined | Fatal error(エラーメッセージ) 2
5 コード例 … 「例外処理」はネストすることができる 2
5 PHP用語 2
5 register_shutdown_function | 関数処理 関数 2
6 クロージャ | 関数 1
6 session.cookie_lifetime / session.use_cookies | セッション 1
6 セッション管理が必要な理由は、HTTPプロトコルには状態を保持する機能がないため | セッション 1
2024/11/23 1:01 更新