4

我知道你们中的一些人会假设接口或抽象,但这只能处理一些情况。这是他们打破的一个例子。

假设我们有实现相同接口并扩展相同基的类

  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
     }

   }

仅接口和抽象只会走这么远......

4

5 回答 5

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从 PHP 5.4 开始可用)。看来您可以跨不扩展同一类的对象强制执行接口和实现。

- 更新 -

更新(基于响应):在我的“真实”案例中......想象我必须检查各种车辆类型,每种类型都有不同的油漆、车身、内饰、gas_type、cleaner_type、颜色等......

您提到的所有这些属性都是车辆属性,而不是加油站、加油站、停车场等属性,因此我会将所有这些属性添加到车辆类中,然后您可以将车辆传递给GasStation::cleanVehicle()工厂方法来操作车辆属性。

下面的代码片段仅用于演示,演示了如何将上述属性附加到车辆类,以及该类如何根据车辆类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);
}
于 2012-04-18T21:13:07.320 回答
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();

这应该作为一个建议,当然您需要对其进行一些更改以满足您的需求,但我希望我试图表达的观点是明确的。

于 2012-04-18T20:31:18.843 回答
1

使用instanceofis_a来确定对象类或接口。

但是,该pumpAndPay方法及其类与汽车类紧密耦合。您应该创建另一个类可以检查的接口(使用我列出的两种方法)。请记住,接口可以扩展,例如:

interface IPaygas
{
    private tank1;
    public function payGas($amount);
}

interface IPaygasMultiTank extends IPaygas
{
    private tank2;
}
于 2012-04-18T20:35:51.157 回答
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);
   }
于 2012-04-18T21:35:53.317 回答
0

您所要做的就是:

if($val instanceof whatever.class){
  $val->dostuff();
}
于 2015-10-27T17:01:18.100 回答