在 PHP 中设计服务层类

发布于 2024-11-17 18:14:04 字数 2582 浏览 4 评论 0 原文

最近介绍到服务层Jani Hartikainen 讨论如何在 MVC 应用程序中最好地处理表单数据。在阅读一些内容之后 我确实看到了这种方法的好处。我的问题是:

服务类应该如何构建?

  • 首先,user_service() 是适合我的 user() 模型的类名吗?还有另一个标准吗?
  • 由于我的服务中的方法只会执行一项任务,因此认为这些方法始终可以是静态函数是否正确?服务类并不代表数据,而是一系列操作,因此这似乎是合适的。
  • 服务方法是否应该只接受一个参数(即一个数组)?

考虑表单已将数据发布到控制器以保存用户数据:

<?php

    class form_controller extends controller
    {

        public function process_submit()
        {
            if(user_service::update_preferences($_POST))
            {

                echo json_encode(array('success' => true));
            }
            else
            {
                echo json_encode(array('success' => false));
            }
        }

    }

    class user_service
    {

        // Accepts array()
        public static function update_preferences($fields)
        {

            // Check for required fields
            if((
                isset($fields['firstname']) and
                isset($fields['lastname']) and
                isset($fields['email'])
                ) == false
            {
                return false;
            }

            // Update user
            try
            {
                $s = new user();
                $s->set_firstname($fields['firstname']);
                $s->set_lastname($fields['lastname']);
                $s->set_email($fields['email']);
                $s->update();

                return true;
            }
            catch(Exception $e)
            {
                return false;
            }
        }
    }

我认为这是一个很好的方法,因为:

  • 我可以向表单添加另一个字段,但我不会必须更新控制器,仅更新服务。控制器不应该关心正在传递什么数据,而只关心它被传递,这似乎是正确的。这使我的控制器和模型中的逻辑保持较小。
  • 如果我没有传递数组,我可以设置具有多个参数的函数。例如,我的函数可以是 update_preferences($firstname, $lastname, $email)。然而,这可能会导致函数具有超过 20 个参数(对于大型形式),并且顺序将变得难以使用。
  • 我可以传递一个对象,但这有意义吗?如果我正在创建一个对象,它应该是它所代表的对象(在本例中为用户),对吧?但是控制器实例化用户对象有意义吗?这难道不是服务层的全部意义所在吗?
  • 也许有一些参数需要使用一些具有多个参数的方法(当只有一到三个参数时)和一些接受数组的方法(当有很多字段时)。这看起来像是一场噩梦,因为您总是必须引用该类才能知道该特定方法的要求是什么。

有人对这里正确的做法是什么有意见吗?我走在正确的轨道上吗?你过去做过什么?多谢!

I was recently introduced to service layers by Jani Hartikainen in a discussion about how to best handle form data in a MVC app. After doing some reading I can really see the benefits of this approach. My question is this:

How should services classes be structured?

  • First, is user_service() an appropriate class name for my user() model or is there another standard?
  • Since the methods in my service will only be doing one task, is it correct to think that these can always be a static function? A service class isn't representing data, but rather is a series a actions, so this seems appropriate.
  • Should a service method only accept one argument, which would be an array?

Consider a form has posted data to a controller to save user data:

<?php

    class form_controller extends controller
    {

        public function process_submit()
        {
            if(user_service::update_preferences($_POST))
            {

                echo json_encode(array('success' => true));
            }
            else
            {
                echo json_encode(array('success' => false));
            }
        }

    }

    class user_service
    {

        // Accepts array()
        public static function update_preferences($fields)
        {

            // Check for required fields
            if((
                isset($fields['firstname']) and
                isset($fields['lastname']) and
                isset($fields['email'])
                ) == false
            {
                return false;
            }

            // Update user
            try
            {
                $s = new user();
                $s->set_firstname($fields['firstname']);
                $s->set_lastname($fields['lastname']);
                $s->set_email($fields['email']);
                $s->update();

                return true;
            }
            catch(Exception $e)
            {
                return false;
            }
        }
    }

I feel this is a good approach because:

  • I can add another field to my form and I won't have to update the controller, just the service. It seems right that the controller shouldn't be concerned about what data is being passed, just that it is passed. This keeps my controller small, and the logic in my models.
  • If I didn't pass an array, I could setup functions with multiple arguments. For example my function could be update_preferences($firstname, $lastname, $email). This however could make for functions with upwards of 20 arguments (for large forms), and the order would just become terrible to work with.
  • I could pass an object, but does that make sense? If I'm creating an object, it should the be the object it represents (the user in this case) right? But does it make sense that the controller instantiates the user object? Isn't that the whole point of the service layer in the first place?
  • Maybe there is an argument for having some methods with multiple arguments (when there are just one to three) and some methods that accept an array (when there are lots of fields). This just seems like it could be a nightmare, as you would always have to reference the class to know what that particular method was asking for.

Does anyone have an opinion on what the right thing to do here is? Am I on the right track? What have you done in the past? Thanks a lot!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

与君绝 2024-11-24 18:14:04

首先, user_service() 是适合我的 user() 模型的类名还是有其他标准?

这是可以接受的。但是,您应该使用已建立的 PHP 编码约定之一,例如 PEAR 或 ZF 约定。在这两种情况下,类名称均为 UpperCamelCase ,方法名称为 lowerCamelCase。使用它,类将是 UserUserService

由于我的服务中的方法仅执行一项任务,因此认为这些方法始终可以是静态函数是否正确?服务类并不代表数据,而是一系列操作,因此这似乎是合适的。

不。将方法设为静态是一个糟糕的设计选择 - 这适用于大多数代码,而不仅仅是服务。服务的主要原因之一是,通常您的服务需要与数据存储或代表数据层的另一个类(存储库、数据访问对象等)进行交互。

当您的服务具有静态方法时,这意味着您需要在方法中实例化依赖项。这反过来意味着,除其他外,代码变得难以测试,因为您无法轻松替换依赖项。

例如这里有一些很好的读物 (事实上,该博客上的几乎所有内容都适合软件开发人员阅读)

服务方法是否应该只接受一个参数(数组)?

这取决于该方法的作用。假设您处理表单结果集的示例,那么是的,这可能会起作用。在其他情况下,这可能是一个糟糕的选择。

我可以在表单中添加另一个字段,并且不必更新控制器,只需更新服务。 [...]

如果我没有传递数组,我可以设置具有多个参数的函数。 [...]

是的,在我看来,您对这两种情况的论证对于这个用例来说非常正确。

我可以传递一个对象,但这有意义吗?如果我正在创建一个对象,它应该是它所代表的对象(在本例中为用户),对吧?但是控制器实例化用户对象有意义吗?这难道不是服务层的全部意义所在吗?

这取决于。例如,如果您使用的框架允许您将表单表示为对象(例如 Zend Framework 和 Zend_Form),您可以考虑将表单对象直接传递给服务。

也许有一些方法有多个参数(当只有一到三个参数时)和一些接受数组的方法(当有很多字段时)。这看起来像是一场噩梦,因为您总是必须引用该类才能知道该特定方法的要求。

通常,您的目标应该是根据方法名称使参数至少有一半是可猜测的。在我从事的工作中,我们有一个模型,其中包含例如企业和产品,企业可以在其中赞助产品。在 ProductService 中,我们有一个名为 sponsorProduct 的方法,该方法将业务和产品作为参数。您几乎可以猜到需要这两个(如果您熟悉代码库的话)

IDE 通常也会帮助您完成此任务 - 它们提供代码辅助来显示参数函数所采用的内容。这是我认为 IDE 在大型项目中非常有用的主要原因之一,在大型项目中,您无法始终记住某个函数到底需要什么作为参数。

至于参数数量,我认为通常你应该尝试使用单独的参数。这使得任何人都可以通过查看函数的签名轻松地了解需要哪些参数,并且允许您非常轻松地定义类型提示和默认值。

然而,当你获得如此多的参数时,就会显得太多了。这可能是 +5 左右,具体取决于它是什么类型的方法。在这种情况下,您可以考虑使用数组或称为参数对象的东西,它本质上是一个包含调用的所有参数的对象。有关参数对象的更多信息此处

First, is user_service() an appropriate class name for my user() model or is there another standard?

That's acceptable. However, you should rather use one of the established PHP coding conventions, such as the PEAR or ZF conventions. In both cases, class names are UpperCamelCase and method names lowerCamelCase. Using this, the classes would be User and UserService

Since the methods in my service will only be doing one task, is it correct to think that these can always be a static function? A service class isn't representing data, but rather is a series a actions, so this seems appropriate.

No. It's a poor design choice to make methods static - and this applies to most code, not just services. One of the main reasons in case of a service would be that generally your service needs to interact with a data store or another class which represents the data layer (repository, data access object, whatever).

When your service has static methods, this means you would need to instanciate your dependencies in your methods. This in turn means that, amongst other things, the code becomes hard to test, as you can't easily replace the dependencies.

There's some good reading on this for example here (In fact nearly everything on that blog is good reading for software devs)

Should a service method only accept one argument, which would be an array?

This is dependent on what the method does. Assuming your example of processing a form's resultset, then yes this will probably work. In some other case it might be a poor choice.

I can add another field to my form and I won't have to update the controller, just the service. [ ... ]

If I didn't pass an array, I could setup functions with multiple arguments. [ ... ]

Yep, your argumentation for these two cases is pretty much spot on for this use-case in my opinion.

I could pass an object, but does that make sense? If I'm creating an object, it should the be the object it represents (the user in this case) right? But does it make sense that the controller instantiates the user object? Isn't that the whole point of the service layer in the first place?

This depends. For example, if you were using a framework which lets you represent forms as objects (such as the Zend Framework and Zend_Form), you could consider passing the form object straight to the service.

Maybe there is an argument for having some methods with multiple arguments (when there are just one to three) and some methods that accept an array (when there are lots of fields). This just seems like it could be a nightmare, as you would always have to reference the class to know what that particular method was asking for.

You should usually aim to make the parameters at least half-guessable based on the method's name. In something I work on, we have a model which has for example businesses and products, where a business can sponsor a product. In a ProductService, we have a method called sponsorProduct which takes a business and a product as parameters. You can pretty much guess it would take these two (if you were familiar with the codebase anyway)

IDE's generally help you with this too - they provide code-assist which displays what params functions take. This is one of the main reasons I think IDE's are very useful in larger projects where you can't always remember what exactly a certain function needs as parameters.

As for parameter count, I think usually you should try to have separate parameters. This allows anyone to easily see what parameters are required by just looking at the function's signature, and allows you to define typehints and default values quite easily.

However there is a point when you get so many parameters it's too much. This is maybe at +5 or so, depending a bit on what sort of method it is. In this case you can consider using an array, or something called a Parameter Object, which is essentially an object that contains all the parameters for the call. More on parameter objects here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文