到目前为止已经明确过的需求已经全部实现了,趁现在还没有新的需求出来,我们可以回过头来看看我们的代码还有什么可以优化的。
一段代码在整个项目里的不同地方用到,叫做代码的复用。从我们开始项目之后,我们在不断重构代码,其中很大一部分成果是提高了代码的复用,比如说我们用来检查用户是否登录的 security/http/login-check.php 被好多个控制器文件引入,那么它的代码就被复用了好多次,类似的还有 pdo.php。为了不重复写代码,代码的复用率应该是要尽可能高的,而反例就是大量『复制粘贴』的重复代码,比如我们刚开始的代码。
先总结一下:把常用的代码,单独成一个文件,在需要它们的时候再引入,是一种常见的复用代码的方法。类似的还有把常用的代码封装在类的方法里,或者函数里,也是复用。这个思想经常用 DRY 来表示,也就是不要重复你自己(的工作,Don’t Repeat Yourself)。我们现在的代码已经比刚开始的时候进步了很多,但我们还有可能继续提高复用性吗?
先看看我们的代码还有哪里没有被复用呢?比如我们之前说,我们所有的控制器文件几乎都要先引入 security/http/login-check.php 文件来检查登录状态,这个动作本身是不是也是一种重复的代码,是不是也可以被复用呢?
另外,现在我们所有的请求都要求使用会话,所以会话功能看起来已经成了一种网站配置,而不是单独的跟某些控制器有关系了,我们能把调用 session_start 这个行为复用吗?
上面两个问题,实际上都可以通过一个叫『前端控制器』的『设计模式』来解决,它的主要做法就是,将所有的请求都先发送给一段代码来处理,这段代码就好像之前我们说过的『公因式』一样,把每个页面都需要执行的代码在这里执行,而每个页面所不同的部分,它就转发给每个控制器来自己完成。不多说,我们看看应该怎么做。
上面所说的『公因式』代码,我们一般叫『前端入口文件』,我们得先创建这个文件。但创建这个文件之前,我们先把目前的 web 目录,改名为 controller 也就是控制器目录,然后我们重新创建一个 web 目录,并且创建我们的前端入口文件 web/index.php
// $_GET 类似 $_POST 也是超级变量,但它是通过 URL 传的
// 比如下面要处理的 c 参数,就可以通过 http://localhost:8000/?c=xxx 的方式传
$controller = $_GET['c'] ?? 'index';
$filePath = sprintf('%s/../controller/%s.php', __DIR__, $controller);
// 通过控制器名生成控制器文件,但如果控制器文件并不存在,我们通过下面这种方式返回 404
if (!file_exists($filePath)) {
header('HTTP/1.0 404 Not Found');
die('Not Found');
}
session_start();
// 也不是所有的页面都需要检查是否登录,比如处理登录本身的地址
// 有了前端控制器,我们就可以在这里统一配置哪些地址需要检查登录,哪些不需要
if ('login' !== $controller) {
require '../security/http/login-check.php';
}
require $filePath;
就这样,我们的前端控制器就做好了。这么处理完之后,我们的控制器文件就都又可以少一行引入 security/http/login-check.php 的代码了。
同样的,security/http/login-check.php 以及 controller/login.php 里的 session_start 调用也可以去掉了。
这个改动毫无疑问是会影响 MVC 里的 C 的,所以控制器代码肯定要做调整,我们也确实已经改过了。但要注意的是,因为我们修改了访问页面的方式,所以也会影响到 V,因为所有链接地址现在是在 V 里面写死的,比如说提交任务内容的表单的提交地址,就不再是 add.php 而是 /?c=add 了,这里大家需要将模板里所有提到链接地方都改掉。
如果一切顺利,我们的小网站依然能正常使用,大家修改过后可以实际测试一下。
除了网页可以用前端控制器,我们的命令也是可以的,只不过现在用意义也不是很大,就先略过了。
不过请读者注意的是,代码复用率虽然可以作为一个评判代码好坏的指标,但我的经验是不能只求代码复用率。代码复用的本质依然是拆分和抽象,被复用的代码应该是本身就可以独立出来负责一个任务的。如果是为了拆分而拆分,很有可能到最后自己都不知道想关注的代码写在什么地方,反而会增加维护难度。
本章节的完整代码请见这里。
写作累,服务器还越来越贵
求分担,祝愿好人一生平安
天使打赏人