首页 > 解决方案 > 在具有特定或可变参数的接口中编写方法?

问题描述

我正在编写与第 3 方 API 网关的集成,并努力使其尽可能解耦(并且能够在将来更改提供程序),我创建了 3 个接口,其中包含读取、写入的方法并从网关中删除(因为我需要使用很多方法,所以我不想把所有东西都塞进一个大接口中,违反接口隔离原则)。API Gateway 用于处理应用程序创建和其他 CRUD 操作。

而且我不确定前进的最佳方式是什么。我可以创建这样的界面

interface Api_Gateway_Create {
  public function create_app( string $organization, string $developer_id, string $body );

  // other methods.
}

然后,在做具体实现的时候,我会创建一个实现这个的类,当我需要使用它的时候,我需要提供三个参数。

这似乎有点束缚。如果当我更换提供者时,我不再需要$developer_id? 我可以用一些默认值设置所有参数

interface Api_Gateway_Create {
  public function create_app( string $organization, 
           string $developer_id = 'some-default-value', 
           string $body = 'some-default-value' );

  // other methods.
}

但这意味着我最终会得到我可能不需要的参数,这可能会扰乱我的实现。

最后想到的是我可以放一个可变参数,然后让实现处理参数

interface Api_Gateway_Create {
  public function create_app( ...$arguments_list );

  // other methods.
}

在实施中我有

class Create_App_Implementation1 implements Api_Gateway_Create {
  public function create_app( ...$arguments_list ) {
    list( $organization, $developer_id, $body ) = $arguments;
    // Some business logic.
  }
}

或者

class Create_App_Implementation2 implements Api_Gateway_Create {
  public function create_app( ...$arguments_list ) {
    list( $organization, $app_key, $body ) = $arguments;
    // Some business logic.
  }
}

这样我就不需要关心我的提供者是否提供了这些参数,因为我只会实现我需要的那些。

然而,这提出了另一个问题。在消费代码中,比如说一个将create_app()通过依赖注入使用方法的类,我需要特别注意传递正确的值。这不是未来的证明,因为我还需要更改消费类中的代码(在我看来,这似乎与接口的相反意图)。

对于第一个,我将永远不必更改使用代码,因为如果我期望相同的结果(基于相同的输入参数),我不在乎我正在使用哪个提供程序。但正如我已经提到的,这似乎有点限制。两个提供商可能有不同的处理方式。

有没有人不得不面对这种事情,处理这种事情的行业标准是什么?

我正在用 PHP 编写代码,但我认为 Java 也可以适用,因为它具有面向对象的特性。

标签: javaphpinterfaceimplementation

解决方案


有没有人不得不面对这种事情?处理这种事情的行业标准是什么?

  1. 我确定他们有。
  2. 没有“行业标准”的方式来做到这一点。

(您可能应该从词汇表中删除诸如“行业标准”和“最佳实践”之类的短语。在我看来,它们对交流有害。阅读“没有最佳实践”并思考他在说什么。)


我对 PHP 不够熟悉,无法说出用那种语言解决这个问题的常用方法。


在 Java 中,有两种常见的方法:

  1. 为常见情况定义许多不同的方法(或构造函数)重载;例如

        public interface CreateAppApi {
            public String create_app(String organization, String developerId, 
                                     String body);
            public String create_app(String organization, String developerId);
            public String create_app(String developerId);
        }
    

    如果参数都具有相同的类型,这将无法正常工作:不同的重载可能无法区分。

  2. 使用流利的构建器模式;例如定义这样的接口:

        public interface CreateAppRequest {
            public CreateAppRequest organization(String value);
            public CreateAppRequest developer(String developerId);
            public CreateAppRequest body(String body);
            public String send();
        }
    

    并像这样使用它:

        String result = new CreateAppRequestImpl().organization(myOrg)
                              .developer(myId).body(someBody).send();
    

    从 API 用户的角度来看,这很好用,而且很容易发展。(只需添加更多用于向 API 提供新参数并实现它们的方法。)缺点是有更多样板代码。

Java 支持可变参数,但它们不适合您描述的用例。(这适用于您有可变数量的值本质上表示相同事物的情况;例如,表示我们假设的“应用程序”的方法的字符串的内联列表。)


在 Python 中,没有方法重载……因为不需要它。相反: - 您可以使用位置或关键字参数,或它们的组合。- 你也有像*argsand这样的结构**kwargs来传递任意参数而不显式声明它们。

一般来说,关键字参数更适合需要具有不同含义和(可能)类型的各种参数的 API。

所以在这个例子中,你可能会这样写:

class CreateAppRequest(SomeBaseClass):

    def __init__(self, organization='defaultOrg', developer=None, body='',
                 **kwargs):
        super(self, CreateAppRequest).__init__(**kwargs)
        if developer is None:
            raise Exception('developer is mandatory')
        self.developer = developer
        self.organization = organization
        self.body = body

    def send(self):
        ...

builder / fluent 方法也可以在 Python 中使用。


推荐阅读