理解和运用PHP中的多态性[译]_php文摘_脚本之家
在面向对象编程,多态性是一个强大的和基本的工具。它可以用来在您的应用程序建立一个更有机的流。本教程将介绍多态性的笼统的概念,以及它如何在PHP中可以很容易地部署。
什么是多态性?
Polymorphism(多态性)是一个很长的单词,但是它表示的是一个非常简单的概念。
多态性描述了在面向对象编程模式中类有不同的功能,而共享一个通用的接口。
多态性的优点是,并不需要知道它使用的是哪一个类,因为他们都用同样的方式与不同的类的代码工作。
可将多态性类比成现实世界的一个按钮。大家都知道如何使用一个按钮:您只需给它施加压力。一个按钮“确实是这样”,然而,取决于它和什么连接和使用它的上下文 - 但结果并不影响它是如何使用。如果你的老板告诉你按下一个按钮,您已经有执行任务所需的所有信息。
在编程的世界中,多态性是用来使应用程序更加模块化和可扩展的。相比于凌乱的条件语句描述的行动不同的课程,您可以创建根据您的需求选择的互换对象。这是多态性的基本目标。
Interfaces接口
接口是与类(class)类似的,除了它不能包含代码。接口可以定义方法名称和参数,但不是方法的内容。实现接口的任何类都必须实现接口中定义的所有方法。一个类可以实现多个接口。
使用的“interface”关键字声明一个接口:
代码如下:
interface MyInterface {
// methods
}
被附加到一个类,使用“implements”关键字(多个接口,可以通过用逗号分开):
代码如下:
class MyClass implements MyInterface {
// methods
}
在接口中可以像在类中一样定义方法,除了没有方法体(在大括号中的部分)以外。
代码如下:
interface MyInterface {
public function doThis();
public function doThat();
public function setName($name);
}
在这里定义的所有方法都必须如接口中所描述地那样被包含在任何实现它的类中。(读下面的代码注释)
代码如下:
//合法的 VALID
class MyClass implements MyInterface {
protected $name;
public function doThis() {
// code that does this
}
public function doThat() {
// code that does that
}
public function setName($name) {
$this->name = $name;
}
}
// 非法的INVALID
class MyClass implements MyInterface {
// missing doThis()!
private function doThat() {
// this should be public!
}
public function setName() {
// missing the name argument!
}
}
抽象类Abstract Class
抽象类是接口和类的混合。它可以像接口一样定义方法。继承自抽象类的类必须实现抽象类中定义的所有抽象方法。
抽象类的定义方式与类一样,不过是在前面附加了一个abstract 关键字。
代码如下:
abstract class MyAbstract {
// methods
}
并且 是用 ‘extends‘ 关键字附加到类:
代码如下:
class MyClass extends MyAbstract {
// class methods
}
就像在普通类中一样,普通的方法以及任何抽象方法(使用关键字“abstract”)可以在抽象类中定义。抽象方法的行为就像在接口中定义的的方法,而且继承它的扩展类中必须实现完全一样的定义。
代码如下:
abstract class MyAbstract {
public $name;
public function doThis() {
// do this
}
abstract public function doThat();
abstract public function setName($name);
}
我们假设你有一个文章Article类负责管理你网站上的文章。它包含关于文章的信息,包括:title, author, date, and category.
就像下面这样:
代码如下:
class poly_base_Article {
public $title;
public $author;
public $date;
public $category;
public function __construct($title, $author, $date, $category = 0) {
$this->title = $title;
$this->author = $author;
$this->date = $date;
$this->category = $category;
}
}
注意:在这个教程中的示例类使用了“package_component_Class”的命名约定,这是一个用来将类名分隔到虚拟的命名空间来避免命名冲突的通用方法。
现在你想添加一个方法来输出各种不同格式的信息,如XML和JSON。你也许会打算像下面这样做:
代码如下:
class poly_base_Article {
//...
public function write($type) {
$ret = '';
switch($type) {
case 'XML':
$ret = '';
$ret .= '';
$ret .= '' . $obj->author . '';
$ret .= '' . $obj->date . '';
$ret .= '' . $obj->category . '';
$ret .= '';
break;
case 'JSON':
$array = array('article' => $obj);
$ret = json_encode($array);
break;
}
return $ret;
}
}
这种解决方案是丑陋的,但是它是可靠的——至少在现在。问下你自己将来会发生什么,当我们需要添加更多格式的时候?你可以继续编辑这个类,然后添加越来越多的case ,
但是现在你只是在稀释(FIX ME: diluting) 你的类。
OOP的一个重要原则是一个类应该做的一件事情,而应该把它做好。
考虑到这一点,条件语句应该是一个红色的标志,表明你的类是试图做太多不同的东西。这是多态性的用武之地。
在我们的例子中,有两个任务明确的提出:管理文章和格式化其数据。在本教程中,我们将重构我们的格式化代码到一个新的类,然后我们会发现使用多态性是多么容易。
Step 2: 定义你的接口 Define Your Interface
第一件事就是我们应该定义接口,努力想好怎么定义你的接口是件重要的事情,因为对它的任何改变都将需要改动调用它的代码。
在我们这个例子中,我们将使用一个简单的接口来定义一个方法:
代码如下:
interface poly_writer_Writer {
public function write(poly_base_Article $obj);
}
它就是那样简单,我们已经定义了一个公用方法write() ,它接受一个文章对象作为参数。任何实现Writer接口的类都将会确保有这个方法。
小提示:如果你想严格限制传递给你的方法和函数的参数类型,你可以使用类型提示,就像我们已经在write()方法中做的一样,它只能接受poly_base_Article 对象类型的数
据。不幸的是,在目前版本的PHP中,返回类型提示是不被支持的,所以,你要小心返回值的类型了。
Step 3: 创建实现类 Create Your Implementation
定义接口后,该是时候来创建类来真正干活的啦。在我们的例子中,我们需要输出两种格式。这样,我们就要两个Writer 类:XMLWriter 和 JSONWriter 。从传递过来的
Article 对象提取数据然后格式化这些信息完全取决于这些类了。
下面是 XMLWriter 类的一个示例:
代码如下:
class poly_writer_XMLWriter implements poly_writer_Writer {
public function write(poly_base_Article $obj) {
$ret = '';
$ret .= '';
$ret .= '' . $obj->author . '';
$ret .= '' . $obj->date . '';
$ret .= '' . $obj->category . '';
$ret .= '';
return $ret;
}
}
就如你可以从类定义中看到的一样,我们使用implements关键字来实现我们的接口。write() 方法包含格式化为XML的功能。
现在我们来看下JSONWriter 类:
代码如下:
class poly_writer_JSONWriter implements poly_writer_Writer {
public function write(poly_base_Article $obj) {
$array = array('article' => $obj);
return json_encode($array);
}
}
现在,我们的代码中的特定每种格式都包含在单独的类。每个类有全权负责处理特定的格式,而不是其他。您的应用程序中没有其他部分需要关心这些是如何工作的才能使用它,
感谢我们的接口。
Step 4: 使用你的接口Use Your Implementation
在我们的新类定义后,该是时候来重温一下我们的Article类了,所有原write() 方法中的代码已经被分离出来,进入到我们的新类中了。
我们的所有方法现在需要做的就是使用这些新的类,像这样:
代码如下:
class poly_base_Article {
//...
public function write(poly_writer_Writer $writer) {
return $writer->write($this);
}
}
获取一个 Writer对象 Obtaining A Writer
你也许会疑惑你该从哪里获取一个 Writer对象开始,因为你需要传递一个 Writer对象到这个方法。
这完全取决于你,并且,有很多策略。如,你可能会使用工厂类来获取请求数据然后创建一个对象:
代码如下:
class poly_base_Factory {
public static function getWriter() {
// grab request variable
$format = $_REQUEST['format'];
// construct our class name and check its existence
$class = 'poly_writer_' . $format . 'Writer';
if(class_exists($class)) {
// return a new Writer object
return new $class();
}
// otherwise we fail
throw new Exception('Unsupported format');
}
}
就像我说的,根据你的需求,有好多其它策略可用。在这个例子中,通过一个请求变量选择哪种格式是要使用的。它基于request请求变量来构造一个类名,检测它是否存在,
然后返回一个新的Writer对象。如果没有那个名字的类存在,抛出一个异常,让客户端代码决定接下来要干什么。
Step 5: 把它们放一起 Put It All Together
当所有东东都到位了,下面是我们的客户端代码如何放在一起:
代码如下:
$article = new poly_base_Article('Polymorphism', 'Steve', time(), 0);
try {
$writer = poly_base_Factory::getWriter();
}
catch (Exception $e) {
$writer = new poly_writer_XMLWriter();
}
echo $article->write($writer);
首先,我们创建了一个示例 Article 对象来配合工作。然后,我们试图从工厂Factory获取一个Factory对象,如果异常发生的话回滚到默认(XMLWriter) 。
最后,我们传递Writer对象给我们的Article的 write() 方法,输出结果。
结论 Conclusion
在本教程中,我提供了一个多态性的简介而且解释了PHP中的接口。我希望你意识到,我只向您展示一个潜在的使用多态性的案例。
多态性是以一个优雅的方式来避免您的OOP代码中丑陋的条件语句。它遵循的原则是使您的组件分离,而且它是许多设计模式的组成部分。如果您有任何问题,不要犹豫,在评论中提问!
译自:
原文发表在:

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

繼承和多態性會影響類別的耦合度:繼承會增加耦合度,因為衍生類別依賴基底類別。多態性可以降低耦合度,因為物件可以透過虛擬函數和基底類別指標以一致的方式回應訊息。最佳實踐包括謹慎使用繼承、定義公共介面、避免在基底類別中新增資料成員,以及透過依賴注入解耦類別。實戰案例顯示如何使用多態性和依賴注入來降低銀行帳戶應用程式中的耦合度。

C++多態性的優點和缺點:優點:程式碼重用性:通用程式碼可處理不同物件類型。可擴充性:輕鬆新增類,無需修改現有程式碼。靈活性與可維護性:行為與型別分離,提升程式碼彈性。缺點:運行時開銷:虛函數分派導致開銷增加。程式碼複雜度:多繼承層次結構增加複雜度。二進位大小:虛擬函數使用增加二進位檔案大小。實戰案例:動物類層次結構中,多態性使不同的動物物體都能透過Animal指針發出聲音。

介面:無實作的契約介面在Java中定義了一組方法簽名,但不提供任何具體實作。它充當一種契約,強制實作該介面的類別實現其指定的方法。介面中的方法是抽象方法,沒有方法體。程式碼範例:publicinterfaceAnimal{voideat();voidsleep();}抽象類別:部分實作的藍圖抽象類別是一種父類,它提供了一個部分實現,可以被它的子類別繼承。與介面不同,抽象類別可以包含具體的實作和抽象方法。抽象方法是用abstract關鍵字聲明的,並且必須被子類別覆蓋。程式碼範例:publicabstractcla

析構函數在C++多態性中至關重要,它確保衍生類別物件在銷毀時正確清理記憶體。多態性允許不同類型的物件回應相同方法呼叫。析構函數在物件銷毀時自動調用,釋放其記憶體。衍生類別析構函數呼叫基底類別析構函數,確保釋放基底類別記憶體。

多態中,函數傳回值類型規定了當衍生類別重寫基底類別方法時,傳回的具體物件類型。派生類別方法的傳回值類型可以與基底類別相同或更具體,允許傳回更派生的類型,從而提高靈活性。

函數重載可用於實現多態性,即透過基底類別指標呼叫衍生類別方法,編譯器根據實際參數類型選擇重載版本。範例中,Animal類別定義虛擬makeSound()函數,Dog和Cat類別重寫函數,透過Animal*指標呼叫makeSound()時,編譯器會基於指向的物件類型呼叫對應的重寫版本,從而實現多態性性。

多態性是物件導向程式設計中允許物件以多種形式的存在的概念,使程式碼更靈活、可擴展和可維護。 C++中的多態性利用虛擬函數和繼承,以及純虛函數和抽象類別來實現動態綁定,使我們可以創建根據物件的實際類型更改行為的類別層次結構。在實踐中,多態性允許我們建立指向不同衍生類別物件的基底類別指針,並根據物件的實際類型呼叫適當的函數。

函數重寫和繼承的多態性是OOP中實現物件靈活呼叫的兩個關鍵概念:函數重寫:衍生類別重新定義基底類別中的同名函數,在呼叫時執行衍生類別中的具體實作。繼承的多態性:派生類別可以以與基底類別相同的方式使用,透過基底類別引用呼叫方法時,執行派生類別中特定於它的實作。
