基本概念

类是由三种类型的项组成的结构,称为成员,即:

  • 常量;
  • 被称为“属性”的变量;
  • 被称为“方法”的函数。

每个成员都有一个访问修饰符,可以是显式的(如“private”、“protected”和“public”)或隐式的(即省略声明,仅对默认值为“public”的方法有效)。

对象是类的一个实例,可以调用类中的方法(如果有)并为类的成员(如果有)提供新的值,从而覆盖默认值。

一个简单的类可以这样定义:

<?php
  
/* 定义类 */
class Html {
  
    /*
      声明一个名为 "br" 的公有成员,默认值为 "&lt;br /&gt;"(XHTML 换行符)
    */
    public $br = '&lt;br /&gt;';
  
    /*
      声明一个公有方法,名为 "printLn",需要一个名为 "message" 的参数
    */
    function printLn($message) {
        
        /*
          输出 "message" 参数,后跟 "br" 成员的值(通过 "$this" 关键字访问当前对象,
          使用 "->" 操作符来访问成员,成员名后不加 "$" 符号)。
        */
        echo $message . $this->br;
    }
}
?>

然而,创建了类并不意味着你可以直接使用其中的任何内容。类是一个抽象实体,它是一种声明“这是如何创建 Html 类型对象以及它们的功能”的方式。在你可以对类的成员做任何操作之前,必须先实例化该类。

也就是说,必须先创建一个“Html”对象,并与之交互。

对象

一旦定义了类,就可以尝试使用它。对象是类的实例。在 PHP 中使用对象的方法如下:

<?php
$myPage = new Html; // 创建一个 Html 类的实例
$myPage->printLn('This is some text'); // 输出 "This is some text&lt;br /&gt;"
?>

这一段代码中新出现的概念是:

  • new 关键字 — 用于创建对象的新实例,在这个例子中是 Html 类的对象。
  • -> 操作符 — 用于访问已实例化对象的成员或方法,在这里是访问 printLn() 方法。

现在,既然换行符的表示是在类的一个成员中定义的,就可以进行修改。需要注意的是,这些修改仅会影响实例,而不是整个类。下面是一个示例(请注意,<br> 是 HTML 换行符,<br /> 是 XHTML 换行符):

<?php
$myPage = new Html; // 创建一个 Html 类的实例
$myOldHtmlPage = new Html; // 创建另一个 Html 类的实例
$myOldHtmlPage->br = "&lt;br&gt;"; // 修改 "$myOldHtmlPage" 实例中 "br" 成员的值为旧的 HTML 值
$myOldHtmlPage->printLn('This is some text'); // 输出 "This is some text&lt;br&gt;",使用的是新值
$myPage->printLn('This is some text'); // 输出 "This is some text&lt;br /&gt;",使用的是默认值
?>

不过,注意最好避免修改实例的成员:为了保证实例的约束验证,应该在类中添加“get_br”和“set_br”方法,以便在允许实例执行测试并可能拒绝更改的情况下获取和设置成员的值。

作用域

类的成员的作用域定义了谁可以调用它(对于方法)或读取/更改其值(对于属性)。

  • 作用域定义对属性是强制性的,而对于方法来说则是可选的,如果未指定,默认值为“public”。
公有(Public)

公有成员可以在任何地方访问。

保护(Protected)

类中定义的保护成员可以被实例化该类的对象或该类的子类访问。

私有(Private)

类中定义的私有成员仅能被实例化该类的对象访问。

成员

常量

常量使用 const 声明(与过程式编程中的 define() 不同),并且可以选择性地添加默认值:

const MY_CONSTANT_1 = 0;

可以通过反射(Reflection)获取:

$myClass1::getConstants();
属性

虽然将类属性声明为公有使得使用更为方便,但通常不推荐这么做。如果你在未来版本的类中决定对公有属性进行某种检查,或为了缓存目的将其存储在另一个额外的属性中,那么你无法这么做,因为其他代码仍然会直接使用公有属性,而没有任何检查。

因此,应该尽量避免(除特殊情况外)允许公有访问属性,而提供简单的“get_foo”和“set_foo”方法来返回或覆盖它们,这样可以在需要时添加复杂性。

可以通过反射获取:

$myClass1::getProperties();
方法

由于方法提供了对象与外界之间的实际接口,因此几乎所有方法都应当是公有的。

更具体地说,任何打算从外部调用的方法应该是公有的。然而,仅供内部使用的方法且不属于接口的一部分不应该是公有的。一个典型的例子是抽象数据库连接的对象,同时提供缓存系统。应该存在不同的公有方法来允许外部交互,但例如,发送原始 SQL 请求到数据库的方法不应该是公有的,因为任何外部代码调用它都会绕过缓存,可能会导致不必要的负载或(更糟糕的是)数据丢失,因为数据库中的数据可能已经过时(如果延迟更改已发生在缓存中)。

可以通过反射获取:

$myClass1::getMethods();

实际应用

上面的示例并没有展示类和对象的实际用途。为什么要为访问函数创建这么多额外的结构呢?

让我们举一个例子,展示这种方法如何带来便利:

<?php
class Html {
   private $source = "";
   public function printLn($message) {
      echo $this->source .= $message . "&lt;br /&gt;";
   }
   public function show() {
      echo $this->source;
   }
}

$elvis = new Html();
$goth = new Html();

$elvis->printLn("Welcome to my Elvis Fan Page! Uh-huh, uh-huh, uh-huh.");
$goth->printLn("Entree the Goth Poetry Labyrinth of Spoooky Doooommmm...");
$elvis->show(); 
?>

一些需要注意的事项:

  • echo $this->source .= $message . "&lt;br /&gt;"; 语句首先修改了私有属性 $source 的值,然后输出修改后的值。修改后的值被保存。
  • 每个 Html 类的实例中都存在 $source 属性的一个不同副本。
  • printLnshow 方法引用的是它们各自对象的 $source 副本。(当我调用 elvisprintLn 方法时,它修改了 elvis$source 变量,而调用 $elvis->show() 时输出的是修改后的 $source 值。)

使用标准变量和方法时,可能会有更大的风险将错误的内容发送到错误的页面,这可能会导致访问者的混乱。

现在我们可以看到,使用类和对象可以节省多少时间和麻烦。我们可以轻松地管理两个潜在的网页,并将正确的内容输出到每个页面。完成后,我们只需要调用页面的 show 方法将该页面的 HTML 源代码输出到流中。

我可以通过向控制类添加变量和函数为所有对象添加更多功能,并且只需声明更多对象即可。实际上,随着我的程序变得更复杂,使用面向对象编程会让一切变得更容易管理。

最后修改: 2025年01月10日 星期五 01:03