第 2 部分 – WordPress 和麵向對象編程:一個真實世界的示例

已發表: 2021-07-29

在我們的 WordPress 和麵向對象編程概述中,我們介紹了面向對象編程 (OOP) 背後的理論以及使用它時的預期。

在我們繼續使用 OOP 進行更具體的編碼示例之前,在本文中,我們將嘗試解釋如何使用 OOP 所需的不同思維方式處理現實世界場景,以及如何使用對象和類進行分析。

真實場景:發送短信

這更像是一個“過去”的生活場景,實際上是現在越來越少使用 SMS,但正如您將看到的,我們使用它作為示例是有原因的!

假設您有一個移動設備,並且您想向您的一個聯繫人發送一條短信。 使示例盡可能簡單,操作順序為:

  1. 準備消息
  2. 選擇您的一位聯繫人並添加為收件人
  3. 發送消息

因此,讓我們嘗試可視化您發送消息所要遵循的步驟:

我們添加了一些更詳細的操作描述,但您所做的或多或少只是 3 個基本步驟。 您在設備編輯器中準備消息,從聯繫人中選擇收件人,然後發送消息。 你完成了! 您的消息現已發送。

現在,如果我們要在代碼中表示一個發送 SMS 消息的應用程序,我們應該分析哪個路由更適合遵循; 程序或 OOP 方法。

採用程序方法的應用程序

如果您是 WordPress 插件開發人員,那麼您很可能熟悉過程式編程

如前所述,過程式編程是一種命令式編程,我們的程序由一個或多個過程組成。 因此,作為開發人員,您將插件分解為一組保存數據的變量,以及對數據進行操作的函數

在上面的 SMS 消息示例中,您將執行一系列操作,從而獲得所需的結果。 您可能已經猜到了,例如,您將擁有一個保存消息文本內容的變量、一個帶有$contact參數的返回電話號碼的函數,最後還有一個發送消息的函數。 在代碼中它看起來像這樣:

 function get_phone_number( $contact ) { // Code that finds the contact's number in the list of contacts return $phone_number; } function send_sms( $contact, $message ) { $phone_number = get_phone_number( $contact ); // Code that sends the message to this number print "Message Sent!"; }

你會像這樣使用它:

 $text = "Hello John"; function send_message( "John Doe", $text );

因此,您將完成一系列任務,這些任務將引導您達到預期的結果。

當然,在這個非常簡單的示例中,它具有有限且非常具體的要求,根本沒有理由考慮使用 OOP。 程序化編程足以實現您的目標。 但是,如果您考慮一些關於此應用程序將來如何擴展的場景,您可能會意識到,從長遠來看,您可能會遇到可伸縮性方面的問題。 我們將嘗試在下面解釋原因。

用程序方法擴展應用程序

假設您想改進此應用程序並提供發送其他類型消息的能力,例如電子郵件。 在每種情況下,傳遞消息的功能都會有所不同。

發送電子郵件時,您需要聯繫人的電子郵件地址,而不是電話號碼。 除此之外,我們需要在最終的send_message()函數中添加一個參數,該參數將對應於我們使用的技術類型; 電子郵件或短信。

相應的代碼可能如下所示:

 function get_phone_number( $contact ) { // Code that finds the contact's number return $phone_number; } function get_email_address( $contact ) { // Code that finds the contact's email address return $email_address; } function send_sms( $contact, $message ) { $phone_number = get_phone_number( $contact ); // Code that sends the message to this number print "SMS Sent!"; } function send_email( $contact, $message ) { $email_address = get_email_address( $contact ); // Code that sends the email to this number print "Email Sent!"; } function send_message( $contact, $message, $technology ) { if ( $technology == "SMS") { send_sms( $phone_number, $message ); } else if ( $technology == "Email") { send_email( $email_address, $message ); } }

因此,這並不是不能用程序方法來實現的。 但是,如果您是一位經驗豐富的開發人員,您可能已經了解這在未來會變得多麼混亂。

程序方法的缺點

如果我們有多種類型的消息怎麼辦? if語句會變得非常大。 而且,最重要的是,如果您有使用send_message()函數的函數怎麼辦? 在這種情況下,您還需要在所有這些函數中添加$technology參數。

隨著代碼的增長,函數將遍布各處,這意味著您將開始復制/粘貼代碼塊(永遠不可取),並且對函數進行小的更改可能會破壞其他幾個函數。 我們都去過那裡。 您可能希望避免這種情況,並且能夠輕鬆地向代碼中添加功能,而不會過多地干擾結構。

使用 Pressidium 託管您的網站

60 天退款保證

查看我們的計劃

面向對象編程(或 OOP)是一種編程範式,它試圖通過允許我們將插件構建成小的、可重用的代碼片段(稱為)來解決這個問題。 正如我們在 OOP 概述文章中所描述的,類基本上是一個模板,我們使用它來創建類的各個實例,稱為對象

一個對象包含數據和代碼。 我們仍然有可以存儲信息的變量,稱為屬性。 對數據進行操作的過程稱為方法

採用 OOP 方法的應用程序

現在讓我們使用 OOP 方法分析與上述相同的場景。

首先,我們將在這裡定義我們擁有哪些對象、每個對象具有哪些特徵以及它們執行哪些操作。 這些特徵將成為我們的屬性,而動作將成為我們在 OOP 中調用的函數或方法。

讓我們考慮一下在第一個場景中以最簡單的方式發送 SMS 的情況。 有一個設備有一個我們用來發送 SMS 消息的接口。 我們有消息內容,我們選擇一個聯繫人作為收件人,最後選擇消息。

 <?php /** * Plugin Name: Send Message */ interface MessagingCapable { public function send_message( $contact, $message ); } class Phone implements MessagingCapable { public function send_message( $contact, $message ) { print "You sent" . $message ; } } function say_hi( MessagingCapable $device, $contact, $message ) { $device->send_message( $contact, $message ); }

我們聲明實現了MessagingCapable接口的Phone類。 所以我們必須實現其中聲明的所有方法。 say_hi()函數需要 3 個參數:

  1. 支持消息傳遞的設備
  2. 聯繫人
  3. 訊息

為了實際發送消息,我們使用這個函數,如下所示:

 $phone = new Phone(); say_hi( $phone, "John Doe", "Hello John" );

我們基本上是通過實例化 Phone 類並傳遞聯繫人和消息內容來創建一個對象。 這將輸出:

 You sent "Hello John"

我們演示了使用類發送文本消息的簡單場景。 在下一節中,我們將了解如何按照 OOP 方法擴展應用程序的功能,並且在擴展時,我們將研究 OOP 功能在哪裡發揮作用以及使用這種技術的好處。

使用 OOP 方法擴展應用程序

讓我們也添加發送電子郵件的功能,就像我們之前在程序上所做的那樣。

無論使用哪種設備,理想情況下我們都希望以相同的方式使用say_hi()函數。 看看下面的代碼:

 <?php /** * Plugin Name: Send Message */ interface MessagingCapable { public function send_message( $contact, $message ); } class Phone implements MessagingCapable { public function send_message( $contact, $message ) { print ('You sent a "' . $message . '" SMS to ' . $contact ); } } class Computer implements MessagingCapable { public function send_message( $contact, $message ) { print ('You sent a "' . $message . '" email to ' . $contact ); } } function say_hi( MessagingCapable $device, $contact, $message ) { $device->send_message( $contact, $message ); }

當我們使用這段代碼時,我們會拿起移動設備發送短信,拿起電腦發送電子郵件。 我們要么:

 say_hi ( new Phone(), "John Doe", "Hello John" );

或者:

 say_hi ( new Computer(), "John Doe", "Hello John" );

這將輸出You sent a "Hello John" SMS to John Doe and You sent a "Hello John" email to John Doe相應地。

在這裡,我們已經開始檢測一些 OOP 特徵。 我們通過使用MessagingCapable接口引入了接口。

接口聲明了一組必須由類實現的方法,而無需定義這些方法的實現方式。 接口中聲明的所有方法都必須是公共的。

PHP 不支持多重繼承,這意味著一個類不能繼承多個父類的屬性/方法。

雖然它只能擴展一個類,但它可以實現多個接口。

使用電話發送消息與使用計算機不同。 當被要求執行相同的操作(即send_message() )時,不同類的實例的行為不同。 這是多態性的一個例子。 如果我們稍後創建一個新設備,我們不需要修改我們的代碼來適應它,只要它們都共享相同的接口。

我們還想在這裡指出,我們已經看到了可讀性的差異。 我們最終通過編碼來使用這個腳本的方式:

 say_hi( new Computer(), "John", "Hi" );

對於從事該項目的任何開發人員來說,這都是完全簡單的。 當然,插件越複雜,它的幫助就越明顯,尤其是在團隊工作時。

為了更好地解釋在面向對象編程中擴展插件是多麼容易,讓我們嘗試添加更多功能。

添加更多功能

如果我們想添加瀏覽互聯網的功能,我們只需為任何可以響應此功能的設備(例如計算機)添加一個額外的接口。

 interface InternetBrowsingCapable { public function visit_website( $url ); }

該接口的實現將編碼如下:

 class Computer implements MessagingCapable, InternetBrowsingCapable { public function send_message( $contact, $message ) { print ('You sent a "' . $message . '" email to ' . $contact ); } public function visit_website( $url ) { print ('You visited "' . $url ); } }

因此,在當前的 Computer 類中,我們只是添加了要實現的額外接口,因為計算機可以發送消息並瀏覽互聯網,以及visit_website( $url )方法。

注意:當然,由於訪問 url 與say_hi()函數完全無關,我們還將引入一個新函數,例如:

 function visit_url( InternetBrowsingCapable $device, $url ) { $device->visit_website( $url ); }

就是這樣! 對於任何可以訪問 URL 的設備,我們都可以像使用計算機一樣使用此功能。 不用擔心您會破壞其餘的功能。 這顯示了與過程編程相比,使用 OOP 時可用的可伸縮性。

讓我們添加一個智能手機設備只是為了演示更多功能。 這是完整的代碼,添加了智能手機類,以便您可以更好地了解正在發生的事情:

 <?php /* * Plugin Name: Communication Plugin */ interface MessagingCapable { public function send_message( $contact, $message ); } interface InternetBrowsingCapable { public function visit_website( $url ); } class Phone implements MessagingCapable { public function send_message( $contact, $message ) { print 'You sent a "' . $message . '" SMS to ' . $contact; } } class Computer implements MessagingCapable, InternetBrowsingCapable { public function send_message( $contact, $message ) { print 'You sent a "' . $message . '" email to ' . $contact; } public function visit_website( $url ) { print 'You visited "' . $url; } } class Smartphone extends Phone implements InternetBrowsingCapable { public function visit_website( $url ) { print 'You visited "' . $url; } public function send_message( $contact, $message ) { parent::send_message( $contact, $message ); print ' from your smartphone'; } } function say_hi( MessagingCapable $device, $contact, $message ) { $device->send_message( $contact, $message ); } function visit_url( InternetBrowsingCapable $device, $url ) { $device->visit_website( $url ); }

Smartphone 類擴展了 Phone 父類並實現了InternetBrowsingCapable接口。 這意味著它可以發送消息並訪問 URL。 在這裡,我們檢測繼承特徵。 換句話說,我們有一個類的層次結構,一個父類(電話)和一個子類(智能手機)。

因此,Smartphone 對象繼承了父 Phone 類的所有屬性和行為。 這樣,我們可以在子類中添加方法或覆蓋父類的方法,就像我們在 Smartphone 類中使用send_message()所做的那樣。 我們這樣做是為了改變輸出。 我們可以完全忽略這個方法,直接使用父類的send_message()

您可以將代碼粘貼到這個出色的 PHP 在線工具的代碼塊中,自己嘗試代碼。 在代碼下,嘗試任何這些代碼行並查看不同的結果。

 say_hi ( new Phone(), "John Doe", "Hello John" ); say_hi ( new Computer(), "John Doe", "Hello John" ); say_hi ( new Smartphone(), "John Doe", "Hello John" ); visit_url ( new Smartphone(), "https://www.pressidium.com" ); visit_url ( new Computer(), "https://www.pressidium.com" );

為了更好地理解整個概念,請查看上述代碼的類圖。

如上所述,在設計類之間的關係時,我們不包括子類中的公共元素。 此外,不要忘記註意左側的指南,以便您可以識別關係以及它們的屬性和方法的可見性。

如果您還想查看封裝功能的實際效果,請嘗試在我們提供的上述任何示例腳本中包含一個 Contact 類。 該類將如下所示:

 class Contact { private $name; private $phone_number; private $email_address; public function __construct( $name, $phone_number, $email_address ) { $this->name = $name; $this->phone_number = $phone_number; $this->email_address = $email_address; } public function get_name() { return $this->name; } public function get_phone_number() { return $this->phone_number; } public function get_email_address() { return $this->email_address; } }

按照設計, __construct()方法在創建對象時自動調用。 現在,當我們實例化 Contact 類時,它的構造函數被調用並設置其私有屬性的值。 然後我們使用get_name()get_phone_number()get_email_address()公共方法來檢索這些值。

封裝是將數據與對數據進行操作的方法捆綁在一起,同時限制直接訪問,防止隱藏的實現細節暴露。

結論

希望本文能幫助您以更實用的方式理解面向對象編程。 OOP 通過清晰和可重用確實有助於使應用程序在未來在必要時更容易擴展。

此外,使用 OOP 的插件將更快更容易執行。 這是因為類的所有對象通用的方法在聲明期間只消耗一次內存。

由於封裝,安全性也得到了提高。 另一方面,在過程編程中,所有數據都是全局的,這意味著可以從任何地方訪問。

由於上述原因,代碼維護、生產力、可擴展性和故障排除對您和您的團隊來說也變得更加容易。

在本系列的下一篇文章中,我們將通過將這種編程風格應用到 WordPress 插件來了解它的實際應用。 具體來說,我們將創建一個由 Johan Eenfeldt 創建的限制登錄嘗試插件版本 1.7.1 的副本,但盡可能使用面向對象的方法進行轉換。

在此過程中,我們將分解插件流程並設置要求。 展望未來,我們將嘗試對插件設計的第一個想法,在實現步驟中,我們將編寫代碼。 在實施過程中,我們將進行一些反復和重新設計,如有必要,以獲得預期的結果。

不過,我們不會詳細介紹代碼的所有部分。 相反,我們希望專注於分享以面向對象的方式構建插件的方式。 我們相信,一旦您閱讀完本系列文章,您就可以很好地創建自己的 OOP 插件。

單擊此處閱讀我們的面向對象編程系列的第 3 部分

也可以看看

  • WordPress 和麵向對象的編程——概述