为什么PHP中需要抽象类?

时间:2016-08-27 18:47:07

标签: php abstraction

我们可以使用简单的继承或接口而不是抽象。 为什么我们需要在PHP中使用抽象?我们如何使用抽象隐藏基本功能?我很困惑使用抽象和接口和继承。哪里用哪个? 请帮助理解我。

3 个答案:

答案 0 :(得分:7)

我认为首先澄清术语非常重要,以便更精细地回答这个问题。

  1. 继承
    • 继承实际上广泛应用于许多面向对象的编程原则和概念。它只需要从另一个繁殖出来的东西。因此,无论您是实现接口还是扩展类,您仍然使用一种继承形式。它们不是相互排斥的概念。
  2. 接口
    • 尝试想象一个像合约一样的界面。合同本身只是一份文件,通常是两方或多方之间的文件,规定了他们关系的规则。接口,特别是在OOP和PHP的上下文中,不提供实现。它们仅提供实现类必须实现的必需公共方法。接口也无法单独实例化。
  3. 抽象类
    • 抽象类类似于接口,因为它不能单独实例化,但不一定强制扩展类的合同。由于它是一个实际的类,而不仅仅是一个接口,它还允许实现。如果方法在抽象类中声明为abstract,则此实现可以由抽象类本身提供,或者由扩展类提供。它还允许实现属性和私有/受保护方法,因为这里的继承就像一个基类,而不仅仅是一个要求。
  4. 所以回答这个问题," 为什么我们在PHP "中有抽象类,因为它很有用。您可能会首先将这些视为棘手的想法,但它们实际上可以协同工作以提供联合效用。

    实施例

    考虑到有时候界面不足以创建有用的实现。接口只能强制存在方法,并且其签名与实现的接口兼容。例如,您可能希望提供接口的默认实现。

    interface Device {
        public function input(Stream $in);
        public function output(): Stream;
    }
    
    abstract class DefaultDevice implements Device {
        protected $buffer = "";
        public function input(Stream $in) {
            $this->buffer .= $in->read(1024);
            $this->process();
        }
        abstract protected function process();
    }
    

    所以现在任何扩展DefaultDevice的类都可以选择覆盖input方法的实现。即使接口不需要它,它也必须实现process方法。这意味着实现Device接口的其他类可以向后兼容,这仍然是一个实现细节。

    进一步的例子

    将实现与规范分开通常是编写良好的软件的关键属性。

    看看Device界面本身就是一个很好的例子。我们依靠input方法接受Stream类型,output方法返回Stream类型。由于Stream本身实际上可以是一个接口,这意味着任何实现Stream的类型都是可以接受的。所以我可以创建我自己的类并实现Stream接口,而不会破坏此代码。

    class CustomStream implements Stream {
        public function read($bytes = 1024) {
            /* implementation */
        }
        public function write($data) {
            /* implementation */
        }
    }
    
    $device->input(new CustomStream); // this will not throw an error
    

答案 1 :(得分:2)

abstract类用于提供一组数据成员或方法,使其可用于继承自它的类,即使基类不是特别有用(并且永远不应该自己实例化) )没有继承的实现。

从这里继承接管。

另一方面,interface是提供一组实现规则,要求每个使用该接口的类实现其中的规范。实现相同接口的类不需要相互继承,它们实现了接口,因此可以在需要该组功能的任何应用程序中使用它们。

答案 2 :(得分:1)

正如练习一样,我们可以尝试创建一些类来处理几何形状。

基类:

class Shape
{
    public function draw()
    {
    }
}

上面的代码片段中只描述了基类的相关部分。当然,它应该有属性来存储它的位置,线条颜色等和方法(至少是构造函数)。

一些派生类:

class Circle extends Shape 
{
    public function draw()
    {
        // the code to draw a circle
    }
}

class Rectangle extends Shape
{
    public function draw()
    {
        // the code to draw a rectangle
    }
}

我们可以在基类draw()中为方法Shape提供实现吗?

当然不是。 Shape是通用的,它并不仅仅意味着“圆圈”或“矩形”或“三角形”。没有办法为Shape::draw()提供合理的实施,因为我们甚至不知道它代表什么样的形状。

Shape::draw()提供空实现是否可以?

显然是。然而,在第二个想法,很明显,这是不安全的。无法绘制扩展Shape并且不为方法draw()提供其自己的实现的类的对象。

因为类Shape无法为方法shape提供合适的实现,所以它应该以某种方式向派生类发出信号,并强制它们提供实现。

它表示这种情况的方式是abstract关键字。 abstract方法告诉类的读者该类无法提供实现,因为它太通用了,它将此责任委托给扩展它的每个类。

没有完全定义具有abstract方法的类。这就是为什么它是abstract类并且无法实例化的原因。