我正努力为我的项目设计一个坚实的架构。特别是,我不知道如何处理模型和控制器之间的通信。
我的目标是:
我读过很多代码基,但没有一个正确地完成了上面的所有操作。要么是因为根本不做错误处理,要么是因为他们只做模型中的所有事情。
所以我采用了这种方法,但我不确定我是否做得对,或者我做了什么可怕的事情。
首先,我将在这里列出代码的主要方面:
下面是一个控制器的示例,UserController:
class UserController {
public function __construct(
private User $user,
private ResponseHandler $response_handler
) {}
public function changeAvatar() {
$this->user->changeAvatar( /* arguments */ );
$this->handleResults($this->user);
}
private function handleResults( $model ) {
// NOTE: this function *could also be put in the front controller / main caller
// so that we don't have to repeat this in every controller.
// *should/is, I'm placing it here so you can see how it works
$map = array(
'User' => array(
User::ERROR_INVALID_AVATAR_EXTENSION => array(
400,
TEXT_INVALID_AVATAR_EXTENSION,
LogLevel::ERROR
),
User::ERROR_OVERSIZED_AVATAR => array(
// 413 = payload too large, idk if it fits here
// anyway it's just an example
413,
TEXT_OVERSIZED_AVATAR,
LogLevel::ERROR
),
User::ERROR_CANNOT_CREATE_AVATAR => array(
500,
TEXT_CANNOT_CREATE_AVATAR
LogLevel::ERROR
)
User::STATUS_AVATAR_CREATED => array(
// 201 = created
201,
TEXT_AVATAR_CREATED,
LogLevel::INFO
),
User::STATUS_AVATAR_UPDATED => array(
200,
TEXT_AVATAR_UPDATED,
LogLevel::INFO
)
),
'SomeOtherModel' => array(
SomeOtherModel::STATUS_IDK_SUCCESS => array(
200,
TEXT_BLAHBLAH,
LogLevel::INFO
)
);
foreach($model->results as $result) { // of course before we check if the key exists in $map
if(in_array($result[0], $map[get_class($model)])) {
$this->response_handler->setStatusCode($map[$result[0]][0]);
// addMessage( level, message, interpolation args )
$this->response_handler->addMessage($map[$result[0]][2], $map[$result[0]][1], $result[1]);
} else {
$this->response_handler->setStatusCode(500);
$this->response_hanlder->addMessage(TEXT_UNDEFINED_RESULT);
}
}
// Then in the caller we one of these:
// $response_hanlder->buildJSONResponse();
// $response_hanlder->buildHTMLResponse();
}
}下面是User模型:
class User {
public $results = array();
public const ERROR_INVALID_AVATAR_EXTENSION = 0;
public const ERROR_OVERSIZED_AVATAR = 1;
public const ERROR_CANNOT_CREATE_AVATAR = 2;
public const STATUS_AVATAR_CREATED = 3;
public const STATUS_AVATAR_UPDATED = 4;
public function changeAvatar( /* arguments */ ) {
if( /* invalid extension */) {
$this->results[] = array(
self::ERROR_INVALID_EXTENSION,
array( /* details for the interpolation message */)
);
return false;
}
if( /* oversized avatar */) {
$this->results[] = array(
self::ERROR_OVERSIZED_AVATAR,
array( /* details for the interpolation message */)
);
return false;
}
try {
if( /* avatar exists */ ) {
/* update avatar */
$this->results[] = array(
self::STATUS_AVATAR_UPDATED,
array( /* interpolation args */)
);
} else {
/* create avatar */
$this->results[] = array(
self::STATUS_AVATAR_CREATED,
array( /* interpolation args */)
);
}
return true;
} catch( /* idk exception */ ) {
if( /* cannot create avatar */) {
$this->results[] = array(
self::ERROR_CANNOT_CREATE_AVATAR,
array( /* details for the interpolation message */)
);
}
return false;
}
}
}那么,我对这种方法有什么不好的感觉呢?$results数组。它的结构与模型“绑定”,虽然它依赖于语言的原语,但它可以被认为是一种耦合形式。我可以为结果编写一个包装类,比如class Result{ public int $error_code; public array $interpolating_args; },但是这意味着将模型绑定到这个新类,我不喜欢它。
尝试不同的方法怎么样?对于插值字符串,我们需要参数,这是在模型中,所有的方法得到这些参数是有点丑陋的。另一种方法是直接在模型内插入结果消息,但这会将模型与字符串内插代码耦合起来。
我看不出有什么别的办法。我对在模型中添加更多的抽象层有点怀疑;我可以从模型类中提取错误代码,为每个模型构建一个临时错误类,尽管如此,我会将模型文件分为三层,整个过程背后的核心逻辑将是相同的。见下面的代码:
class UserErrorType {
public const ERROR_INVALID_AVATAR_EXTENSION = 0;
public const ERROR_OVERSIZED_AVATAR = 1;
public const ERROR_CANNOT_CREATE_AVATAR = 2;
public const STATUS_AVATAR_CREATED = 3;
public const STATUS_AVATAR_UPDATED = 4;
}
class UserError {
public UserErrorType $type;
public array $interpolation_args;
public function __construct(UserErrorType $type, array $interpolation_args){
$this->type = $type;
$this->interpolation_arg = $interpolation_arg;
}
}我的问题与这个问题有关:考虑到我希望我的模型是模块化的(这意味着它们可以像从我的项目中提取出来,很容易地投入到任何其他东西中),那么他们在外部“交流”的“正式方式”是什么呢?我能想出的“外部通信”的唯一方法是两种:一个$result数组和一个更整洁的Result类--结果类--但是必须随每个您想插入/插件的模块一起使用,因此我不喜欢它。
发布于 2021-12-28 20:54:50
快速回答:
几年前我也做过类似的事。
在$results的情况下,采用O.O.包装方法。而且,是的,这将是更多的代码,但更多的控制。
特别是,当您有太多的参数时,这些参数虽然显示为文本,但最初是不同的类型,转换代码可以在这个新的类中处理,由UserController类调用,而不是UserController类本身。
也得到了本地化的文本。
对于HTML & JSON响应,还需要至少有三个类:一个基类加上每个子类。
https://softwareengineering.stackexchange.com/questions/435536
复制相似问题