这可能是一个通用问题,我读过的99%的解决方案都是使用isset
来保持安全和冗长。但是,我仍然认为它不是那么优雅,而且有点重复。
为了更好地理解我的问题,以下是我的代码的一般模式:
public static function buildObject(array $userProperties) {
// Here, I'm building a user from an associative array.
// If the developer passes in an array with missing name or address,
// this will generate an undefined index notice!
$user = new User();
$user->setName($userProperties['name']);
$user->setAddress($userProperties['address']);
return $user;
}
到目前为止我尝试过:
添加对所有设置调用的
isset()
检查。对于此示例来说,这可能不是一个坏主意,但是我有一些具有许多属性的大型类,并且看起来和感觉都非常重复。通过循环访问属性并将其设置为 null(如果数组中不存在(来确保数组安全。这在代码中似乎并不那么糟糕,但感觉像是一种解决方法。
使用空合并运算符
。?? null
。这也感觉重复且不可读
我更喜欢一个简单的解决方案,它不会添加太多逻辑,但看起来很优雅。我知道最好在这里进行验证,但是在处理可选字段时会遇到同样的问题。
优雅没有客观的衡量标准。一个开发人员可能会觉得优雅的东西,另一个开发人员会发现冗长和重复。对于这样的事情,无论什么被认为是最好的,总是完全主观的。
话虽如此,如果你发现自己一遍又一遍地做一些不必要的重复和冗长的事情,这通常是一种可以改进的气味。
您的用例的可能解决方案会有很大差异,具体取决于您想要的方向。您可以创建一个实现ArrayAccess
的参数对象,以便使用者仍然可以将参数视为数组,但您可以在方法中更简洁地使用它。
或者,您可以创建一个具有流畅界面的构建器对象,以便创建用户根本不需要数组:
$user = UserBuilder::start()
->setName('Foo')
->setLastName('Bar')
->build();
但这完全取决于你的实现细节,取决于你愿意为这种相对方便而经历的额外工作,以及你想要解决的具体问题。
当将关联数组作为参数传递并且您希望访问一些有保证的键时,一种非常常见且非常简单的模式是使用array_merge()
。
例如:
function foo(array $arguments) {
$arguments = array_merge( [
'name' => null,
'lastName' => null,
'role' => ROLE_USER
],
$arguments);
}
再多的魔法也不会给一个没有收到名字的User
对象命名,但这样至少设置了数组键,你可以在使用它们之前使用它来设置其他默认值。
最后,你必须决定什么对你有用,你认为什么是优雅的,以及什么样的实现在你的项目中真正值得。