我知道你们有些人会假设接口或抽象,但这只能处理一些情况。这是他们破裂的一个例子。
假设我们有类实现相同的接口并扩展相同的基础
class car extends fourwheeler implements ipaygas{
protected $tank1;
//interface
public function payGas($amount){}
}
class sportscar extends fourwheeler implements ipaygas{
protected $tank1;
protected $tank2;
//interface
public function payGas($amount){}
}
interface ipaygas{
function payGas($amount);
}
在某些情况下,您只需要一个界面,因为您可能只想执行'payGas()'。但是当你有条件要做时,你会怎么做。
例如,如果 - 在支付燃气之前需要(1)检查汽车类型,(2)使用跑车的优质汽油,以及(3)填充跑车的第二辆汽油。
这就是我想要做的但是不能
function pumpAndPay(iPayGas $car){
if(gettype($car) == "car"){
fillTank($car,(car) $car->tank1);
}else{
fillTank($car,(sportscar) $car->tank1);
fillTank($car,(sportscar) $car->tank2);
}
}
如何使用真实类型转换来完成此操作?在PHP中可以吗?
更新(根据回复): 在我的'真实'案例中......想象一下我必须检查各种车型,每种车型都有不同的油漆,车身,内饰,gas_type,cleaner_type,颜色等......
abstract class AVechicle{}
abstract class ACar extends AVechicle{}
abstract class ATruckOrSUV extends AVechicle{}
abstract class ABike extends AVechicle{}
class Car extends ACar{}
class SportsCar extends ACar{}
class SUV extends ATruckOrSUV{}
class Truck extends ATruckOrSUV{}
class Bike extends ABike{}
class Scooter extends ABike{}
class GasStation{
public function cleanVehicle(AVehicle $car){
//assume we need to check the car type to know
//what type of cleaner to use and how to clean the car
//if the car has leather or bucket seats
//imagine we have to add an extra $2/h for sports cars
//imagine a truck needs special treatment tires
//or needs inspection
}
public function pumpAndPay(AVehicle $car){
//need to know vehicle type to get gas type
//maybe we have a special for scooters only, Green Air campaign etc.
}
public function fullService(AVehicle $car){
//need to know if its a truck to do inspection FIRST
$this->cleanVehicle($car);
$this->pumpAndPay($car);
//bikes get 10% off
//cars get free carwash
}
}
接口和摘要本身只会到目前为止......
答案 0 :(得分:3)
对你的问题的简短回答,不,你不能将对象重铸为另一个对象。
然而,Dan Lee的反应非常好,接近我的建议。为什么不将fillTank
作为车辆对象的属性,这样扩展车辆类的所有对象都将知道如何填充自己的坦克。像这样:
abstract class Vehicle
{
protected $tank1;
protected $tank2;
// Declaring an abstract function in parent class forces all child class to
// implement same class
abstract public function fillGas() {}
}
class Car extends Vehicle
{
public function fillGas()
{
$this->tank1 = 'full';
}
}
class SportsCar extends Vehicle
{
public function fillGas()
{
$this->tank1 = 'full';
$this->tank2 = 'full';
}
}
class Skateboard extends Vehicle
{
// Skateboards don't have gastanks, just here to sastify parent abstract definition
public function fillGas() {}
}
当然,你的OP的一个大谬论就是你假设所有的跑车都有两个油箱,事实上并非如此。只有某些跑车有多个油箱。
另一种方法是查看traits
(available as of PHP 5.4)。看来你可以强制实现接口以及不扩展相同类的对象的实现。
- 更新 -
更新(根据回复):在我的真实'案例...想象一下我必须检查各种车型,每种车型都有不同的油漆,车身,内饰,gas_type,cleaner_type,颜色等......
您提到的所有这些属性都是车辆属性,而不是gastation,fillstation,parkinglot等属性,因此我会将所有这些属性添加到车辆类中,然后您可以将车辆传递给GasStation::cleanVehicle()
工厂方法操纵车辆属性。
下面的代码段仅为 DEMONSTRATIVE ,演示了如何将上述属性附加到车辆类,以及GasStation
类如何根据车辆类别操纵车辆属性。我在5分钟内写了以下内容,但显而易见的是,需要更多考虑正确处理工厂方法以及是否传递给其他对象等。请考虑以下内容:
abstract class Vehicle
{
// Setting these to public for demonstration only, otherwise you should set these
// to protected and write public accessors
public $paintType;
public $bodyType;
public $interior;
}
class Car extends Vehicle
{
}
class Suv extends Vehicle
{
}
class Truck extends Vehicle
{
}
class GasStation
{
public static function cleanVehicle(Vehicle $vehicle)
{
switch (get_class($vehicle)) {
case 'Car':
// Car specific cleaning
break;
case 'Truck':
// Truck specific cleaning
break;
default:
throw new Exception(sprintf('Invalid $vehicle: %s', serialize($vehicle)));
}
// We've gone through our vehicle specific cleaning, now we can do generic
if ('Leather' === $vehicle->getInterior()) {
// Leather specific cleaning
}
if ('Sedan' === $vehicle->getBodyType()) {
// Sedan specific cleaning
}
}
}
$car = new Car();
$car->setPaintType = 'Glossy';
$car->setBodyType = 'Sedan';
$car->setInterior = 'Cloth';
$suv = new Suv();
$suv->setPaintType = 'Glossy';
$suv->setBodyType = 'Crossover';
$suv->setInterior = 'Leather';
$truck = new Truck();
$truck->setPaintType = 'Flat';
$truck->setBodyType = 'ClubCab';
$truck->setInterior = 'Cloth';
$vehicles = array($car, $suv, $truck);
foreach ($vehicles as $vehicle) {
GasStation::cleanVehicle($vehicle);
}
答案 1 :(得分:2)
你不能自己编译类,你需要实例化一个新类,省略变量。
你有非常奇怪,不一致的定义方式。你为什么要创建一个填充/坦克的全局函数?这应该是班级本身的一种方法,不要给予处理。而是使用像
这样的抽象类class FillingStation
{
protected $vehicles = array();
public function addVehicle(Vehicle $vehicle) {
$this->vehicles[] = $vehicle;
}
public function fillTanks() {
foreach($this->vehicles as $vehicle) {
$vehicle->fillTank();
}
}
}
现在你定义你的抽象类,每辆汽车都将从中派生出来
abstract class Vehicle
{
public $fillLevel = 0;
abstract public function fillTank();
}
具体类型
class Car extends Vehicle
{
public function fillTank()
{
// ... do some stuff here, e.g.:
$this->fillLevel = 100;
}
}
class SportsCar extends Vehicle
{
// ....
现在粘合在一起:
$parkingLot = new FillingStation();
$parkingLot->addVehicle(new Car());
$parkingLot->addVehicle(new SportsCar());
$parkingLot->fillTanks();
这应该作为一个建议,当然你需要稍微改变一下来满足你的需求,但我希望我尝试做的一点很清楚。
答案 2 :(得分:1)
使用instanceof或is_a来确定对象类或接口。
但是,pumpAndPay
方法及其类与汽车类紧密耦合。您应该创建该类可以检查的另一个接口(使用我列出的两种方法)。 请记住,接口可以扩展,例如:
interface IPaygas
{
private tank1;
public function payGas($amount);
}
interface IPaygasMultiTank extends IPaygas
{
private tank2;
}
答案 3 :(得分:0)
你的错误在这里:
fillTank($car,(sportscar) $car->tank1);
几乎所有的类属性都应该受到保护或私有。基本上一个类不应该对另一个类的属性执行操作。相反,汽车应该处理自己的内部,而PumpAndPay只运行汽车的fillTank方法。
function pumpAndPay(iPayGas $car){
$amount = $car->fillTank();
$car->payGas($amount);
}
interface ipaygas{
function payGas($amount);
function fillTank($amount);
}
答案 4 :(得分:0)
你所要做的就是这样做:
if($val instanceof whatever.class){
$val->dostuff();
}