对于此示例,我需要通过某些AuthService
发送电子邮件,但不需要通过其他电子邮件发送(FacebookAuthService
,GoogleAuthService
和LoginFormAuthService
应该发送电子邮件,但ApiAuthService
不应发送电子邮件,因此我不能使用AbstractAuth
父类这样做)。
这里的类是相关的,因为它们都是关于Auth的,但这只是一个例子。不相关的类呢(一个用于Auth,一个用于Upload等)?
鉴于这些课程:
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');
}
}
现在代码已解耦,您的“身份验证服务”仅取决于可以具有多个实现的通用接口,如果您创建新的实现,则可以简单地“注入”新的实现,而无需触及多个“身份验证服务”类。
组合这些功能不仅在概念上是错误的(发送邮件实际上不是您的身份验证服务责任,而且这些类对这些通知的实际发送方式一无所知),但实际上也存在问题:在现实生活中,您的邮件服务也将拥有自己的依赖关系(例如,与外部API进行通信的HTTP客户端,访问某些配置以了解在发送消息时使用哪些凭据等)。使用特征,您将无法声明这些依赖关系,也无法在不将其注入每个函数调用的情况下满足它们。
关于“
何时使用特征以及何时使用服务?的一般问题”没有多大意义,因为正如我一开始所说的那样,概念是正交的。最好将其表述为“
何时使用特征?”。答案是:很少。
但是,如果您想使用有关特征的有效用例的更一般的答案,我会通过一些好的示例将您指向this good post。
“ HAS A”。]]例如,FacebookAuthService
是A AuthService
和AuthService
具有名为MailerService
的服务或您需要设计的任何其他结构。
traits
用PHP解决了这个问题,在Java中,我们有interface的默认方法
。但最好还是将特征用于相同类型的相同行为。否则,您可能会认为它是实用程序,并定义了一个[[HAS A关系。特质可以具有可在多个类中使用的方法和抽象方法。在设计中可能需要编写抽象方法和类。这取决于项目的规模和软件体系结构的最佳实践。我强烈建议您学习《 [[四种设计模式的使用]]》,您还可以发现this link确实有用。那么当您面对封装和多态性概念时,您会感到很高兴编写空方法。编写清晰的代码是在长期支持下的大型项目中的一个问题。这个概念超出了单元的最佳效率,您可能会为开发付出更多的代价,而在维护和扩展上的花费会更少。