在OO上下文中建模变更

时间:2014-07-17 09:23:54

标签: php oop encapsulation software-design

  面向对象编程语言的

Formal semantics包括封装状态。在状态更改之前是否有用于封装潜在变更的用例?虽然以下示例是PHP格式的,但您也应该在答案中认为与语言无关。

背景

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

'图层'对象

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

  • ImageLayer
  • BackgroundLayer
  • TextLayer
  • AudioLayer
  • (N)层

要更改ImageLayer,API需要:

  • ID
  • 新图片的网址

要更改TextLayer,API需要:

  • ID
  • 你正在改变什么(字体,大小,文字内容?)
  • 新值

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

  • ID
  • 比特率
  • 新音频文件的网址
  • 其他一些值

我的注意事项

我最初考虑添加Client::updateImageLayer()Client::updateTextLayer(),但后来意识到Client对象在将来{@ 1}}时可能会呈指数级变大。

然后我考虑添加(N)Layer,但我认为这不够好。

最后,这是我一直在考虑的另一个选项: Client::updateLayer(Layer $layer, array $values) 对象。

更改对象

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

Client

使用上面的内容,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,通过调用{{1}确保它们全部有效}},并调用ChangeSet将特定数组直接发送到API,因为它已经过全部验证。

问题

  • 我之前从未听说过建模更改。您对上述选项有何看法?我是不是太复杂了?我喜欢能够随意添加/删除isValid()更改的想法,以及所有内容仍能正常工作,因为它们符合getChanges()界面。

  • 也许我不是最好的方式吗?我的解决方案有什么不好吗?

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

1 个答案:

答案 0 :(得分:1)

根据您的描述,在我看来,您需要一个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
}