小熊奶糖(BearCandy)
小熊奶糖(BearCandy)
发布于 2024-03-13 / 19 阅读
0
0

php 面向对象 抽象类 抽象类(Abstract Class)方法 与接口(Interface)区别

在PHP面向对象编程中,抽象类(Abstract Class)和接口(Interface)都是为了提供一种规范化的途径,强制子类去实现特定的方法,以达到某种设计要求或契约。以下是它们的主要区别:

抽象类(Abstract Class)

  1. 定义:抽象类通过使用 abstract 关键字来声明,它可以包含抽象方法(使用 abstract 声明并且没有具体实现的方法)和普通方法(有具体实现的方法)以及属性(成员变量)。
  2. 特性
    • 抽象类可以有构造函数,并且可以有非抽象(具体实现)的方法和属性。

    • 子类必须继承抽象类,并实现其所有抽象方法,否则子类也需要声明为抽象类。

    • 抽象类自身不能实例化,只能通过其非抽象子类实例化。

    • 可以使用public,protect,private

      
      abstract class test1{
      abstract private function test()//不能这样用
      
      private function test1(){ //可以这样用
       echo "123";
      }
      }
      
    • 抽象方法只有方法的声明,而没有方法体

    • 抽象类可以包含抽象方法和普通方法

抽象类可以像具体类一样调用内部成员

abstract class Onetwo {
    private static $a = 1;

    public function qwer() {
        return self::$a;
    }
}

class Wokao extends Onetwo {
    public function __construct() {
        $c = parent::qwer();
        echo $c; // 为了演示目的,显示结果
    }
}

$wokao = new Wokao(); // 实例化 Wokao 类

解释:

  1. 抽象类 Onetwo:抽象类 Onetwo 包含一个私有静态变量 $a,初始值为 1,以及一个公共方法 qwer()

    • private static $a = 1;:声明一个私有静态变量 $a,只能在 Onetwo 类内部访问。其值设为 1。
    • public function qwer() { return self::$a; }:定义一个公共方法 qwer(),返回静态变量 $a 的值,使用 self 关键字,该关键字在此处指代当前类(即 Onetwo)。此方法可以从 Onetwo 类的任何实例或其子类中调用。
  2. 具体类 Wokao:类 Wokao 继承自抽象类 Onetwo。在 __construct() 方法中,我们调用父类的 qwer() 方法并将返回值赋给局部变量 $c。接着,为了演示目的,我们使用 echo 输出 $c 的值。

    • public function __construct() { ... }:构造方法在创建 Wokao 类实例时被调用。
    • $c = parent::qwer();:使用 parent 关键字调用父类(Onetwo)中定义的 qwer() 方法。返回值被赋给局部变量 $c
    • echo $c;:显示 $c 的值,以示例说明父类的 qwer() 方法成功被调用,其返回值已存储在 Wokao 类实例的 $c 变量中。
  3. 实例化 Wokao:最后,我们创建 Wokao 类的一个实例 ($wokao = new Wokao();),这会触发构造方法并执行其中的代码。

运行此修正后的代码时,将会输出 "1",这是 Onetwo 类中私有静态变量 $a 的值,通过 qwer() 方法访问,并存储在 Wokao 类实例的 $c 变量中。

接口(Interface)

  1. 定义:接口使用 interface 关键字来声明,它只包含常量和抽象方法(PHP 7.4及以后版本允许定义私有方法,但仍然不能有属性和具体实现)。
  2. 特性
    • 接口中所有的方法默认为 public 类型,并且都不能有任何实现(在PHP 8之前)。
    • 接口不能包含属性(除了常量,默认为 publicstatic)和构造函数。
    • 类可以通过 implements 关键字来实现一个或多个接口,并且必须实现接口中声明的所有方法。
    • 接口可以继承其他接口,形成一个方法集合的扩展。

总结差异

  • 抽象类侧重于定义一个类的基本结构,允许包含部分实现,适用于类之间的层次结构关系较为紧密的情况,可作为模板类供子类扩展。

  • 接口更强调纯粹的行为规范,是对类功能的一种约定,强制实现接口的类必须拥有某些公共方法,而不关心这些方法的具体实现。接口通常用于解耦组件间的依赖关系,使得不同的类能够共享同一套接口标准,实现多态。

  • 如果子类继承抽象类时,子类中无抽象类方法,则需要将子类声明为抽象类,而子类继承接口时必须包含接口中的方法

    abstract class lover{
       public static $name;
       abstract public function eat(){
    echo "eeat;
    }
    }
    //将eat也变成抽象类
    abstract class eat extend lover{
    //不必实现上述方法
    function ear(){
    126;
    }
    }
    
  • 接口中只能为public,而抽象类中可以为public,protected和private

所以,在设计时选择抽象类还是接口,往往取决于你希望实现的设计模式和代码组织结构的要求。如果关注的是类的共同结构和部分共性实现,则使用抽象类;如果关注的是统一的对外行为规范,则更适合使用接口。

抽象类的例子及错误实例:

// 正确实例 - 定义一个抽象类及其抽象方法
abstract class Animal {
    public $name;
  
    abstract protected function makeSound(); // 抽象方法

    public function __construct($name) {
        $this->name = $name;
    }
  
    public function introduce() {
        echo "This is an animal named {$this->name} and it makes this sound: ";
        $this->makeSound();
    }
}

// 继承抽象类并实现抽象方法
class Dog extends Animal {
    protected function makeSound() {
        echo "Woof!";
    }
}

$dog = new Dog("Buddy");
$dog->introduce(); // 输出: This is an animal named Buddy and it makes this sound: Woof!

// 错误实例 - 忘记在子类中实现抽象方法
abstract class Animal {...}

class Cat extends Animal { // 编译错误,因为Cat类没有实现Animal中的抽象方法makeSound()
}

接口的例子:

// 定义接口
interface CanFly {
    public function fly();
}

interface CanSwim {
    public function swim();
}

// 创建一个实现了两个接口的类
class Duck implements CanFly, CanSwim {
    public function fly() {
        echo "The duck is flying.";
    }

    public function swim() {
        echo "The duck is swimming.";
    }
}

$duck = new Duck();
$duck->fly(); // 输出: The duck is flying.
$duck->swim(); // 输出: The duck is swimming.

// 错误实例 - 忘记在类中实现接口方法
interface CanJump {
    public function jump();
}

class Elephant implements CanJump { // 编译错误,因为Elephant类没有实现CanJump接口的jump()方法
}

上述例子中,抽象类Animal定义了一个通用的结构并包含一个抽象方法makeSound(),Dog类继承Animal并实现了这个抽象方法。而接口CanFly和CanSwim分别定义了飞和游泳的行为规范,Duck类通过实现这两个接口确保了它具备这两种能力。在错误实例中,未实现抽象方法或接口方法的类会导致编译错误。

总结interface接口和abstract类似都能定义类的行为规范,但存在不同abstract类可以有自己的属性和方法,interface完全提供一种行为规范来限制类中必须有这个方法体和属性。

在确定行为规范时都只需要定义变量名或者方法名,而不用具体实现具体的方法

接口中只能为public,而抽象类中可以为public,protected和private

接口要使用implements来使用,抽象类直接使用extend方式继承

抽象类可以像普通类(具体类)一样调用内部方法变量,接口不行。


评论