php - 在 OO 上下文中建模更改

标签 php oop encapsulation software-design

Formal semantics of an Object-oriented programming language include encapsulated state. Is there a use-case for encapsulating a potential change, previous to the state change? Although the following examples are in PHP, please also think language-agnostic in your answer.

背景

我有一个 Client负责发送请求并从服务器检索响应的对象,它用于通过调用 API 来更改驻留在另一台服务器上的对象的状态。有几个 Url,一个带有端点 create另一个端点为 update .问题是,update可用于更新给定对象中的多个不同元素,每个元素需要不同的参数。

“图层”对象

想象一下 object拥有以下对象:

  • 图像图层
  • 背景图层
  • 文本图层
  • 音频层
  • (N)层

为了改变ImageLayer ,API 要求:

  • 身份证
  • 新图片的网址

为了改变TextLayer ,API 要求:

  • 身份证
  • 您要对其进行哪些更改(是字体、大小、文本内容吗?)
  • 新值(value)

现在,在您认为这可以简单地抽象为一个 id 和一个替换值之前,请考虑 AudioLayer :

  • 身份证
  • 比特率
  • 新音频文件的url
  • 一些其他值

我的考虑

我本来考虑加上Client::updateImageLayer() , Client::updateTextLayer() , 但后来意识到 Client给定(N)Layer,对象可能会呈指数级增长 future 。

然后我考虑加上Client::updateLayer(Layer $layer, array $values)但我认为这还不够好。

最后,这是我一直在考虑的另一个选择:a Change 对象。

改变对象

如果我创建一个 Change 会怎么样?将更改封装到任何特定层的对象,然后可以将其传递给 Client ,已经过验证,并准备好在 API 请求中发送?

interface Change
{
    public function isValid();

    public function getChanges();
}

class ImageLayerChange implements Change
{
    protected $url;

    protected $id;

    public function __construct($id, $url)
    {
        $this->url = $url;
        $this->id  = $id;
    }

    public function isValid()
    {
        // Use a validator object to check url is valid etc
    }

    public function getChanges()
    {
        return array($this->id, $this->url);
    }
}

使用上面的,Client对象可以围绕一组 Change 循环对象,我什至可以制作一个 ChangeSet , 通过调用 isValid() 确保它们都有效, 并调用 getChanges()将特定数组直接发送到 API,因为它已经过验证。

问题

  • 我以前从未听说过建模更改。您如何看待上述选项?我是不是把事情复杂化了?我喜欢能够从 ChangeSet 添加/删除更改的想法随意,并且一切都按照预期工作,因为它们符合 Change界面。

  • 也许我不是解决这个问题的最佳方式?我的解决方案有什么不好的吗。

  • 在使用我的解决方案或您建议的解决方案时,是否有我正在使用或应该考虑的模式?我对好的代码很感兴趣。

最佳答案

根据您的描述,在我看来您需要一个 API 客户端和请求对象。

namespace Api;

interface Client {

    /**
     * @param string $method
     * @param string $urn
     * @param array $params
     * @return Response
     */
    public function call($method, $urn, array $params = array());
}

interface Request {

    public function isValid();

    public function make(Client $client);
}

例如实现类似这样的东西。 App 的 ApiClient 负责进行 API 调用,知道要定位哪个 URL。我会让 ApiClient 知道 API 的 URL,并且请求将包含 URN 部分(资源名称)。它们将一起构成完整的 URI。 (这是可选的,只是预期 API 版本)。

namespace App;

class ApiClient implements \Api\Client {

    private static $url = 'https://api.yourapp.tld';

    /**
     * Just an example implementation using json (not completed)
     */
    public function call($method, $uri, array $params = array()) {
        $cUrlHandle = \curl_init(self::$url . '/' . $uri);
        \curl_setopt($cUrlHandle, CURLOPT_CUSTOMREQUEST, $method);
        \curl_setopt($cUrlHandle, CURLOPT_RETURNTRANSFER, true);

        if (!empty($params)) {
            \curl_setopt($cUrlHandle, CURLOPT_POSTFIELDS, \json_encode($params));
        }

        $response = curl_exec($cUrlHandle);

        //...
    }

}

现在我们可以创建不同类型的请求。每个人都负责为 API 客户端验证和打包参数。

我注意到所有更新请求都需要 ID。我会用它来处理一个对象的创建和更新请求(如果那不可能,你可以拆分它们)。

class ImageLayerRequest implements \Api\Request {

    private $id;
    private $url;
    private $apiUrn;
    private $params = array();

    /**
     * If $id provided it's an update request otherwise it'll be create
     * 
     * @param string $id
     * @param string $imageUrl
     */
    public function __construct($id, $imageUrl) {
        $this->id = $id;
        $this->url = $imageUrl;
    }

    public function isValid() {
        if ($this->id === null) {
            //validate create request
            //...

            $this->apiUrn = 'image-layer/create';
        } else {
            //validate update request
            //...
            $this->params['id'] = $this->id;
            $this->apiUrn = 'image-layer/update';
        }

        $this->params['url'] = $this->url;
    }

    public function make(\Api\Client $client) {
        return $client->call('post', $this->apiUrn, $this->params);
    }

}

对于 AudioLayer:

class AudioLayerRequest implements \Api\Request {

    private $id;
    private $bitrate;
    private $url;

    //the rest is similar, just diferent parameters
}

关于php - 在 OO 上下文中建模更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24799622/

相关文章:

c++ - 一个内部表示不暴露给用户并且可以透明改变的类,这个例子是什么?

java - "Encapsulation helps make sure clients have no dependence on the choice of representation"

其他语言中的 Python 描述符协议(protocol)模拟?

php - 添加将在 PHP 中以 JSON 编码的 mysql 查询结果的值?

php http header

php - 查询中的问题(PHP MYSQL UTF-8)

Swift:如何子类化一个对象以便我可以添加一个额外的变量?

php - 配置 Apache、PHP5 FPM 和 MySQL 进行长查询

Java继承: How to declare a static variable in Super and instantiate it in subclass

python - 如何动态地为 Python 中的类属性赋值?