首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >类MVC体系结构:模型-控制器结果/状态通信

类MVC体系结构:模型-控制器结果/状态通信
EN

Software Engineering用户
提问于 2021-12-23 22:56:28
回答 1查看 337关注 0票数 1

我正努力为我的项目设计一个坚实的架构。特别是,我不知道如何处理模型和控制器之间的通信。

我的目标是:

  • 遵循关注点分离原则,使模型尽可能独立。
  • 对结果消息具有国际化支持
  • 能够使用参数插入结果消息(就像PSR-3中描述的日志一样)
  • 能够将结果消息与严重性级别和HTTP状态代码耦合
  • 能够对HTML和JSON响应使用相同的控制器

我读过很多代码基,但没有一个正确地完成了上面的所有操作。要么是因为根本不做错误处理,要么是因为他们只做模型中的所有事情。

所以我采用了这种方法,但我不确定我是否做得对,或者我做了什么可怕的事情。

首先,我将在这里列出代码的主要方面:

  • 该模型与所有其他类分离
  • 模型中唯一“耦合”的是$results的结构
  • 控制器将应用程序的各个层粘合在一起;它调用控制器并在正确的位置设置东西,准备构建响应,可能是原始的JSON (用于API响应),也可能是用模板引擎构建的HTML响应。
  • 结果消息是国际化的,可以使用一些参数进一步定制。(例如"Yo,欢迎返回{user_name}“或”在此键索引处处理名为{name}的数组时出错:{ key }")

下面是一个控制器的示例,UserController

代码语言:javascript
复制
    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模型:

代码语言:javascript
复制
    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; },但是这意味着将模型绑定到这个新类,我不喜欢它。

尝试不同的方法怎么样?对于插值字符串,我们需要参数,这是在模型中,所有的方法得到这些参数是有点丑陋的。另一种方法是直接在模型内插入结果消息,但这会将模型与字符串内插代码耦合起来。

我看不出有什么别的办法。我对在模型中添加更多的抽象层有点怀疑;我可以从模型类中提取错误代码,为每个模型构建一个临时错误类,尽管如此,我会将模型文件分为三层,整个过程背后的核心逻辑将是相同的。见下面的代码:

代码语言:javascript
复制
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类--结果类--但是必须随每个您想插入/插件的模块一起使用,因此我不喜欢它。

EN

回答 1

Software Engineering用户

发布于 2021-12-28 20:54:50

快速回答:

几年前我也做过类似的事。

$results的情况下,采用O.O.包装方法。而且,是的,这将是更多的代码,但更多的控制。

特别是,当您有太多的参数时,这些参数虽然显示为文本,但最初是不同的类型,转换代码可以在这个新的类中处理,由UserController类调用,而不是UserController类本身。

也得到了本地化的文本。

对于HTML & JSON响应,还需要至少有三个类:一个基类加上每个子类。

票数 0
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/435536

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档