何时使用特征或服务?
When to use a Trait or a Service?
对于这个例子,我需要通过一些 AuthService
发送电子邮件,而不是其他人(FacebookAuthService
、GoogleAuthService
和 LoginFormAuthService
应该发送电子邮件,但是ApiAuthService
不应该,所以我不能使用 AbstractAuth
parent class 这样做)。
类 在这里是相关的,因为它们都是关于 Auth 的,但这只是一个例子。不相关的 class 怎么办(一个用于 Auth,一个用于上传等)?
鉴于这些 classes:
class MailerService() { /* do stuff to send emails */}
class FacebookAuthService {
public function connect() {}
}
class GoogleAuthService {
public function connect() {}
}
class LoginFormAuthService {
public function connect() {}
}
class ApiAuthService {
public function connect() {}
}
这样做是否更有效率(使用 LoginMailService):
class LoginMailService() {
public function send(User $user, MailerService $mailer) {
$mailer->sendTo($user->email());
$mailer->message('Login email message');
}
}
class FacebookAuthService {
public function connect(User $user, LoginMailService $loginMail) {
$loginMail->send($user->email());
}
}
class GoogleAuthService {
public function connect(User $user, LoginMailService $loginMail) {
$loginMail->send($user->email());
}
}
class LoginFormAuthService {
public function connect(User $user, LoginMailService $loginMail) {
$loginMail->send($user->email());
}
}
class ApiAuthService {
public function connect(User $user) {}
}
或这样做(具有特征):
trait SendLoginMailTrait {
private function sendLoginMail(User $user, MailerService $mailer) {
$mailer->sendTo($user->email());
$mailer->message('Login email message');
}
}
class FacebookAuthService {
use SendLoginMailTrait;
public function connect(User $user) {
$this->sendLoginMail($user->email());
}
}
class GoogleAuthService {
use SendLoginMailTrait;
public function connect(User $user) {
$this->sendLoginMail($user->email());
}
}
class LoginFormAuthService {
use SendLoginMailTrait;
public function connect(User $user) {
$this->sendLoginMail($user->email());
}
}
class ApiAuthService {
public function connect(User $user) {}
}
概念是正交的,特征和服务依赖性不可互换。
您建议在问题中使用特征的方式是错误的,破坏了封装并使维护更加困难。
这是依赖注入的明显案例。
为您的MailerService
创建一个界面:
interface MailerServiceInterface {
public function message($to, $body);
}
class MailerService implements MailerServiceInterface {
public function message($to, body) {
/* message implementation */
}
}
并且您的不同服务应依赖于该接口:
class FacebookAuthService
{
private MailerServiceInterface $mailer;
public __construct(MailerServiceInterface $mailer) {
$this->mailer = $mailer;
}
public function connect(User $user) {
$this->mailer->message($user->getEmail(), 'Login Message');
}
}
现在代码已解耦,您的 "auth service" 只依赖于一个可以有多个实现的通用接口,如果您创建一个新的实现,您可以简单地 "inject" 新的实现而不必触摸多个 "auth services" 类.
中的任何内容
组合这些功能不仅在概念上是错误的(发送邮件并不是您真正的身份验证服务职责,这些 类 应该不知道这些通知的实际发送方式),而且在实践中也存在问题:
在现实生活中,您的邮件程序服务也将拥有自己的依赖项(例如,与外部通信的 HTTP 客户端 API、访问某些配置以了解发送消息时使用的凭据, ETC)。有了 traits,你将无法声明这些依赖关系,并且如果不在每次函数调用时注入它们,就无法满足它们。
关于“何时使用特征以及何时使用服务?”的一般性问题没有多大意义,因为正如我在开头所说的那样,这些概念是正交的。最好表述为“何时使用特征?”。答案是:很少.
但是,如果您想要一个更通用的答案以及特征的有效用例,我会向您指出 this good post 和一些很好的例子。
对于这个例子,我需要通过一些 AuthService
发送电子邮件,而不是其他人(FacebookAuthService
、GoogleAuthService
和 LoginFormAuthService
应该发送电子邮件,但是ApiAuthService
不应该,所以我不能使用 AbstractAuth
parent class 这样做)。
类 在这里是相关的,因为它们都是关于 Auth 的,但这只是一个例子。不相关的 class 怎么办(一个用于 Auth,一个用于上传等)?
鉴于这些 classes:
class MailerService() { /* do stuff to send emails */}
class FacebookAuthService {
public function connect() {}
}
class GoogleAuthService {
public function connect() {}
}
class LoginFormAuthService {
public function connect() {}
}
class ApiAuthService {
public function connect() {}
}
这样做是否更有效率(使用 LoginMailService):
class LoginMailService() {
public function send(User $user, MailerService $mailer) {
$mailer->sendTo($user->email());
$mailer->message('Login email message');
}
}
class FacebookAuthService {
public function connect(User $user, LoginMailService $loginMail) {
$loginMail->send($user->email());
}
}
class GoogleAuthService {
public function connect(User $user, LoginMailService $loginMail) {
$loginMail->send($user->email());
}
}
class LoginFormAuthService {
public function connect(User $user, LoginMailService $loginMail) {
$loginMail->send($user->email());
}
}
class ApiAuthService {
public function connect(User $user) {}
}
或这样做(具有特征):
trait SendLoginMailTrait {
private function sendLoginMail(User $user, MailerService $mailer) {
$mailer->sendTo($user->email());
$mailer->message('Login email message');
}
}
class FacebookAuthService {
use SendLoginMailTrait;
public function connect(User $user) {
$this->sendLoginMail($user->email());
}
}
class GoogleAuthService {
use SendLoginMailTrait;
public function connect(User $user) {
$this->sendLoginMail($user->email());
}
}
class LoginFormAuthService {
use SendLoginMailTrait;
public function connect(User $user) {
$this->sendLoginMail($user->email());
}
}
class ApiAuthService {
public function connect(User $user) {}
}
概念是正交的,特征和服务依赖性不可互换。
您建议在问题中使用特征的方式是错误的,破坏了封装并使维护更加困难。
这是依赖注入的明显案例。
为您的MailerService
创建一个界面:
interface MailerServiceInterface {
public function message($to, $body);
}
class MailerService implements MailerServiceInterface {
public function message($to, body) {
/* message implementation */
}
}
并且您的不同服务应依赖于该接口:
class FacebookAuthService
{
private MailerServiceInterface $mailer;
public __construct(MailerServiceInterface $mailer) {
$this->mailer = $mailer;
}
public function connect(User $user) {
$this->mailer->message($user->getEmail(), 'Login Message');
}
}
现在代码已解耦,您的 "auth service" 只依赖于一个可以有多个实现的通用接口,如果您创建一个新的实现,您可以简单地 "inject" 新的实现而不必触摸多个 "auth services" 类.
中的任何内容组合这些功能不仅在概念上是错误的(发送邮件并不是您真正的身份验证服务职责,这些 类 应该不知道这些通知的实际发送方式),而且在实践中也存在问题:
在现实生活中,您的邮件程序服务也将拥有自己的依赖项(例如,与外部通信的 HTTP 客户端 API、访问某些配置以了解发送消息时使用的凭据, ETC)。有了 traits,你将无法声明这些依赖关系,并且如果不在每次函数调用时注入它们,就无法满足它们。
关于“何时使用特征以及何时使用服务?”的一般性问题没有多大意义,因为正如我在开头所说的那样,这些概念是正交的。最好表述为“何时使用特征?”。答案是:很少.
但是,如果您想要一个更通用的答案以及特征的有效用例,我会向您指出 this good post 和一些很好的例子。