开发者

Initializing class with bad input makes it unusable but object is still existent

If I want to create a new object that needs certain informations like a product id or something like that but the input is bad how can I elegant manage suc开发者_开发技巧h a case?

class Product
{
function __construct($id)
    {
    if(is_invalid_id($id))
        { return false; }
    }
}

If I initialize it this way I still get an object (since return inside a constructor doesn't return anything). Another way would be to use exceptions which I then can catch but that's kinda unelegant. 3rd option is to use a static function which then checks the input and then returns the object.

class Product
{
static function init($id)
    {
    if(is_invalid_id($id))
        { return false; }
    return new self($id);
    }

private function __construct($id)
    {
    $this->id = $id;
    }
}

$product = Product::init($productId);

The problem here is when I try to extend the class. Either I have to create a init() method for every class I extend (even if it is the exact same code) or return new self() always returns an instance of the parent class.


Throw an exception. Not sure why you consider it unelegant. Man, things were unelegant before exceptions (FALSE, -1, null)


For the problem with self:: you might be able to use late static binding (PHP5.3+):

<?php

class Product
{
static function init($id)
    {
    if(false)
        { return false; }
    $s = get_called_class();
    return new $s($id);
    }

private function __construct($id)
    {
    $this->id = $id;
    }
function getId()
    {
    return "Product-$this->id";
    }
}


class Headphones extends Product
{
function getId()
    {
    return "Headphones-$this->id";
    }
}

$c1 = Product::init(1);
$c2 = Headphones::init(1);
printf("c1 is %s, c2 is %s\n", $c1->getId(), $c2->getId());
// Prints: c1 is Product-1, c2 is Headphones-1
?>


Your third option is the Factory Pattern.

As you've noticed, the downside is that each class that needs this kind of check generally needs it's own factory method.


I'll give you a non-standard workaround that's universally frowned upon by purists: the hybrid constructor

And it's even more evil than it sounds, because it's actually just a wrapper procedure:

function Product($i) {
    $prod = new Product($i);
    return $prod->valid() ? $prod : new InvalidProduct();
}

class Product {
    function __construct() { ... }
}
class InvalidProduct extends Product implements Stub { }


$new_prod = Product("oops123");   // what will it be?

It simply verifies the object instantantly. If there is something wrong, and now here comes the trick, return a specific stub or NULL object. It might implement a few of the interfaces, but generally cause no side-effects or print an error message once it's inevitable. This concept basically hinges on the viability of carrying a stub object around. It's sometimes more sensible to the application logic to have such a specialized instance than to use decorative test logic.

And then there's the second alternative: just bail with an exception.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜