第 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 和面向对象的编程——概述