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) スポーツ カーの 2 番目のタンクを満タンにする必要があるとします。

これは私がやりたいことですが、できません

   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、colorなどがあります...

   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 の大きな間違いは、すべてのスポーツカーに 2 つのガソリン タンクがあると仮定しているということですが、実際にはそうではありません。特定のスポーツカーだけが複数のガソリン タンクを備えています。

もう 1 つの方法はtraits( PHP 5.4 以降で利用可能) を調べることです。同じクラスを拡張しないオブジェクト間で、インターフェースと実装を強制できるようです。

- アップデート -

更新(回答に基づく):私の「実際の」ケースでは...さまざまな車両タイプを確認する必要があると想像してください。それぞれに異なるペイント、ボディ、インテリア、gas_type、cleaner_type、colorなどがあります...

あなたが言及したこれらの属性はすべて、ガスステーション、ガソリンスタンド、駐車場などの属性ではなく、車両属性です。これらすべての属性を車両クラスに追加すると、車両を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

オブジェクト クラスまたはインターフェイスを決定するには、instanceofまたはis_aを使用します。

ただし、pumpAndPayメソッド、つまりそのクラスは、車のクラスに密接に結合されています。クラスがチェックできる別のインターフェイスを作成する必要があります (リストした 2 つのメソッドを使用)。インターフェイスは拡張できることに注意してください。たとえば、次のようになります。

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 に答える