boleh :
tidak boleh :
interface DownloadableReport { public function getName(): string; public function getHeaders(): array; public function getData(): array; }
class BlogReport { public function getName(): string { return 'Blog report'; } }
membina rentetan yang dikembalikan. Walau bagaimanapun, katakan kita memanggil kaedah ini daripada kod dalam kelas lain. Kelas lain tidak peduli bagaimana rentetan itu dibina, ia hanya peduli sama ada ia dikembalikan. Sebagai contoh, mari lihat cara memanggil kaedah ini dalam kelas lain: getName()
class ReportDownloadService { public function downloadPDF(BlogReport $report) { $name = $report->getName(); // 在这里下载文件... } }
Tambah kaedah ke muat turun laporan pengguna. Sudah tentu, kami tidak boleh menggunakan kaedah yang sudah ada dalam UserReport
kerana kami telah dipaksa untuk lulus dalam satu kelas ReportDownloadService
sahaja. Oleh itu, kita perlu menamakan semula kaedah sedia ada dan menambah yang baru. Seperti ini: BlogReport
class ReportDownloadService { public function downloadBlogReportPDF(BlogReport $report) { $name = $report->getName(); // 在这下载文件... } public function downloadUsersReportPDF(UsersReport $report) { $name = $report->getName(); // 在这下载文件... } }
baharu; kami kini perlu menambah kaedah AnalyticsReport
baharu pada kelas ini. Pada ketika ini, anda mungkin melihat bahawa fail berkembang pesat. Ini adalah masa yang sesuai untuk menggunakan antara muka. downloadAnalyticsReportPDF()
dan mentakrifkannya seperti ini: DownloadableReport
interface DownloadableReport { public function getName(): string; public function getHeaders(): array; public function getData(): array; }
dan BlogReport
untuk melaksanakan antara muka UsersReport
seperti yang ditunjukkan di bawah. Saya sengaja menulis kod DownloadableReport
yang salah untuk menunjukkan sesuatu. UsersReport
class BlogReport implements DownloadableReport { public function getName(): string { return '博客报告'; } public function getHeaders(): array { return ['头在这']; } public function getData(): array { return ['报告的数据在这里']; } }
class UsersReport implements DownloadableReport { public function getName() { return ['用户报告']; } public function getData(): string { return '报告的数据在这里'; } }
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
Atas ialah kandungan terperinci Artikel yang menerangkan maksud antara muka dan cara menggunakan antara muka untuk menulis kod PHP berkualiti tinggi. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!