类抽象

PHP 有抽象类和抽象方法。定义为抽象的类不能被实例化,任何包含至少一个抽象方法的类也必须是抽象类。定义为抽象的方法只声明方法的签名,不能定义实现。

从抽象类继承时,子类必须定义父类声明中标记为抽象的所有方法,并且遵循通常的继承签名兼容性规则。

示例 #1 抽象类示例

<?php
abstract class AbstractClass
{
// 强制扩展类定义此方法
abstract protected function getValue();
abstract protected function
prefixValue($prefix);

// 公共方法
public function printOut() {
print
$this->getValue() . "\n";
}
}

class
ConcreteClass1 extends AbstractClass
{
protected function
getValue() {
return
"ConcreteClass1";
}

public function
prefixValue($prefix) {
return
"{$prefix}ConcreteClass1";
}
}

class
ConcreteClass2 extends AbstractClass
{
public function
getValue() {
return
"ConcreteClass2";
}

public function
prefixValue($prefix) {
return
"{$prefix}ConcreteClass2";
}
}

$class1 = new ConcreteClass1;
$class1->printOut();
echo
$class1->prefixValue('FOO_') ."\n";

$class2 = new ConcreteClass2;
$class2->printOut();
echo
$class2->prefixValue('FOO_') ."\n";
?>

上面的例子将输出

ConcreteClass1
FOO_ConcreteClass1
ConcreteClass2
FOO_ConcreteClass2

示例 #2 抽象类示例

<?php
abstract class AbstractClass
{
// 我们的抽象方法只需要定义所需的参数
abstract protected function prefixName($name);

}

class
ConcreteClass extends AbstractClass
{

// 我们的子类可以定义父类签名中没有的可选参数
public function prefixName($name, $separator = ".") {
if (
$name == "Pacman") {
$prefix = "Mr";
} elseif (
$name == "Pacwoman") {
$prefix = "Mrs";
} else {
$prefix = "";
}
return
"{$prefix}{$separator} {$name}";
}
}

$class = new ConcreteClass;
echo
$class->prefixName("Pacman"), "\n";
echo
$class->prefixName("Pacwoman"), "\n";
?>

上面的例子将输出

Mr. Pacman
Mrs. Pacwoman
添加备注

用户贡献的备注 26 个备注

741
ironiridis at gmail dot com
16 年前
再简单地说明一次

接口就像一个协议。它不指定对象的具体行为,而是指定你的代码如何告诉该对象如何行动。接口就像英语:定义一个接口定义了你的代码如何与实现该接口的任何对象通信。

接口始终是一种协议或承诺。当一个类说 "我实现接口 Y",它就表示 "我承诺拥有与具有接口 Y 的任何对象相同的公共方法"。

另一方面,抽象类就像一个部分构建的类。它就像一份空白的文档。它可能使用英语,但并不像文档中已经写好的部分那样重要。

抽象类是另一个对象的基石。当一个类说 "我扩展抽象类 Y",它就表示 "我在这个名为 Y 的其他类中使用了一些已经定义的方法或属性"。

因此,请考虑以下 PHP 代码
<?php
class X implements Y { } // 这表示 "X" 同意用你的代码说 "Y" 语言。

class X extends Y { } // 这表示 "X" 将完成部分类 "Y"。
?>

如果你要分发一个供其他人使用的类,你应该让你的类实现特定接口。接口是拥有类特定公共方法集的协议。

如果你(或其他人)编写了一个已经包含一些你想要在你的新类中使用的方法的类,你应该让你的类扩展抽象类。

这些概念虽然容易混淆,但它们是截然不同且独立的。就所有意图和目的而言,如果你只是你自己的类的唯一用户,你不需要实现接口。
290
mbajoras at gmail dot com
14 年前
以下是一个帮助我理解抽象类的例子。它只是一种非常简单的解释方法(在我看来)。假设我们有以下代码

<?php
class Fruit {
private
$color;

public function
eat() {
//咀嚼
}

public function
setColor($c) {
$this->color = $c;
}
}

class
Apple extends Fruit {
public function
eat() {
//咀嚼到果核
}
}

class
Orange extends Fruit {
public function
eat() {
//剥皮
//咀嚼
}
}
?>

现在我给你一个苹果,你把它吃了。

<?php
$apple
= new Apple();
$apple->eat();
?>

它尝起来是什么味道?尝起来像苹果。现在我给你一个水果。

<?php
$fruit
= new Fruit();
$fruit->eat();
?>

那尝起来是什么味道???好吧,这没什么意义,所以你不能那样做。这是通过将 Fruit 类和其中的 eat 方法都设为抽象来实现的。

<?php
abstract class Fruit {
private
$color;

abstract public function
eat();

public function
setColor($c) {
$this->color = $c;
}
}
?>

现在,想想一个 MySQL 和 PostgreSQL 扩展的 Database 类。另外,需要注意的是,抽象类和接口很像,但是你可以定义抽象类中的方法,而接口中的方法都是抽象的。
3
shewa12kpi at gmail dot com
3年前
<?php
//这是一个抽象类的很好的例子。这里 BaseEmployee 不是真正的员工,它只是一个抽象类,它减少了我们的代码,并强制子类实现抽象方法

abstract class BaseEmployee {

/**
* 员工的通用属性可以在抽象类中
*/
public $firstname,
$lastname;

function
__construct($fn, $ln){
$this->firstname = $fn;
$this->lastname = $ln;
}

public function
getFullName() {

return
"$this->firstname $this->lastname";
}

/**
* 子类必须定义的抽象方法
*/
abstract protected static function task();
}

class
WebDeveloper extends BaseEmployee {

static function
task()
{
return
' 开发 web 应用程序';
}
}

class
HR extends BaseEmployee {

static function
task()
{
return
' 管理人力资源';
}

}
/**
* 现在实例化并获取数据
*/
$webDeveloper = new WebDeveloper('shaikh','ahmed');
echo
$webDeveloper->getFullName();
echo
$webDeveloper->task();
12
swashata4u at gmail dot com
6年前
关于抽象类和接口,这里还有一点。

有时,我们为 `Factory` 定义一个接口,并通过 `abstract` 类简化 `Factory` 的一些常用方法。

在这种情况下,抽象类实现了接口,但不需要实现接口的所有方法。

简单来说,任何实现接口的类,都需要实现所有方法,或者声明自己为抽象类。

因此,以下代码是完全可以的。

<?php
interface Element {
/**
* 构造函数。必须传递现有的配置,或者保留为
* 用于新元素,使用默认值代替。
*
* @param array $config 元素配置。
*/
public function __construct( $config = [] );

/**
* 获取元素的定义。
*
* @return array 包含 'title'、'description' 和 'type' 的数组
*/
public static function get_definition();

/**
* 获取元素配置变量。
*
* @return array 元素配置的关联数组。
*/
public function get_config();

/**
* 设置元素配置变量。
*
* @param array $config 新的配置变量。
*
* @return void
*/
public function set_config( $config );
}

abstract class
Base implements Element {

/**
* 元素配置变量
*
* @var array
*/
protected $config = [];

/**
* 获取元素配置变量。
*
* @return array 元素配置的关联数组。
*/
public function get_config() {
return
$this->config;
}

/**
* 创建一个 eForm 元素实例
*
* @param array $config 元素配置。
*/
public function __construct( $config = [] ) {
$this->set_config( $config );
}
}

class
MyElement extends Base {

public static function
get_definition() {
return [
'type' => 'MyElement',
];
}

public function
set_config( $config ) {
// 在这里做一些事情
$this->config = $config;
}
}

$element = new MyElement( [
'foo' => 'bar',
] );

print_r( $element->get_config() );
?>

你可以看到这里执行的测试,在 PHP 5.4 及以上版本,输出是一致的。https://3v4l.org/8NqqW
28
jai at shaped dot ca
7年前
这个例子希望能帮助你了解抽象类是如何工作的,接口是如何工作的,以及它们是如何协同工作的。这个例子也适用于 PHP7,其他例子是在表单中实时键入的,可能可以工作,但最后一个是真正制作/测试过的。

<?php

const = PHP_EOL;

// 定义产品必须具备的功能(必须实现)
interface productInterface {
public function
doSell();
public function
doBuy();
}

// 定义我们的默认抽象类
abstract class defaultProductAbstraction implements productInterface {
private
$_bought = false;
private
$_sold = false;
abstract public function
doMore();
public function
doSell() {
/* 默认实现 */
$this->_sold = true;
echo
"defaultProductAbstraction doSell: {$this->_sold}".;
}
public function
doBuy() {
$this->_bought = true;
echo
"defaultProductAbstraction doBuy: {$this->_bought}".;
}
}

class
defaultProductImplementation extends defaultProductAbstraction {
public function
doMore() {
echo
"defaultProductImplementation doMore()".;
}
}

class
myProductImplementation extends defaultProductAbstraction {
public function
doMore() {
echo
"myProductImplementation doMore() does more!".;
}
public function
doBuy() {
echo
"myProductImplementation's doBuy() and also my parent's dubai()".;
parent::doBuy();
}
}

class
myProduct extends defaultProductImplementation {
private
$_bought=true;
public function
__construct() {
var_dump($this->_bought);
}
public function
doBuy () {
/* 非默认的 doBuy 实现 */
$this->_bought = true;
echo
"myProduct overrides the defaultProductImplementation's doBuy() here {$this->_bought}".;
}
}

class
myOtherProduct extends myProductImplementation {
public function
doBuy() {
echo
"myOtherProduct overrides myProductImplementations doBuy() here but still calls parent too".;
parent::doBuy();
}
}

echo
"new myProduct()".;
$product = new myProduct();

$product->doBuy();
$product->doSell();
$product->doMore();

echo
."new defaultProductImplementation()".;

$newProduct = new defaultProductImplementation();
$newProduct->doBuy();
$newProduct->doSell();
$newProduct->doMore();

echo
."new myProductImplementation".;
$lastProduct = new myProductImplementation();
$lastProduct->doBuy();
$lastProduct->doSell();
$lastProduct->doMore();

echo
."new myOtherProduct".;
$anotherNewProduct = new myOtherProduct();
$anotherNewProduct->doBuy();
$anotherNewProduct->doSell();
$anotherNewProduct->doMore();
?>

将产生以下结果
<?php
/*
new myProduct()
bool(true)
myProduct overrides the defaultProductImplementation's doBuy() here 1
defaultProductAbstraction doSell: 1
defaultProductImplementation doMore()

new defaultProductImplementation()
defaultProductAbstraction doBuy: 1
defaultProductAbstraction doSell: 1
defaultProductImplementation doMore()

new myProductImplementation
myProductImplementation's doBuy() and also my parent's dubai()
defaultProductAbstraction doBuy: 1
defaultProductAbstraction doSell: 1
myProductImplementation doMore() does more!

new myOtherProduct
myOtherProduct overrides myProductImplementations doBuy() here but still calls parent too
myProductImplementation's doBuy() and also my parent's dubai()
defaultProductAbstraction doBuy: 1
defaultProductAbstraction doSell: 1
myProductImplementation doMore() does more!

*/
?>
65
a dot tsiaparas at watergate dot gr
13 年前
抽象类和接口是两种截然不同的工具。它们就像锤子和钻头一样。抽象类可以实现方法,而接口本身没有实现。

将所有方法都声明为抽象的抽象类不是名称不同的接口。可以实现多个接口,但不能扩展多个类(或抽象类)。

抽象类和接口的使用取决于具体的问题,选择是在软件设计阶段做出的,而不是在实现阶段。在同一个项目中,你可能需要提供一个接口和一个实现该接口的基类(可能是抽象类)。你为什么要这样做?

假设我们要构建一个调用不同服务的系统,这些服务又包含一些动作。通常,我们可以提供一个名为 execute 的方法,该方法接受动作名称作为参数,并执行该动作。

我们希望确保类可以实际定义自己的执行动作的方式。因此我们创建了一个名为 IService 的接口,该接口包含 execute 方法。好吧,在大多数情况下,你都会复制粘贴完全相同的 execute 代码。

我们可以为名为 Service 的类创建一个参考实现,并实现 execute 方法。这样,其他类的代码就不用再复制粘贴了!但是,如果你想扩展 MySLLi 呢?你可以实现该接口(可能需要复制粘贴),然后你又得到了一个服务。抽象类可以包含在类的初始化代码中,而这些代码不能为你要编写的每个类预先定义。

希望这不会太令人费解,并对某人有所帮助。干杯!
亚历克西奥斯·齐亚帕拉斯
17
shaman_master at list dot ru
5 年前
你还可以为抽象方法设置返回值/参数类型声明(PHP>=7.0)
<?php
declare(strict_types=1);

abstract class
Adapter
{
protected
$name;
abstract public function
getName(): string;
abstract public function
setName(string $value);
}

class
AdapterFoo extends Adapter
{
public function
getName(): string
{
return
$this->name;
}
// 在抽象类中没有定义返回值类型声明,这里设置
public function setName(string $value): self
{
$this->name = $value;
return
$this;
}
}
?>
68
joelhy
13 年前
文档中说:"不允许创建已定义为抽象的类的实例。" 这只意味着你不能从抽象类初始化对象。调用抽象类的静态方法仍然是可行的。例如
<?php
abstract class Foo
{
static function
bar()
{
echo
"test\n";
}
}

Foo::bar();
?>
19
sneakyimp at hotmail dot com
16 年前
好的...文档在抽象类扩展另一个抽象类方面有点含糊不清。扩展另一个抽象类的抽象类不需要定义父类中的抽象方法。换句话说,这会导致错误

<?php
abstract class class1 {
abstract public function
someFunc();
}
abstract class
class2 extends class1 {
abstract public function
someFunc();
}
?>

错误:致命错误:无法继承抽象函数 class1::someFunc()(之前在 class2 中声明为抽象)在 /home/sneakyimp/public/chump.php 的第 7 行

然而,这不会

<?php
抽象类 class1 {
抽象公共函数
someFunc();
}
抽象类
class2 扩展 class1 {
}
?>

一个扩展抽象类的抽象类可以在实现其父抽象类的抽象方法时将责任委托给子类。
5
Eugeny at Kostanay dot KZ
7年前
代码片段,帮助您更多地了解抽象类中的属性。
<?php
抽象类 anotherAbsClass
{
// 定义和设置静态属性
静态 $stProp = 'qwerty'; // 我们仍然可以通过静态方式直接使用它
// 定义和设置受保护属性
受保护 $prProp = 'walrus';
// 为抽象类的非静态变量设置任何其他可见性级别都是无用的。
// 我们无法访问私有属性,即使在抽象类声明的方法中,因为我们无法在对象上下文中调用该方法。
// 公共方法的实现
受保护函数 callMe() {
回显
'On call: ' . $this->prProp . PHP_EOL;
}
// 声明一些抽象方法
抽象受保护函数 abc($arg1, $arg2);
抽象公共函数
getJunk($arg1, $arg2, $arg3, $junkCollector = true);
// 注意:如果抽象类已经声明了可选值,我们不能省略它而不得到错误
}

someChildClass 扩展 anotherAbsClass
{
函数
__construct() {
回显
$this->callMe() . PHP_EOL; // 现在我们从抽象类中继承了受保护属性 $prProp
}
// 必须在下面实现声明的函数 abc 和 getJunk
受保护函数 abc($val1, $val) {
// 做点什么
}
函数
getJunk($val1, $val2, $val3, $b = false) { // 可选值是必要的,因为它在上面已经声明过
// 做点什么
}
}

回显
anotherAbsClass::$stProp; // qwerty
$objTest = 新建 someChildClass; // On call: walrus
?>
17
bishop
14 年前
顺便说一下,抽象类不需要是基类。

<?php
Foo {
公共函数
sneeze() { 回显 'achoooo'; }
}

抽象类
Bar 扩展 Foo {
公共抽象函数
hiccup();
}

Baz 扩展 Bar {
公共函数
hiccup() { 回显 'hiccup!'; }
}

$baz = 新建 Baz();
$baz->sneeze();
$baz->hiccup();
?>
7
jai at shaped dot ca
7年前
接口指定了一个类必须实现哪些方法,以便使用该类的任何东西都期望它遵守该接口,从而可以正常工作。

例如:我期望任何 $database 都有 ->doQuery(),因此我分配给数据库接口的任何类都应该实现 databaseInterface 接口,该接口强制实现 doQuery 方法。

<?php
接口 dbInterface {
公共函数
doQuery();
}

myDB 实现 dbInterface {
公共函数
doQuery() {
/* 实现细节在此处 */
}
}

$myDBObj = 新建 myDB()->doQuery();
?>

抽象类类似,只是可以预定义一些方法。列出的抽象方法必须像接口一样定义。

例如,我期望我的 $person 能够 ->doWalk(),大多数人用两条腿走路很好,但有些人不得不跳着走 :(

<?php
接口 PersonInterface() {
/* 每个人的都应该走路,或者尝试走路 */
公共函数 doWalk($place);
/* 每个人都应该能够变老 */
公共函数 doAge();
}

抽象类
AveragePerson 实现 PersonInterface() {
私有
$_age = 0;
公共函数
doAge() {
$this->_age = $this->_age+1;
}
公共函数
doWalk($place) {
回显
"我要去 $place".PHP_EOL;
}
/* 每个人说话的方式都不一样! */
抽象函数 talk($say);
}

Joe 扩展 AveragePerson {
公共函数
talk($say) {
回显
"用澳大利亚口音,Joe 说: $say".PHP_EOL;
}
}

Bob 扩展 AveragePerson {
公共函数
talk($say) {
回显
"用加拿大口音,Bob 说: $say".PHP_EOL;
}
公共函数
doWalk($place) {
回显
"Bob 只有一条腿,不得不跳着去 $place".PHP_EOL;
}
}

$people[] = 新建 Bob();
$people[] = 新建 Joe();

遍历 (
$people 作为 $person) {
$person->doWalk('那里');
$person->talk('PHP 很棒');
}
?>
3
Malcolm
8 年前
我发现了一个不一致之处:示例 #2 抽象类示例

如果你删除了 $separator 的默认值

<?php
公共函数 prefixName($name, $separator) {
// ...
}
?>

然后 php 会显示此致命错误消息
致命错误:ConcreteClass::prefixName() 的声明必须与 AbstractClass::prefixName($name) 兼容,位于 /index.php 第 23 行

奇怪的是,它给出了一个错误的声明 "ConcreteClass::prefixName()" ... 它缺少两个参数。因此,我认为这可能只是一个错误,可能在较新版本中已经解决了。(或者只是特定于我的版本)我主要指出这一点,因为在一些我从示例 #2(没有额外参数的默认值)中编写的测试代码中,它让我非常抓狂。也许这可以避免其他人遇到挫折。

--
请注意,我在 php5.5 上运行此代码。
操作系统:ubuntu-16.04-server-amd64.iso
仓库:ppa:ondrej/php

# php5.5 --version
PHP 5.5.36-2+donate.sury.org~xenial+1 (cli)
版权所有 (c) 1997-2015 PHP 小组
Zend Engine v2.5.0,版权所有 (c) 1998-2015 Zend Technologies 与 Zend OPcache v7.0.6-dev,版权所有 (c) 1999-2015,由 Zend Technologies
6
joebert
17 年前
我不同意 jfkallens 关于抽象类和对象接口的最后比较。

在抽象类中,你可以定义一些方法的工作方式,而对象接口则不能。

对象接口本质上只是函数名的列表,如果一个类实现了该接口,则该类必须定义这些函数名。

抽象类本质上是一种原型,它暗示了扩展类应该做什么。
抽象类也可以被认为是基类,它提供了一些基本功能,并且还定义了一个内置的对象接口,所有扩展类都将实现该接口。

因此,对象接口实际上是抽象类的一个内置部分。
1
arma99eDAN at yahoo dot com
9 年前
您也可以这样使用抽象类

abstract class A{
public function show(){
echo 'A';
}
}
class B extends A{
public function hello(){
echo 'B';
parent::show();
}
}

$obj = new B;
$obj->hello(); // BA
# 请注意,抽象类没有至少一个抽象方法
# 即使在这种情况下,我仍然可以扩展它,或调用其非抽象成员
4
pete at surfaceeffect dot com
14 年前
PHP 的抽象函数与 Java 之间的一个相当重要的区别是,PHP 不会以任何方式指定返回值类型 - 或者实际上是否必须有返回值。

<?php public abstract function square($number); ?>

可以由...实现

<?php
public function square($number) {
return
$number*$number;
}
?>



<?php
public function square($number) {
print (
$number*$number);
}
?>

因此,您需要确保由于没有返回正确类型的 value 而不会出现不兼容,并且这不会以任何方式强制执行。
-1
aloydev2586 at gmail dot com
9 年前
如果您对函数参数感到困惑,请参考以下示例

/*************示例 1********************/

abstract class my_class {
abstract public function my_function($number);
}

class subclass extends my_class {
public function my_function($new_number, $string = ' is an integer!!!')
{
echo $new_number . $string;
}
}
$var = new subclass();
$var->my_function(1024); //这将输出:1024 is an integer!!!

/*************示例 2********************/
abstract class my_class {
abstract public function my_function($number);
}

class subclass extends my_class {
//现在 $string = ' is a float!!!'
public function my_function($new_number, $string = ' is a float!!!')
{
echo $new_number . $string;
}

}
$var = new subclass();
//添加了 ' is an integer'
$var->my_function(1024, ' is an integer!!!'); //这将输出:1024 is an integer!!!,重写了 $string。

/*************示例 3********************/

abstract class my_class {
abstract public function my_function($number);
}

class subclass extends my_class {
//现在 $string 没有初始化
public function my_function($new_number, $string )
{
echo $new_number . $string;
}

}

$var = new subclass();
$var->my_function(1024, ' is an integer!!!'); /*这将触发一个致命错误,表明 subclass::my_function() 与 my_class::my_function($number) 之间不兼容*/

/*************示例 4********************/

abstract class my_class {
abstract public function my_function($number);
}

class subclass extends my_class {
public function my_function($new_number, $string )
{
echo $new_number . $string;
}

}
$var = new subclass();
//没有第二个参数,无论如何
$var->my_function(1024);//也是致命错误。可选参数必须在扩展类函数中初始化。
-1
designbyjeeba at gmail dot com
13 年前
请注意父类的字段可见性。如果字段是私有的,那么您将无法在子类中看到这些字段。这是 OOP 的基本原理,但有时会造成问题。
-1
nathan dot vorbei dot tech at gmail dot com
14 年前
"此外,这些方法必须以相同(或更不严格)的可见性定义。"

这句话不仅适用于抽象类,也适用于普通类,
子类中重写父类方法的方法也可以将方法的可见性更改为相同或更不严格的可见性。
例如
<?php
class ClassOne {
protected static
$staticone = 'nathan';
protected function
changestaticone() {
return
self::$staticone = 'john';
}
}

class
ClassTwo extends ClassOne {
public function
changestaticone() {
return
self::$staticone = 'Alexey';
}
}
$classtwo = new ClassTwo();
echo
$classtwo->changestaticone();
-3
Cheese Doodle
14 年前
理解这些概念其实并没有什么太大的障碍。

如果您定义了一个新的抽象类,这意味着您可以创建一些非抽象函数,用于定义该类的基本行为,以及抽象函数。

在接口中,您无法做到这一点,因为在接口中定义的函数不能有函数体。

您将抽象函数用于必须在“扩展”您的类时定义更特定行为的类。

举个简单的例子 - 通过您的非抽象函数来定义该特定对象(可能是较大类层次结构的一部分)如何在 SQL、XML 等中存储和处理其数据。

然后定义抽象函数,允许实现该类的用户专门操作要存储的数据。然后要求返回此数据的格式,然后在您的非抽象函数中调用这些函数以进行销毁、正常运行时等。

同样,非抽象函数或另一个类可以实现确保数据格式正确的细化点,依此类推,无限循环。

如果您使用普通类而不是抽象类,那么可以说两者之间没有太大的本质区别。

假设您希望这些函数使用彼此的函数,并且您需要专门使用它们的名字,那么您必须编写一些代码来检查 - 笨拙地使用 function_exists() 等方法 - 该类是否具有您需要的互操作性函数,而您只需使用合适的工具来完成工作,就能避免所有可能的混乱和麻烦。

以及阅读一本优秀的 OOP 书籍。
-3
eeescalona
16 年前
以下是一个使用抽象类的现实世界示例

一个(抽象)person 类
一个 student 和一个 employee 终类,它们扩展了 person 类。

简单的理论是,student 和 employee 都是 person 类的扩展。区别在于数据写入哪个表,以及在写入每个类之前需要进行哪些其他预处理(例如强制字段检查、类型检查等)。

代码

<?php

abstract class person {

abstract protected function
write_info();

public
$LastName;
public
$FirstName;
public
$BirthDate;

public function
get_Age($today=NULL){
//年龄计算函数
}
}

final class
employee extends person{
public
$EmployeeNumber;
public
$DateHired;

public function
write_info(){
echo
"Writing ". $this->LastName . "'s info to emloyee dbase table";
//添加仅适用于 EMPLOYEE 的唯一强制检查
//此处添加实际的 sql 代码
}
}

final class
student extends person{
public
$StudentNumber;
public
$CourseName;

public function
write_info(){
echo
"Writing ". $this->LastName . "'s info to student dbase table";
//添加仅适用于 STUDENT 的唯一强制检查
//此处添加实际的 sql 代码
}
}

///----------
$personA = new employee;
$personB = new student;

$personA->FirstName="Joe";
$personA->LastName="Sbody";

$personB->FirstName="Ben";
$personB->LastName="Dover";

$personA->write_info();
?>

输出:Writing Sbody's info to emloyee dbase table
-3
rmoisto at gmail dot com
8 年前
在抽象类中,self 关键字将引用抽象类本身,而不是扩展类,无论如何。

例如,以下代码看起来很漂亮,但会导致致命错误(无法实例化抽象类 Basic)。

<?php
abstract class Basic {
public static function
doWork() {
return (new
self())->work();
}

abstract protected function
work();
}

class
Keeks extends Basic {
protected function
work() {
return
'Keeks';
}
}

echo
Keeks::doWork();
?>
-5
nikola at petkanski dot com
10 年前
应该删除调用抽象类的静态方法的操作。

什么是接口?
- 确保所有实现都实现了相同的方法的一种方式。

什么是抽象类?
- 它是一个可以包含一些具体方法的接口。

开发人员是否应该能够调用接口的静态方法?
- 我认为不应该。

GoF 教导我们依靠抽象类和接口来隐藏子类与客户端之间的差异。
- 接口定义了对象的用途(协议)
- 实现定义了特定的策略

我认为不应该能够调用抽象类中定义的一些抽象逻辑,而无需继承该类本身。
-7
sam at righthandedmonkey dot com
9 年前
请注意,代码中类的顺序或位置可能会影响解释器,并可能导致致命错误:类“YourClass”未找到,如果存在多个抽象级别顺序错误。例如
<?php
abstract class horse extends animal {
public function
get_breed() { return "Jersey"; }
}

class
cart extends horse {
public function
get_breed() { return "Wood"; }
}

abstract class
animal {
public abstract function
get_breed();
}

$cart = new cart();
print(
$cart->get_breed());
?>

这将输出
Wood

但是,如果您将购物车放在抽象马之前(从字面上讲)

<?php
class cart extends horse {
public function
get_breed() { return "Wood"; }
}

abstract class
horse extends animal {
public function
get_breed() { return "Jersey"; }
}

abstract class
animal {
public abstract function
get_breed();
}

$cart = new cart();
print(
$cart->get_breed());
?>

这将抛出一个错误
致命错误:类“horse”未找到

因此,当使用多个抽象级别时,请注意源代码中类的排列方式——不要将购物车放在抽象马之前。
-14
oliver at ananit dot de
12 年前
抽象类可能有一个 final 构造函数,有时用 final 构造函数实现一个类是有意义的。

<?php
abstract class AbstractModel
{
//我们的模型必须使用默认构造函数
public final function __construct(){}
public function
inject($array){
foreach(
array_keys(get_class_vars(get_called_class())) as $property){
$this->$property = $array[$property];
}
}
}

class
ProductModel extends AbstractModel
{
public
$name;
public
$price;
protected
$id;

public function
getId(){return $this->id;}
}

class
Factory{
private
$dataSource;
public function
__consruct($dataSource){
$this->dataSource = $dataSource;
}

public function
get($class, $table, $filter, $orderby, $limit){
$result = array();
foreach(
$datasource->fetchAssoc($table, $filter, $orderby, $limit) as $rawData){
$obj = new $class();//这只有在所有模型都有默认构造函数的情况下才能工作
$obj->inject($rawData);
$result[] = $obj;
}
return
$result;
}
}
?>

注意:这是一个非常简单的例子,我知道还有其他(更好)的方法来做到这一点。
Oliver Anan
-18
balbuf
8 年前
abstract 关键字不能用于指定派生类必须设置/定义的属性或类常量。相反,这些必需的属性或常量可以包含在抽象类中,并期望它们将在派生类中被覆盖,这至少可以确保所需属性/常量被设置/定义。
To Top