Heim > Artikel > Backend-Entwicklung > Ein Artikel, der die Bedeutung von Schnittstellen erklärt und wie man Schnittstellen zum Schreiben von hochwertigem PHP-Code verwendet
Dieser Artikel richtet sich an Entwickler, die über ein grundlegendes Verständnis der OOP-Konzepte (objektorientierte Programmierung) und der Vererbung in PHP verfügen. Wenn Sie wissen, wie man Vererbung in PHP verwendet, ist dieser Artikel einfacher zu verstehen.
Grundsätzlich beschreibt eine Schnittstelle, was eine Klasse tun soll. Mithilfe von Schnittstellen wird sichergestellt, dass jede Klasse, die die Schnittstelle implementiert, die von der Schnittstelle angegebenen öffentlichen Methoden enthält.
:
verwendet werden, um öffentliche Methoden in einer Klasse zu definieren.
Wird zum Definieren von Konstanten in Klassen verwendet.nicht einzeln instanziiert werden.
Wird verwendet, um private oder geschützte Methoden in einer Klasse zu definieren.interface DownloadableReport { public function getName(): string; public function getHeaders(): array; public function getData(): array; }
class BlogReport { public function getName(): string { return 'Blog report'; } }
getName()
den zurückgegebenen String erstellt. Nehmen wir jedoch an, wir rufen diese Methode aus dem Code einer anderen Klasse auf. Der anderen Klasse ist es egal, wie die Zeichenfolge aufgebaut ist, sie kümmert sich nur darum, ob sie zurückgegeben wird. Sehen wir uns zum Beispiel an, wie diese Methode in einer anderen Klasse aufgerufen wird: class ReportDownloadService { public function downloadPDF(BlogReport $report) { $name = $report->getName(); // 在这里下载文件... } }
Obwohl der obige Code bereits funktioniert, stellen wir uns vor, was wäre, wenn wir jetzt eine Methode zum Herunterladen von Benutzerberichten hinzufügen möchten. Natürlich können wir die vorhandenen Methoden in ReportDownloadService
nicht verwenden, da wir gezwungen sind, nur eine BlogReport
-Klasse zu übergeben. Daher müssen wir die bestehende Methode umbenennen und eine neue hinzufügen. Etwa so:
class ReportDownloadService { public function downloadBlogReportPDF(BlogReport $report) { $name = $report->getName(); // 在这下载文件... } public function downloadUsersReportPDF(UsersReport $report) { $name = $report->getName(); // 在这下载文件... } }
Obwohl Sie es nicht wirklich sehen können, gehen wir davon aus, dass die übrigen Methoden in der obigen Klasse denselben Code zum Erstellen des Downloads verwenden. Wir können öffentlichen Code zu Methoden hochstufen, aber wir werden immer noch öffentlichen Code haben. Darüber hinaus haben wir in dieser Klasse mehrere Einträge mit nahezu identischem Code. Dies kann zu zusätzlichem Aufwand führen, wenn Sie in Zukunft versuchen, den Code zu erweitern oder Testfunktionen hinzuzufügen. getName()
是如何构建返回的字符串的。但是,假设我们在另一个类中的代码中调用此方法。另一个类并不会关心字符串是如何构建的,它只关心它是否被返回。例如,让我们看看如何在另一个类中调用此方法:
interface DownloadableReport { public function getName(): string; public function getHeaders(): array; public function getData(): array; }
尽管上面的代码已经可以用了,让我们设想一下,若是我们现在想要在 UserReport
类中增加一个下载用户报告的方法。当然,我们不能使用 ReportDownloadService
中已经存在的方法,因为我们已经强制只能传入一个 BlogReport
类。因此,我们必须重命名现有方法,再添加一个新的方法。像这样:
class BlogReport implements DownloadableReport { public function getName(): string { return '博客报告'; } public function getHeaders(): array { return ['头在这']; } public function getData(): array { return ['报告的数据在这里']; } }
虽然你实际看不到,但我们假设上述的类中,其余的方法都使用相同的代码来构建下载。我们可以将公共的代码提升为方法,但我们仍然会有一些公共的代码。除此之外,我们还有多个进入这个类的几乎是相同代码的入口。这可能会在未来尝试扩展代码或添加测试功能时导致额外的工作量。
举个例子,我们创建一个新的 AnalyticsReport
;我们现在需要为这个类增加一个新的 downloadAnalyticsReportPDF()
方法。这时你可能会观察到这个文件正在快速增长。这时就是使用接口的绝佳时机。
我们从创建一个接口开始。我们要创建一个叫做 DownloadableReport
的接口,并这样定义:
class UsersReport implements DownloadableReport { public function getName() { return ['用户报告']; } public function getData(): string { return '报告的数据在这里'; } }
我们现在要更改 BlogReport
和 UsersReport
来实现 DownloadableReport
接口,如下所示。我故意写错了 UsersReport
AnalyticsReport
; jetzt müssen wir dieser Klasse eine neue downloadAnalyticsReportPDF()
-Methode hinzufügen. An diesem Punkt stellen Sie möglicherweise fest, dass die Datei schnell wächst. Dies ist ein guter Zeitpunkt, um Schnittstellen zu nutzen. Wir beginnen mit der Erstellung einer Schnittstelle. Wir werden eine Schnittstelle namens DownloadableReport
erstellen und sie wie folgt definieren: 🎜class UsersReport implements DownloadableReport { public function getName(): string { return '用户报告'; } public function getHeaders(): array { return []; } public function getData(): array { return ['报告的数据在这里']; } }🎜Wir werden jetzt
BlogReport
und UsersReport
ändern, um sie zu implementieren DownloadableReport
-Schnittstelle, wie unten gezeigt. Ich habe absichtlich den falschen Code für UsersReport
geschrieben, um etwas zu demonstrieren. 🎜class ReportDownloadService { public function downloadReportPDF(DownloadableReport $report) { $name = $report->getName(); // 在这下载文件 } }rrreee🎜Wenn wir versuchen, diesen Code auszuführen, erhalten wir eine Fehlermeldung, weil: 🎜
getHeaders()
方法。getName()
方法返回类型在接口中定义的方法返回值类型中。getData()
方法定义了返回类型,但和接口中定义的返回类型不同。因此,要让 UsersReport
正确地实现 DownloadableReport
接口,我们需要作如下变动:
class UsersReport implements DownloadableReport { public function getName(): string { return '用户报告'; } public function getHeaders(): array { return []; } public function getData(): array { return ['报告的数据在这里']; } }
现在我们两个报告类都实现了相同的接口,我们可以像这样更新我们的 ReportDownloadService
:
class ReportDownloadService { public function downloadReportPDF(DownloadableReport $report) { $name = $report->getName(); // 在这下载文件 } }
现在我们向 downloadReportPDF()
方法传入了 UsersReport
或 BlogReport
对象,没有任何错误出现。这是因为我们现在知道了报告类所需要的必要方法,并且报告类会按照我们预期的类型返回数据。
向方法传入接口,而不是向类传入接口,这样的结果使得 ReportDownloadService
和报告类产生松散耦合,这根据的是方法做什么,而不是如何做。
如果我们想要创建一个新的 AnalyticsReport
,我们需要让它实现相同的接口,然后它就会允许我们将报告类实例传入相同的 downloadReportPDF()
方法中,而不用添加其他新的方法。如果你正在构建自己的应用或框架,想要让其他开发人员有创建给他们自己的类的功能,这将非常有用。举个例子,在 Laravel 中,你可以通过实现 Illuminate\Contracts\Cache\Store
接口来创建自定义的缓存类。
除了使用接口来改进代码外,我更倾向于喜欢接口的「代码即文档」特性。举个例子,如果我想要知道一个类能做什么和不能做什么,我更喜欢在查看类之前先查看接口。它会告诉我所有可调用的方法,而不需要关心这些方法具体是怎么运行的。
对于像我这样的 Laravel 开发者来说,值得注意的一件事是,你会经常看到 接口 interface
和 契约 contract
交替使用。根据 Laravel 文档,「Laravel 的契约是一组定义了框架提供的核心服务的接口」。因此,契约是一个接口,但接口不一定是契约。通常来说,契约只是框架提供的一个接口。有关契约的更多内容,我建议阅读一下 文档,因为我认为它在契约的理解和使用途径上有很好的见解。
阅读本文后,我们希望你能简要地了解到了什么是接口,如何在 PHP 中使用接口,以及使用接口的好处。
对于我的 Laravel 开发者读者们,我下周会更新一篇新的博文,讲解如何在 Laravel 中使用接口实现桥接模式。如果你感兴趣,可以订阅我的网站更新通知,这样当我更新时,你就会收到通知。
如果这篇文章有助于你理解 PHP 接口,我期待在评论区听到你的声音。继续去创造令人惊叹的作品吧!
非常感谢 Aditya Kadam 、 Jae Toole 和 Hannah Tinkler 帮助我校对和改进这篇文章。
原文地址:https://dev.to/ashallendesign/using-interfaces-to-write-better-php-code-391f
译文地址:https://learnku.com/php/t/62228
Das obige ist der detaillierte Inhalt vonEin Artikel, der die Bedeutung von Schnittstellen erklärt und wie man Schnittstellen zum Schreiben von hochwertigem PHP-Code verwendet. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!