简体   繁体   English

PHP / OOP设计问题,最佳实践是什么?

[英]PHP/OOP design issue, what is best practice?

I'm using PHP 5.3. 我正在使用PHP 5.3。 I have a Vehicle class. 我有车辆课。 These classes have inherited from Vehicle: Car, SUV, Lorry. 这些类继承自Vehicle:Car,SUV,Lorry。

I need to get an array holding a specific type of objects inherited from Vehicle. 我需要获取一个数组,其中包含从Vehicle继承的特定类型的对象。 It can be an array of Cars, SUVs, etc... 它可以是一系列汽车,SUV等。

The way I solved it was that I created a class named CarCollection with a static method, which returns the list of cars. 解决问题的方法是用静态方法创建了一个名为CarCollection的类,该类返回汽车列表。 Then a class named SUVCollection for SUV... 然后是一个名为SUVCollection的SUV类...

But if I were to add a new vehicle class (let's call it Airplane), then I would need to create a new AirPlaneCollection class. 但是,如果我要添加一个新的飞行器类(我们称之为Airplane),那么我将需要创建一个新的AirPlaneCollection类。 Is this a bad choice? 这是一个不好的选择吗?

The goal: I'm looking for a way to implement this using only one class, VehicleCollection returns a list of Cars, SUV etc. 目标:我正在寻找一种仅使用一个类来实现此目标的方法,VehicleCollection返回汽车,SUV等的列表。

How can I know in the code that in this specific script the VehicleCollection::getVehicles() will return cars and not SUV? 我如何在代码中知道在此特定脚本中,VehicleCollection :: getVehicles()将返回汽车而不是SUV? Maybe I can have some logic the class checking from where it was called, or I send in the caller object as a parameter, then check which class it is and according to that get VehicleCollection::getVehicles() to return for examle SUV and not Car. 也许我可以从类的调用位置检查类的逻辑,或者我将调用者对象作为参数发送,然后检查它是哪个类,然后根据该get VehicleCollection :: getVehicles()返回例如SUV的类,而不是汽车。

You could try making your own collection type, which validates that everything is a vehicle, but also enforces they're all the same type of vehicle. 您可以尝试创建自己的集合类型,该集合类型可以验证所有东西都是车辆,还可以强制要求它们都是相同类型的车辆。

<?php
class Vehicle {}

class Car extends Vehicle {}

class SUV extends Vehicle {}

// This doesn't need to be an SplDoublyLinkedList, it's just
// a convenient datastructure to demo with
class VehicleCollection extends SplDoublyLinkedList
{
    public function add($index, Vehicle $obj)
    {
        $this->validateType($obj);
        parent::add($index, $obj);
    }

    public function push(Vehicle $obj)
    {
        $this->validateType($obj);
        parent::push($obj);
    }

    protected function validateType($obj)
    {
        // If we have anything in here, ensure next is the same vehicle type
        if (!($this->isEmpty() || $this->top() instanceof $obj)) {
            throw new InvalidArgumentException('Argument passed to ' . __CLASS__ . '::' . __FUNCTION__ . ' must all be instances of same type.');
        }
    }
}

// Make a new collection
$col = new VehicleCollection();

// Let's have a couple cars
$car = new Car;
$car2 = new Car;

// And an SUV
$suv = new SUV;

// Let's add our cars
$col->push($car);
$col->push($car2);

var_dump($col);
/* Collection right now:
class VehicleCollection#1 (2) {
  private $flags =>
  int(0)
  private $dllist =>
  array(2) {
    [0] =>
    class Car#2 (0) {
    }
    [1] =>
    class Car#3 (0) {
    }
  }
}
*/

// Now we try to add an SUV
$col->push($suv);

// and get this:
// PHP Fatal error:  Uncaught exception 'InvalidArgumentException' with message 'Argument passed to VehicleCollection::validateType must all be instances of same type.'

This has the added benefit that if you further extended, eg made a class SportsCar extends Car {} , that SportsCar could go into your collection. 这样做还有一个好处,就是如果您进一步扩展(例如,将class SportsCar extends Car {} ,SportsCar 可以进入您的收藏夹。

It was pointed out that I might have been misinterpreting your question. 有人指出,我可能一直在曲解你的问题。 If you're just trying to filter an array, this becomes a much simpler problem. 如果您只是尝试过滤一个数组,这将成为一个简单得多的问题。 I wouldn't bother to even implement a special class if that's the case - just pass a Closure into array_filter , which is quite readable and an easy pattern to follow elsewhere: 如果是这种情况,我什至不愿意实现一个特殊的类-只需将Closure传递给array_filter ,它很容易阅读,并且很容易在其他地方遵循:

$vehicles = [$car, $suv, $car2];
$cars = array_filter($vehicles, function($vehicle) { return $vehicle instanceof Car; });
$suvs = array_filter($vehicles, function($vehicle) { return $vehicle instanceof SUV; });

So in that example, the array of vehicles has an SUV, and once filtered, the $cars array has only the cars. 因此,在该示例中,车辆阵列具有SUV,并且一旦过滤,$ cars阵列仅具有汽车。 If you want to make that a class method, you could do something along the lines of: 如果要使该方法成为类方法,则可以执行以下操作:

public function getAllOfType($type)
{
    return array_filter(
        $this->vehicles,
        function($vehicle) { return is_a($vehicle, $type); }
    );
}

Then to grab only cars from your collection: 然后仅从您的收藏中抢购汽车:

$cars = $myVehicleCollection->getAllOfType('Car');

What about a static property in the parent class Vehicle that would look something like: 父类Vehicle的静态属性如下所示:

array( 'cars' => array(car instances),
   'suvs' =>array(suv instances).
    ....
)

Then you would have to put logic in your constructors and destructors to add and remove the objects from this array. 然后,您将不得不在构造函数和析构函数中放入逻辑,以从此数组添加和删除对象。

Then you can get a list of all cars by calling Vehicle::thatArry['cars'] . 然后,您可以通过调用Vehicle::thatArry['cars']获得所有汽车的列表。 But of course you want to throw some nice getters and setters around that array. 但是,当然,您想在该数组周围抛出一些不错的getter和setter方法。

I haven't totally thought this through yet, it might need some tweaks. 我还没有完全考虑到这一点,可能需要进行一些调整。 But if its totally off, holla at me. 但是,如果它完全消失了,霍拉对我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM