我的父类有一些依赖于只能由子类设置的属性的方法。实现这一目标的“最佳”方法是什么?
另外 - 父方法依赖于孩子需要提供的东西是不是很糟糕?
如果重要的话,我在PHP中这样做。
答案 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)。像这样的机制避免重复代码。