我有一个复合对象类,它有一个包含的组件类,在实例化中我希望包含的对象能够实时访问复合对象的方法和属性。
在下面的“汽车”和“车轮”示例中,我建立了车轮直径与汽车高度之间的动态关系。改变汽车的高度会自动改变车轮的直径,从而保持汽车的比例:
class Car {
private $wheel;
private $heightM;
function __construct() {
$this->wheel = new Wheel($this);
}
final public function wheel() {
return $this->wheel;
}
final public function setHeight($metres) {
$this->heightM = $metres;
}
final public function height() {
return $this->heightM;
}
}
class Wheel {
private $car;
final public function __construct(Car &$car) { // by reference
$this->car = &$car; // pointer
}
final public function diameter() {
return $this->car->height() * 20 / 100;
}
}
$toyota = new Car();
$toyota->setHeight(1);
print "Car height = " . $toyota->height() . "m"
. "; Wheel diameter = " . $toyota->wheel()->diameter() . "m"
. PHP_EOL;
$toyota->setHeight(2);
print "Car height = " . $toyota->height() . "m"
. "; Wheel diameter = " . $toyota->wheel()->diameter() . "m"
. PHP_EOL;
输出:
Car height = 1m; Wheel diameter = 0.2m
Car height = 2m; Wheel diameter = 0.4m
这正是我想要的结果,但我正在寻求关于上述是否构成可接受的PHP编码方法或是否有更好的OOP / PHP方法来实现相同的指导。谢谢!
编辑:为了使这个问题更加具体:我真的在寻找一个答案:不 - 这种方法会因为A,B和C的原因而破坏PHP;或否 - 请参阅正式设计模式X,这是实现此功能的公认标准。或者,是 - 使用指针来实现你在这里尝试做的事情是完全可以接受的(当然这是在我理解指针可能存在的风险的限制范围内)
答案 0 :(得分:3)
从技术上讲,Car
课程不是container,而是compound object。
容器是零个或多个相同类型(或抽象类型)对象的集合,但是包含的对象可能存在并且即使它们未添加到容器中也能正常工作。让所包含的物体知道容器的存在使得它们在没有容器的情况下难以(或甚至不可能)使用。这显然是一种不好的做法。
复合对象由较小的对象(较简单的类型)组成,不一定都是相同类型的对象,并且它们的编号通常是预先知道的。组件由复合对象拥有;复合对象被销毁时会被破坏。让组件知道它们的所有者(复合对象)有时是有用的,但大多数时候都不需要它。
您应该以自上而下的方式考虑Car
及其组件的所有操作。它的所有方法都应直接查询或修改其原始属性($heightM
),如果需要,可以调用其组件的方法($wheel
)来查询或修改它们。
从外面只能看到Car
对象。公开其组件以进行直接访问是不好的设计(这是您的方法Car::wheel()
所做的)。
class Car {
private $wheel;
private $heightM;
function __construct($metres) {
$this->wheel = new Wheel(0);
$this->setHeight($metres);
}
final public function setHeight($metres) {
$this->heightM = $metres;
$this->wheel->setDiameter(0.2 * $metres);
}
final public function getHeight() {
return $this->heightM;
}
public function getWheelDiameter() {
return $this->wheel->getDiameter();
}
}
class Wheel {
private $diameter;
final public function __construct($diameter) {
$this->setDiameter($diameter);
}
final public function getDiameter() {
return $this->diameter;
}
public function setDiameter($diameter) {
$this->diameter = $diameter;
}
}
$toyota = new Car(1);
print "Car height = " . $toyota->getHeight() . "m"
. "; Wheel diameter = " . $toyota->getWheelDiameter() . "m"
. PHP_EOL;
$toyota->setHeight(2);
print "Car height = " . $toyota->getHeight() . "m"
. "; Wheel diameter = " . $toyota->getWheelDiameter() . "m"
. PHP_EOL;
我尝试对代码进行最小的更改,以向您展示如何执行此操作。
根据您打算如何使用这些类,最好将Wheel
类设为只读(删除其setDiameter()
方法)并将Wheel
对象传递给Car
对象的构造函数。另外,我会删除类setHeight()
的{{1}}方法。更改车轮类型或添加附件时,车辆的高度会发生变化。所有这些行动都应该有自己的方法。我还会改变Car
的实现以使用轮尺寸。
Car::getHeight()
这样一切都很清楚。车轮高度不会改变,车高也不会改变。但是你可以把不同尺寸的车轮放在车上,车的总高度也会这样改变。
答案 1 :(得分:1)
在您的示例中,无需通过引用传递$car
Wheel
的构造函数。你想通过引用传递它来实现什么目标?
您的代码不会破坏PHP:)
您可能想问自己的一件事是Wheel
是否需要对其所属的Car
有如此深入的了解。
例如:它是否真的需要访问其他(未来?)Car
属性,例如maxSpeed
?它的fuelConsumptionRate
?它的price
?
是否需要访问其startEngine()
和parkHere()
功能?
此外,将来您是否可能希望将Wheel
放在其他内容上,例如SupermarketShoppingCart
或Rollerblades
?
考虑没有Wheel
完全引用Car
。你可以做的一件事就是定义一个接口,比如IWheelOwner
,它将声明Wheel
可能期望其所有者的属性和功能。然后让Car
实现该接口。然后,您仍然可以将$car
传递给$wheel
,但将其视为接口:
/** @var IWheelOwner */
private $wheelOwner;
final public function __construct(IWheelOwner $wheelOwner) {
$this->wheelOwner = $wheelOwner;
}
Car
调整Wheel
的大小。如果您将20%的比率视为" Wheel
知识" Car
应该不知道 - 只需提供Wheel
readjustDiameter($ownerHeight)
个功能,并在Car
的高度发生变化时调用它。答案 2 :(得分:-1)
他们会这样做,就是把一个轮子变成一个具有静态功能的类。 Php不支持多重继承,如果不明显,指针引用会变得很忙(只需在2周内查看代码)。
class Car {
private $heightM;
final public function setHeight($metres) {
$this->heightM = $metres;
}
final public function height() {
return $this->heightM;
}
final public function wheel() {
return Wheel::getWheel($this);
}
}
class Wheel {
public static function getWheel($car) {
$wheel = [];
$wheel["diameter"] = $car->height() * 20 / 100;
return (object)$wheel;
}
}
$toyota = new Car();
$toyota->setHeight(1);
print "Wheel diameter = " . $toyota->wheel()->diameter . "m" . PHP_EOL;
$toyota->setHeight(2);
print "Wheel diameter = " . $toyota->wheel()->diameter . "m" . PHP_EOL;