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.
精彩评论