工厂方法不起作用:改为返回父类


Factory method not working: returns parent class instead

我正在尝试实现我认为是工厂类的东西。我有一个API框架。在我的前端控制器处理完请求后,它会尝试将输出返回给客户端。我的前控制器中的呼叫如下:

<?php
...
$response_obj = new Response($response_str, 'json');
echo $response_obj->render();
?>

我的Response类基本上将第二个参数作为要实例化的类的类型,并将$response_str的内容传递给这个新类。这是:

<?php
class Response {
    public function __construct($data, $format) {
        switch ($format) {
            case 'json':
                $obj = new ResponseJson($data);
            break;
        }
        return $obj;
    }
}

然后我的ResponseJson类看起来如下:

<?php
class ResponseJson {
    protected $data;
    public function __construct($data) {
        $this->data = $data;
        return $this;
    }
    public function render() {
        header('Content-Type: application/json');
        return json_encode($this->data);
    }
}

然而,在我的前端控制器中,$response_obj将其类型返回为Response,而不是预期的ResponseJson,并且对render()方法的调用(存在于ResponseJson中,而不是Response中)会引发致命错误:

致命错误:调用第61行/Users/Martin/Sites/api framework/index.php中的未定义方法Response::render()

我哪里错了?

您所描述的不是工厂,而是无效代码。(__construct将始终返回在其中实现的类的新实例。事实上,它不需要返回任何内容,因为这没有任何效果!)

工厂通常被实现为一个静态函数,可以在不实例化工厂类的对象的情况下调用。然后,它创建一个类的对象实例(在的大多数情况下,与您的情况一样,是另一个类)并返回该实例。

你的响应类应该是这样的:

<?php
class Response {
    static public function create($data, $format) {
        switch ($format) {
            case 'json':
                $obj = new ResponseJson($data);
                break;
            case default:
                return NULL; // or throw exception!
                break;
        }
        return $obj;
    }
}

你的前端控制器是这样的:

<?php
...
$response_obj = Response::create($response_str, 'json');
echo $response_obj->render();
?>

我想你误解了工厂模式:

构造函数方法是在类实例化为对象时自动调用的,我相信你知道。

对象的实例化总是返回对象本身,对象的实例化返回除自身之外的任何东西,尤其是不同类型的对象,这违反了很多逻辑。由于这个规则,构造函数方法不能返回,它总是只在实例化时被调用,从而返回对象本身。

我不完全清楚你的背景,但在这种情况下,你想要一个工厂似乎很奇怪,我希望ResponseJson扩展响应,而不是由它创建的。

希望这能有所帮助!

如果你真的想实现一个工厂模式,你可以做一些事情,比如:

//Pseudo-code
class ResponseFactory
{
    createJSONResponse($data)
    {
        return new JSONResponse($data);
    }
    createXMLResponse($data)
    ...
    createDefaultResponse($data)
    {
        return createJSONResponse($data);
    }
}

不过,对于响应类型(可能只有2或3种),我认为我会使用Kaii的静态工厂方法。

作为对您评论的回应,我更喜欢标准工厂的原因,每种方法看起来都像上面的,而不是:

createResponse($type, $data)
{
    if ($type == 'json')
        return new JSONResponse($data);
    else if (...)
        ...
}

特别是因为在许多情况下,你的来电者看起来像:

$response = createResponse('json', $data);

我更喜欢这个:

$response = createJSONResponse($data)

尽管我应该注意到php将字符串作为函数调用的能力可以创建一个非常优雅(尽管有些不安全)的抽象工厂。