OOP - 在哪里声明属性?

时间:2017-06-23 15:08:28

标签: php oop

我的父类有一些依赖于只能由子类设置的属性的方法。实现这一目标的“最佳”方法是什么?

  1. 父类将属性设置为默认值(例如,将字符串设置为空字符串),然后子类在其构造函数中设置父属性。
  2. 子类将属性定义为自己的
  3. 父类的方法是否应该接受必要的值作为参数?
  4. 别的什么???
  5. 另外 - 父方法依赖于孩子需要提供的东西是不是很糟糕?

    如果重要的话,我在PHP中这样做。

4 个答案:

答案 0 :(得分:8)

所以...你有一个类有一个算法,另一个类负责提供该算法的数据......当我这样说时,对于这些类中的一个,它是否仍然有意义成为另一方的父母?

您的“父类”应该将“子类”的对象作为构造函数中的参数,然后使用该对象来完成其工作。 组合模式更可靠,可重用且易于理解。

有关详细信息,请参阅设计模式文献中的策略模式。

换句话说,而不是:

+--------------+
|    Parent    |
+--------------+
|*getName()    |
|showGreeting()|
+-----+--------+
      ^
      |
      |
 +----+-----+
 |  Child   |
 +----------+
 |*getName()|
 +----------+

这样做:

+--------------------+    +----------+
|       Parent       |    |  Child   |
+--------------------+    +----------+
|Parent(child: Child)|--->| getName()|
|showGreeting()      |    +----------+
+--------------------+    

答案 1 :(得分:4)

在PHP中处理此问题的方法与使用protected abstract方法处理任何正常语言的方法相同。

http://php.net/manual/en/language.oop5.abstract.php

<?php
abstract class AbstractClass
{
    // Our abstract method only needs to define the required arguments
    abstract protected function prefixName($name);
}

class ConcreteClass extends AbstractClass
{

    // Our child class may define optional arguments not in the parent's  signature
    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";
?>

答案 2 :(得分:1)

根据您的评论here,听起来您在谈论subtyping polymorphism。多态性背后的基本思想是你可以做这样的事情。

选项1 :无默认值。如果不应该从父(例如Shape)类

调用此函数,则应该使用此方法
class Shape
{
    // This doesn't make sense until we know what shape we're talking about
    abstract public function getArea();
}

然后你有这样的子课程:

class Square extends Shape
{
    public $sideLength;

    public function getArea()
    {
        return ($this->sideLength ** 2);
    }
}

class Triangle extends Shape
{
    public $base;
    public $height;

    public function getArea()
    {
        return ($this->base * $this->height * 0.5);
    }
}

etc.

选项2 :如果能够在父级中调用该函数,则应包括属性和函数本身。

class Rectangle
{
    public $length;
    public $width;

    public function getArea()
    {
        return ($this->length * $this->width);
    }
}

然后你有这样的子课程:

class Square extends Rectangle
{
    public $length;

    public function getArea()
    {
        return ($this->length ** 2);
    }
}
etc.

答案 3 :(得分:0)

在我看来,这是一个非常广泛的问题,答案完全取决于用例。让我通过一些简单的例子来展示:

您的第一个建议:

abstract class Vehicle {
 String name = "SuperDriver";
 int numberOfWheels = 4;
 int maxSpeed = 20;

 public function drive(Terrain terrain) {
   echo this.name." drives with ".(this.maxSpeed - terrain->modifier)." mph in ".terrain->name;
 }

}

class Car extends Vehicle {

  public function __construct() {
    this.name="Ferrari";
    this.numberOfWheels = 4;
    this.maxSpeed = 310;
  }

}

class Bicycle extends Vehicle {
  public function __construct() {
    this.name="Mountain Bike";
    this.numberOfWheels = 2;
    this.maxSpeed = 150;
  }
}

优势:

如果您没有将其声明为抽象的甚至可以驱动,您甚至可以实例化Vehicle。

<强>缺点:

  • 非混凝土车辆没有多大意义
  • 任何人如何扩展类必须知道必须覆盖所有属性
  • 难以扩展且难以维护
  • 也许是其他人......

您的第二个建议:

abstract class Vehicle {

 public function drive(Terrain terrain) {
   echo this.name." drives with ".(this.maxSpeed - terrain->modifier)." mph in ".terrain->name;
 }

}

class Car extends Vehicle {

    this.name="Ferrari";
    this.numberOfWheels = 4;
    this.maxSpeed = 310;

}

class Bicycle extends Vehicle {

    this.name="Mountain Bike";
    this.numberOfWheels = 2;
    this.maxSpeed = 150;
}

我看不出任何优势。如果父类需要这些属性,那么您应该明确地要求它们。如果父类不需要任何这些属性,那么这是您不应该关心的子类区域。

您的第三个建议:

abstract class Vehicle {

 protected function drive(Terrain terrain, String name, int maxSpeed) {
   echo name." drives with ".(maxSpeed - terrain->modifier)." mph in ".terrain->name;
 }

 abstract public function drive(Terrain terrain);

}

class Car extends Vehicle {
    this.name="Ferrari";
    this.numberOfWheels = 4;
    this.maxSpeed = 310;

  function drive(Terrain terrain) {
   parent::drive(terrain,this.name,this.maxSpeed);
  }
}

class Bicycle extends Vehicle {
    this.name="Mountain Bike";
    this.numberOfWheels = 2;
    this.maxSpeed = 150;

  function drive(Terrain terrain) {
   parent::drive(terrain,this.name,this.maxSpeed);
  }
}

优点:

  • 您现在有一些灵活性
  • 您可以扩展驱动方法

缺点:

  • 基本上,您声明了两个具有相同名称的不同方法,以达到您想要的效果,因此这是一种解决方法
  • 不是很灵活

这是强制不可更改的属性的方法:

abstract class Vehicle {
 String name;
 int numberOfWheels;
 int maxSpeed;

 public function __construct() {

 }

 protected abstract function getName();
 protected abstract function getNumberOfWheels();
 protected abstract function getMaxSpeed();

 public function drive(Terrain terrain) {
   echo this.getName()." drives with ".(this.getMaxSpeed() - terrain->modifier)." mph in ".terrain->name;
 }

}

class Car extends Vehicle {

  protected function getName() { return "Ferrari"; }
  protected function getNumberOfWheels() { return 4; }
  protected function getMaxSpeed() { return 310; }

}

class Bicycle extends Vehicle {
  protected function getName() { return "Mountain Bike"; }
  protected function getNumberOfWheels() { return 2; }
  protected function getMaxSpeed() { return 150; }
}

如果你这样做,没有人可以从外部改变属性,只有子类。

您还可以在超类中使用受保护的构造函数:

abstract class Vehicle {
 String name;
 int numberOfWheels;
 int maxSpeed;

 protected function __construct($name,$numberOfWheels,$maxSpeed) {
   this.name = name;
   this.numberOfWheels = numberOfWheels;
   this.maxSpeed = maxSpeed;
 }

 public function drive(Terrain terrain) {
   echo this.name." drives with ".(this.maxSpeed - terrain->modifier)." mph in ".terrain->name;
 }

}

class Car extends Vehicle {
   // or pass the arguments here, if you want an outside user to set em    
   public function __construct() {
       parent::__construct("Ferrari",4,310);
   }

}

为什么会这样:

  

注意:如果子类,则不会隐式调用父构造函数   定义构造函数。为了运行父构造函数,调用   子构造函数中的parent :: __ construct()是必需的。如果   child没有定义构造函数,那么它可能会继承自   父类就像普通的类方法(如果它没有声明   作为私人)。

如果你有刚刚应该初始化的公共属性,只需使用构造函数参数。

你的第二个问题: 父方法依赖于孩子需要提供的东西是不是很糟糕?

在我看来不,不一定。否则将只有接口而没有抽象类或者例如特征(PHP)。像这样的机制避免重复代码