PHP的最新发行版本中已经实现了对迟绑定的支持。当然,在使用其迟绑定功能时还存在很多问题。如果你使用的是更旧版本的PHP(我的服务器上运行的是PHP 5.0.1版本),那么你可能发现其中缺乏对于迟绑定的支持。因此,请注意本文中的代码有可能无法工作在你特定的PHP 5版本中。
一、 PHP 5和多态性
本文想讨论面向对象编程中最为重要的部分之一--多态性的设计。为了说明问题,我使用了PHP 5。在你继续阅读之前,请首先明确本文并不是完全有关于PHP的。尽管这种语言在以前的两个主要版本中在快速开发方面已经取得很大的进步,但是,在其与更为成熟的语言如C 或者Java相匹敌之前,它对于对象的支持还要经历一段历程。
如果你是一位面向对象编程的入门者,那么本文可能不适合你,因为多态性这部分知识比较特别:一旦理解了它,你将永远不会忘记。如果你想简单了解一点对象编程和设计知识,并且当某人说"某个对象是多态的"时,还不十分清楚这是什么意思的话,那么本文正适合你。
到本文最后,你应该知道什么是多态性以及如何把它应用到面向对象的设计中,并且你会了解PHP 5中对象编程的优点与不足。
二、什么是多态性?
多态性,其来自于dictionary.com的定义是"以不同形式,阶段或者类型出现在独立的组织中或者同种组织中,而不存在根本区别。"由该定义,我们可以认为,多态性是一种通过多种状态或阶段来描述相同对象的编程方式。其实,它的真正意义在于:实际开发中,我们只需要关注一个接口或基类的编程,而不必担心一个对象所属于的具体类(class)。
如果你熟悉设计模式,即使只是有个初步了解,那么你也会了解这个概念。事实上,多态性可能是基于模式设计编程中的最伟大的工具。它允许我们以一种逻辑的方式来组织相类似的对象从而实现在具体编码时不必担心对象的具体类型;而且,我们只需要对一个所期望的接口或基类编程即可。一个应用程序越抽象,则它就显得越灵活--而多态性是对行为加以抽象的最好的方式之一。
例如,让我们考虑一个叫Person的类。我们可以用称为David,Charles和Alejandro的类来子类化Person。Person有一个抽象方法AcceptFeedback(),所有的子类都要实现这个方法。这意味着,任何使用基类Person的子类的代码都能调用方法AcceptFeedback()。你不必检查该对象是一个David还是一个Alejandro,仅知道它是一个Person就够了。结果是,你的代码只需关注"最小公分母"-Person类即可。
在这个示例中的Person类也可以被创建为一个接口。当然,与上面相比存在一些区别,主要在于:一个接口并没有给出任何行为,而仅确定了一组规则。一个Person接口要求的是"你必须支持AddFeedback()方法",而一个Person类可以提供一些AddFeedback()方法的缺省代码-你对之的理解可以是"如果你不选择支持AddFeedback(),那么你应该提供一种缺省实现。"至于如何选择接口或基类则并非本文的主题;但是,一般说来,你需要通过基类来实现一个缺省的方法。如果你能够简单地勾勒出你的类所要实现的一组期望的功能,那么你也可以使用一个接口。
三、应用多态性设计
我们将继续使用Person基类的例子,现在让我们分析一个非多态性的实现。下列示例中使用了不同类型的Person对象--这是一种非常不理想的编程方式。注意,实际的Person类被省略。目前为止,我们仅关心代码调用的问题。
<?php
$name = $_SESSION['name'];
$myPerson = Person::GetPerson($name);
switch (get_class($myPerson)){
case 'David' :
$myPerson->AddFeedback('Great Article!','Some Reader', date('Y-m-d'));
break;
case 'Charles':
$myPerson->feedback[] = array('Some Reader', 'Great Editing!');
break;
case 'Alejandro' :
$myPerson->Feedback->Append('Awesome Javascript!');
break;
default :
$myPerson->AddFeedback('Yay!');
}
?>
这个示例展示了行为不同的对象,还有一个switch语句用于区分不同的Person类对象,从而执行其各自相应的正确操作。注意,这里针对不同条件的回馈注释是不同的。在实际应用程序开发中可能不会出现这种情形;我仅为了简单地说明类实现中存在的区别。
以下の例ではポリモーフィズムを使用しています。
<?php
$name = $_SESSION['name'];
$myperson = person::Getperson($name);
$myperson->AddFeedback('素晴らしい記事!', 'SomeReader', date(' Y-m-d'));
?>
ここには switch ステートメントがないことに注意してください。そして最も重要なことは、 Person::Getperson() が返すオブジェクトの種類についての情報が不足していることです。もう 1 つの Person::AddFeedback() はポリモーフィック メソッドです。動作は具象クラスによって完全にカプセル化されます。ここで David、Charles、または Alejandro を使用するかどうかに関係なく、呼び出し元のコードは具象クラスが何を行うかを知る必要はなく、基本クラスのみを知る必要があることに注意してください。
私の例は完璧ではありませんが、呼び出しコードの観点からポリモーフィズムの基本的な使用法をすでに示しています。次に、これらのクラスの内部実装を分析する必要があります。基本クラスからの派生の優れた点の 1 つは、派生クラスが親クラスの動作にアクセスできることです。これは多くの場合、デフォルトの実装ですが、より複雑な動作を作成するためにクラス継承チェーン内で発生する場合もあります。以下は、この状況の簡単なデモンストレーションです。
<?php
class person{
function AddFeedback($comment, $sender, $date){
//データベースにフィードバックを追加します
}
}
class David extends Person{
function AddFeedback($comment, $sender, $date) ){
parent::AddFeedback($comment, $sender,
date('Y-m-d'));
}
}
?>
ここで、David クラスの AddFeedback メソッド実装は、最初に Person: :AddFeedback を呼び出します。方法。 C、Java、または C# のメソッドのオーバーロードを模倣していることに気づくかもしれません。これは単なる単純化された例であり、作成する実際のコードは実際のプロジェクトに完全に依存することに注意してください。私の意見では、遅延バインディングこそが Java と C# を非常に魅力的なものにしているのです。これらにより、基底クラスのメソッドが "this" または $this を使用してメソッドを呼び出すことができます (これらが基底クラスに存在しない場合や、基底クラスのメソッドの呼び出しが継承クラスの別のバージョンに置き換えられる場合でも)。 PHP では次の実装が許可されていると考えることができます:
$this->ParseFeedback($messageArray); //データベースに書き込む }
}class David extends Person{
function ParseFeedback($messageArray){ // 分析を行います
}
}
?>
覚えておいてください、Person クラスには ParseFeedback はありません。ここで、(この例のために) 実装コードのこの部分があると仮定すると、これにより $myperson が David オブジェクトになります:
<?php
$myperson = person::Getperson($name);
$myPerson ->AddFeedback($messageArray);
?>
解析エラーが発生しました!一般的なエラー メッセージは、ParseFeedback メソッドが存在しない、または同様の情報が存在しないというものです。 PHP 5 の遅延バインディングについて話しましょう。次に、遅延バインディングの概念をまとめてみましょう。
遅延バインディングとは、メソッド呼び出しが最後の瞬間までターゲットオブジェクトにバインドされないことを意味します。これは、実行時にメソッドが呼び出されたときに、それらのオブジェクトがすでに具象型を持っていることを意味します。上の例では、David::AddFeedback() を呼び出しました。 David::AddFeedback() の $this は David オブジェクトを参照しているため、論理的には ParseFeedback() メソッドが存在すると仮定できますが、実際には存在しません。 AddFeedback() が Person で定義されており、ParseFeedback() が Person クラスから呼び出されるために存在します。
残念ながら、PHP 5 ではこの動作を排除する簡単な方法はありません。これは、柔軟なポリモーフィックなクラス階層を作成したい場合には、少し無力になる可能性があることを意味します。
私がこの記事の表現言語として PHP 5 を選択したことを指摘しなければなりません。その理由は単純です: この言語はオブジェクト概念の完全な抽象化を実現していないからです。 PHP 5 はまだベータ版であるため、これは当然のことです。さらに、抽象クラスとインターフェイスが言語に追加されたので、遅延バインディングも実装する必要があります。
この時点で、ポリモーフィズムとは何か、そしてなぜ PHP 5 がポリモーフィズムの実現に完璧ではないのかについて、基本的な理解を得る必要があります。一般に、多態性オブジェクト モデルを使用して条件付き動作をカプセル化する方法を理解しておく必要があります。もちろん、これによりオブジェクトの柔軟性が向上し、実装するコードが少なくなります。さらに、(オブジェクトの状態に応じて) 特定の条件を満たす動作をカプセル化することで、コードの明瞭さが向上します。