Php 如何区分;“未设置”;及;“无转让”;对于空值?

Php 如何区分;“未设置”;及;“无转让”;对于空值?,php,oop,design-patterns,php-7.1,Php,Oop,Design Patterns,Php 7.1,在本例中的php中–但实际上在一般编程中,在合并相同类型的两个不可变数据对象时,是否有办法区分null值的“no assign”和“unset value”命令 考虑这个php类,它是一个不可变的数据对象。它在其构造函数中接受一个字符串和一个整数,并且只为以下值提供访问器: class Data { protected $someNumber; protected $someString; public function __construct(?int $someNu

在本例中的php中–但实际上在一般编程中,在合并相同类型的两个不可变数据对象时,是否有办法区分
null
值的“no assign”和“unset value”命令

考虑这个php类,它是一个不可变的数据对象。它在其构造函数中接受一个字符串和一个整数,并且只为以下值提供访问器:

class Data
{
    protected $someNumber;
    protected $someString;

    public function __construct(?int $someNumber, ?string $someString)
    {
        $this->someNumber = $someNumber;
        $this->someString = $someString;
    }

    public function getSomeNumber(): ?int
    {
        return $this->someNumber;
    }

    public function getSomeString(): ?string
    {
        return $this->someString;
    }
}
值可以是
null
或其各自的数据类型
string
integer
。构造函数也接受
null
值,而不是
string
和/或
int
:UNSET操作

现在我希望能够合并
数据的两个实例,类似于这个简化的工厂方法,它接受
$first
$second
,其中
$second
中的数据覆盖
$first
中的数据(如果存在)

class DataFactory
{
    public function merge(Data $first, Data $second): Data
    {
        // Uses data from $first if corresponding data from
        // $second is (strictly) null
        return new Data(
            $second->getSomeNumber() ?? $first->getSomeNumber(),
            $second->getSomeString() ?? $first->getSomeString(),
    }
}
在上面的示例中,
$second
的访问者返回的
null
值被解释为没有更新操作:当遇到
null
时,
$first
的对应值被保留。问题是,我希望能够在
merge
中区分对无更新操作或未设置操作的请求

数据
类中的严格键入不允许使用某种字符串常量,如
“Data\u UNSET\u FIELD”
作为要标记的值,因此直接在数据本身上实现这一点似乎是不可能的。更重要的是,为任何值传递构造函数null肯定意味着SET null

我在考虑某种类型的更新镜头,它明确指定了合并时应该取消设置的属性,这样
$second
中的
null
值就意味着不进行更新(远离
$first

什么是紧凑的面向对象模式来解决这个问题?我已经可以想象随着数据的增长,会出现诸如分解普通数组模式或策略类的类爆炸之类的问题。此外,我还略微关注
数据
对象的“移动性”,因为新对象必须在某个点与它们关联

提前谢谢


编辑

我希望在合并两个
数据
实例时,能够区分不覆盖当前值和取消设置值,即分配
null
,其中
$first
是基础,
$second
覆盖
$first
的数据。作为一个细节,合并会产生第三个新对象,即合并结果


查看
$second
中的
DataFactory
片段
null
值当前被解释为“保留
$first
的对应值”。但是,我如何为每个字段携带另一个标志,指示在结果对象中哪些字段应设置为
null
,以干净的方式,并且不会过多地干扰数据类?

PHP无法区分未分配变量和null变量。 这使得跟踪哪些属性应该被覆盖是不可避免的

我知道你有两个顾虑:

  • 保持
    数据
    不变
  • 保持
    数据
    的界面清洁(例如强制执行严格类型)
能够跟踪“已定义”和“未定义”属性的最简单数据结构之一是
\stdClass
对象(但数组也很好)。 通过将
merge()
方法移动到
Data
类中,您将能够隐藏任何实现细节-保持接口干净

实现可能如下所示:

最终类数据{
/**@var\stdClass*/
受保护的美元道具;
//避免直接实例化,请改用::create()
私有函数_u构造()
{
$this->props=new\stdClass();
}
//流畅的界面
公共静态函数create():数据
{
返回新的自我();
}
//强制不变性
公共函数
{
$this->props=克隆$this->props;
}
带有序号(?int$someNumber)的公共函数:数据
{
$d=克隆$this;
$d->props->someNumber=$someNumber;
返回$d;
}
带有someString(?string$someString)的公共函数:数据
{
$d=克隆$this;
$d->props->someString=$someString;
返回$d;
}
公共函数getSomeNumber():?int
{
返回$this->props->someNumber??null;
}
公共函数getSomeString():?字符串
{
返回$this->props->someString??null;
}
公共静态函数合并(…$dataObjects):数据
{
$final=新自我();
foreach($dataObjects作为$data){
$final->props=(对象)数组\合并((数组)$final->props,(数组)$data->props);
}
返回$final;
}
}
$first=数据::创建()
->带序号(42)
->使用字符串('foo');
//通过指定null覆盖someNumber和someString
$second=Data::create()
->带序号(空)
->带字符串(空);
//仅覆盖“someString”
$third=Data::create()
->使用字符串('bar');
$merged=Data::merge($first,$second,$third);//只有“someString”属性设置为“bar”
var_dump($merged->getSomeString());//“酒吧”

在考虑html表单时,“不更新”标志是否与“原始”的用法类似?换句话说(只是为了确保我能理解),你想区分一处未动过的财产和一处被“摧毁”的财产吗?@FélixGagnon Grenier这并不是我想要做的。我会用更多的信息更新我的问题。谢谢@JeffreyWesterkamp,为什么你需要“能够区分请求