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/