当您查看参数名称时,您不能不注意到它们可以分为三个不同的组:
User Data: $user_name, $user_pass
Address Data: $address, $city, $postalcode, $country
Contact Data: $user_email, $phone, $mobilephone
因此,您可以申请Introduce Parameter Object:
您经常会看到一组特定的参数倾向于一起传递。几种方法可以在一个类或多个类中使用该组。这样的一组类是一个数据块,可以用一个包含所有这些数据的对象来替换。将这些参数转换为对象只是为了将数据组合在一起是值得的。这种重构很有用,因为它减少了参数列表的大小,而且长的参数列表很难理解。新对象上定义的访问器也使代码更加一致,这再次使其更易于理解和修改。
如果您不想进行 OOP,您也可以将参数分组到数组中,但是您将失去所有类型的好处。我只是假设你不介意使用对象。所以,在应用重构之后,你最终会得到
function user_registration(User $user, Address $address, Contact $contact)
查看该参数列表应该会让您注意到 Address 和 Contact 可能首先属于 User,因此您可以考虑将函数签名更改为 just
function user_registration(User $user)
然后这样称呼它:
$user = new User('johndoe', 'secretsauce');
$user->setAddress(new Address('Doe Street', 'Doe Town', 12345, 'Neverland'));
$user->setContact('jdoe@example.com', '+123 12345', '+123 54321');
user_registration($user);
我们也可以将用户名和密码设置为 Credentials 对象,然后就这样做
user_registration(new User($credentials, $address, $contact));
通过要求 ctor 中的数据,我们确保新注册的用户确实拥有所有这些信息。我们可以争论我们是否需要地址和联系人来注册用户,所以Setter injection 在这里可能就足够了:
$user = new User(new Credentials('johndoe', 'secretsauce'));
$user->setAddress(new Address('Doe Street', 'Doe Town', 12345, 'Neverland'));
$user->setContact(new Contact('jdoe@example.com', '+123 12345', '+123 54321'));
user_registration($user);
但是,user_registration 作为全局范围内的单独函数是放错了位置。通过GRASP's Information Expert principle,方法应该在具有最多信息以履行职责的对象上。这改善了Cohesion 并减少了Coupling。换句话说:
$user = new User($credentials);
$user->setAddress($address);
$user->setContact($contact);
$user->register();
现在用户类的一个问题是它包含密码。仅在针对身份验证服务对用户进行身份验证时才需要密码。我们可以争论用户名,但密码绝对不应该是用户对象的一部分。所以你应该做类似的事情
$user = new User;
$user->setAddress($address);
$user->setContact($contact);
$user->register($credentials);
当register() 被调用时,它只会使用凭证来委托将新用户插入到用户存储中。但它不会将它们保留在实际的 User 实例中。
最后,您可能想添加一个Simple Factory or Builder pattern 来封装User 的创建,以简化各种实例的aggregation。或者您可能想在此处介绍Repository pattern 和move the register() method。不过,这超出了这个问题的范围。