2012-04-18 17 views
3

私はあなたの中にはインターフェイスや抽象を想定しているものもありますが、状況の一部しか処理しないことは知っています。ここではそれらが壊れる例があります。PHPでカスタムクラスをキャストする最善の方法は何ですか

は、私たちが同じインターフェイスを実装するクラスを持っており、いくつかの状況で

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、色、などとそれぞれに様々な車種をチェックする必要が想像を...

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

これらの 'のvar tank1;'宣言は、 '$ tank1を保護する必要があります;' - ドル記号が必要とされ、あなたが入る必要がありますそれらを「私的」または「保護された」ものに隠す習慣があります。 @dan-leeのように、 'fillTank'はおそらくインスタンスメソッドであるべきです。 – halfer

+0

また、あなたの例でタンク変数を再作成しようとしているようですが、それはあなたが望むものではないと思います。特定のクラスの新しいインスタンスをインスタンス化する方法を作成し、共通のデータを一方から他方にコピーすることによって、ふるい分けの再作成を行うことができますが、私はあなたがそれをとにかく必要とは思わないでしょう。 – halfer

+0

@halfer変数について申し訳ありません...私はそれをすばやく行いました。私はあなたがデータをコピーすることによって何を意味するのか理解していますが、実際のキャスティングが私に値を与えるときに余分な操作(classAの読み込み、classAの小道具のコピー、クラスBへの小道具のコピー) –

答えて

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ガスタンクを持っていることを想定しているということです。特定のスポーツカーだけが複数のガスタンクを持っています。

別のアプローチは、traitsavailable as of PHP 5.4)を見てみることです。同じクラスを拡張しないオブジェクト間で、インターフェイスと実装を強制することができます。

- 更新 - (回答に基づいて)

アップデート:私の「本当の」場合は...私は、さまざまな車種、異なるペイント、ボディ、インテリアとのそれぞれをチェックする必要が想像します、gas_type、cleaner_type、色、等...

あなたが言及するすべてのこれらの属性

は車ですが、私はあなたがして、車両クラスにすべてのこれらの属性を追加するなどの属性がないgastation、fillingstation、parkinglotなど、属性車を操作して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); 
} 
+0

これはまた、各オブジェクトに「独自の作業をさせる」という悪いアプローチではありません...私はそれに問題があります。 ...これはGasStationクラスの「シングルトンのような」メソッドではなく、バイトコードの100倍です...私は十分に調べていません。 –

+1

@ReshapeMedia:このレイヤーの最適化のためにカプセル化を中断しないように注意する必要があります。パフォーマンスが問題になった場合、私たちはいつもオペレーションを高速化するためのオペレーションコードキャッシュメカニズムを導入することができます。 –

+0

抽象メソッドとインターフェイスメソッドを使用してオブジェクトの実行を合理化することについての良い点。 –

2

あなたがあなた自身のクラスをキャストすることはできませんが、あなたは変数を省略して、新しいクラスをインスタンス化する必要があります。

あなたは本当に奇妙で矛盾した定義方法があります。なぜあなたはそれを埋める/タンクするグローバル関数を作成しますか?これは、クラス自体のメソッドである必要があります、離れて処理を与えないでください。

:代わりに、すべての車が

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 
{ 
    // .... 

を派生元となる今、あなたは、あなたの抽象クラスを定義

class FillingStation 
{ 
    protected $vehicles = array(); 

    public function addVehicle(Vehicle $vehicle) { 
     $this->vehicles[] = $vehicle; 
    } 

    public function fillTanks() { 
    foreach($this->vehicles as $vehicle) { 
     $vehicle->fillTank(); 
    } 
    } 
} 

のような抽象クラスを使用します

$parkingLot = new FillingStation(); 
$parkingLot->addVehicle(new Car()); 
$parkingLot->addVehicle(new SportsCar()); 

$parkingLot->fillTanks(); 

これは、あなたのニーズをフルフィルにするために、少し変更する必要がある提案ですが、私が作ろうとしていることがはっきりしていることを願っています。

+0

私は車の 'fillTank'と属性を作りましたが、良い提案です。 –

+0

あなたは完全に正しいですが、はるかに理にかなっています。私はこの部分を編集しました、言及のおかげで:) –

+0

私はそれを少し考え、いくつかの間違いを見ました。私は今それが理にかなっていると思う。 –

1

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

しかし、pumpAndPayメソッドは、したがってそのクラスは、車のクラスに密接に結合されています。クラスがチェックできる別のインタフェースを作成する必要があります(リストにある2つのメソッドを使用してください)。インタフェースは、あなたの質問に例えば:

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

interface IPaygasMultiTank extends IPaygas 
{ 
    private tank2; 
} 
+0

instanceofとis_aはしばしばコードの匂いです。オブジェクトのクラスをチェックするよりも、通常は常に優れた解決策があります。 – dqhendricks

+0

@dqhendricks:合意。しかし、私は彼がインターフェイス*をチェックするためにそれを使うことを提案しています(そしてそれらは拡張することができます)。 – webbiedave

+0

@webbiedave私はこれも複数のインターフェースを追加することを考えましたが、問題はオブジェクトを受け取るメソッドになります。それでも、1つのタイプの変数しか取ることができません。しかし、良いアイデアはすべて同じです。 –

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); 
    } 
+0

Mike Purcellの答えに似ています。それぞれのオブジェクトに「独自の作業をする」という答え –

0

あなたがしなければならないんです、このです:

if($val instanceof whatever.class){ 
    $val->dostuff(); 
} 
関連する問題