在PHP中避免一些代码中的坏味道
做PHP开发已经有快一年的时间了,在这一年的时间中,学习了很多生产环境中的技巧,学习了很多东西,期间也阅读了一些优秀的源码和关于代码的书,对写代码这一块有了一定的思考,也看过很多别人写的好的代码和坏的代码,这里说说自己的感悟和改进吧。
本篇博客直说自己的感悟,在写代码时,我给自己立下的规则,这样可以让代码清晰可读并少走一些坑。这些简单的规则虽然没有设计模式看起来那么激动人心,但是,平常注意可以让代码看起来很清爽。
1. 不要在对象外使用未声明的变量
这个问题其实表述起来可能不容易理解。这个问题是因为PHP语言本身的特点决定的。由于PHP是一个弱类型的动态脚本语言,所以很多情况下,给了这个语言本省很宽松的条件让开发者去编写代码。但是往往这些便利也会变为坑,所以在使用一些动态语言很方便的写法的时候,尤其要注意。
下面我们先声明一个类,暂且叫这个类为用户类,这个User类的背景设定为,框架自带,不允许修改,并且隐藏在框架深处,不容易发现,实际案例可以参考laravel框架的Request类,代码如下:
class User {
public $username;
public $password;
public $otherInfo = [];
public function readUserInfo() {
return [
'username' => $this->username,
'password' => $this->password,
];
}
public function addAtri($info) {
array_push($this->otherInfo, $info);
}
}
这样的代码看似中规中矩,但是接下来,我们需要对这个类进行操作:
$user = new User();
$user->userRealName = "hello world";
这样的代码在PHP中是完全可以运行的,并且不会报错,但是这样的代码会对之后的一些事情做为干扰。我们现在假定,上边的代码是在PHP web项目中是一个拦截器,或者叫做中间件也可以,然后我们在controller中会会使用到这个类的实例,并且使用到这个中间件中添加的这个变量,如下:
class WebOperate {
public function doOprate(User $user) {
$user->userRealName = "hello world";
next($user);
}
}
这里设定的场景是,WebOperate是一个中间件,所有的Controller都会走这个中间件后到达Controller,之后,在处理相应的Controller的功能,接下来,Controller会将中间件的实例注入进来,供控制器使用,而中间件开发人员不是很在意其的存在:
class IndexController {
public function index(User $user) {
return $user->userRealName;
}
}
而这样的代码是可以完美运行的,接下来,开发人员想要的实另一个User类,这个User类中添加一些其他功能,正如之前所说,这个类在框架深处并且很难找到,且不允许修改,因为其他功能使用了这个类,所以,我们只有继承并添加方法。根据开发经验,开发人员会认为User类中存在这个userRealName变量,所以就造成了这个写法:
首先是基于这个User衍生出来的Teacher类:
class Teacher extends User {
public function sayHello() {
return "hello world";
}
}
这样,我们的Teacher就可以sayhello了,但是,这个时候,在我们的Controller中还想知道老师的真实姓名,怎么办?根据经验,我们可以将注入的类换成Teacher并且返回真实姓名:
class IndexController {
public function index(Teacher $user) {
return $user->userRealName;
}
}
那么这下问题来了,其实User类中并没有这个类,所以这个变量根本没有数值,但是根据经验,是中间件已经赋值过一次了,所以我们应该可以直接使用,但是并没有这个数值,我们开始看源码发现,继承的User类中根本不存在这个变量,那么这个变量之前为什么可以使用呢,因为在中间件中,给User的实力付了值。
所以我们的不能这样直接使用未声明的变量,在一个类中。
我们应该这样写:
class WebOperate {
public function doOprate(User $user) {
$user->addAtri([
'userRealName' => 'hello world',
]);
next($user);
}
}
这样的中间件,在调用的时候继承类也可以使用同样的方法,很简单并且很不容易出现坏的味道。
2. 类or数组
其实这个问题同时也衍生出了另外的问题,就是函数返回值的问题。
首先,我明确表示,一个函数做多种类型的返回值是我个人感觉是不好的,在动态语言中虽然很常见,很多PHP的原生方法也有这样的,但是,在生产中使用这样的方式会造成函数返回的不确定性,我们需要作出很多判断来证明我们的结论,但是,如果返回值类型只有一种,我们就可以直接判断返回值就好了。
就像如下代码:
public function addNewUser() {
$res = $this->addData();
if ($res) {
return true;
} else {
return [
'error' => 1,
'errormsg' => "没有添加成功"
];
}
}
这样的代码在作为调用者往往会多一次判断,如下:
public function index() {
$res = $this->addNewUser();
if (is_array($res) && isset($res['error'])) {
return isset($res['errormsg']) ? $res['errormsg'] : "未知错误";
}
return "成功";
}
这样的代码几乎每一次调用完成这个函数都会有这一套出现,不仅代码不美观,而且很臃肿。
这样的代码需要改善,首先限制住函数的返回值。比如,我们只让这个函数返回bool类型的数:
public function addNewUser() {
$res = $this->addData();
if ($res) {
return true;
} else {
return false;
}
}
但是,显然,很多时候,我们要的不是简单的真价值,所以,我们会选择返回更多信息,这个时候,我们可以有三种处理方式。
1)返回int类型的数,然后通过这个int类型的数去判断处理结果,我们可以添加上映射关系:
class Operate{
public $operateRes = [
0 => '成功',
1 => '添加失败',
2 => '未知错误',
];
public function addNewUser() {
$res = $this->addData();
if ($res) {
return 0;
} else if ($res > 1) {
return 1;
}
return 2;
}
}
这样方法的调用者就可以很简单的使用方法并给出提示了:
$opera = new Operate();
$res = $opera->addNewUser();
return $opera->operateRes[$res];
给出统一的返回值类型的时候就完全不需要判断返回值类型而且可以设置一个规范返回提示。
2)我们也可以使用数组
3)数组给人不缺定性,因为很多时候,数组里可以认为的少写一些元素,如果少写了,程序直接报错,很不好。
所以第三种方式就是建议将固定格式的返回,写成一个类,做返回的时候,使用这个类:
class Operate{
public function addNewUser() {
$res = $this->addData();
$result = new Result();
if ($res) {
$result->errno = 0;
$result->errmsg = "成功";
} else if ($res > 1) {
$result->errno = 1;
$result->errmsg = "失败";
}
$result->errno = 2;
$result->errmsg = "未知错误";
return $result;
}
}
class Result {
public $errno;
public $errmsg;
}
这样的返回,保证了所有变量的存在,同样可以减少一次判断。
所以,综合以上,在我们返回结果的时候,尽量使用同种类型的变量,尽量减少使用数组返回。
先想这么多,后面有其他的想法了,继续总结。
以上风格仅仅代表本人坚持对代码的要求,不代表官方立场。
在PHP中避免一些代码中的坏味道的更多相关文章
- android中在java代码中设置Button按钮的背景颜色
android中在java代码中设置Button按钮的背景颜色 1.设置背景图片,图片来源于drawable: flightInfoPanel.setBackgroundDrawable(getRes ...
- Android中如何在代码中设置View的宽和高?
Android中如何在代码中设置View的宽和高?https://zhidao.baidu.com/question/536302117.htmlhttps://blog.csdn.net/u0141 ...
- Dreamweaver中清除php代码中多余空行的方法
使用DW自带的搜索功能,利用正则表达式 使用正则表达式搜索:\r\n\s*\r\n即可搜到代码中的空行,再用回车符\n替换即可消除代码中的多余空行
- 在Android源码中查找Java代码中native函数对应的C++实现
Android源码中很多关键代码都是C++实现的,java通过jni来调用,经常会看到java中这样的代码: static native Thread currentThread(); 如何根据方法名 ...
- 【我的Android进阶之旅】Android 源代码中的Java代码中//$NON-NLS-1$ 注释是什么意思?
1.背景 最近在负责公司基础业务和移动基础设施的开发工作,正在负责Lint代码静态检查工作.因此编写了自定义的Lint规则,在编写自定义的Lint规则前,当然是需要去把Google的关于Lint检测的 ...
- char和String 在jsp java代码中与jstl代码中的区别
在 jsp java代码中 '0' ,这种代表char 在jstl中 '0' 会被解释为 String 所以也可以用 .equals 方法
- 在带(继承)TextView的控件中,在代码中动态更改TextView的文字颜色
今天由于公司项目需求,须要实现一种类似tab的选项卡,当时直接想到的就是使用RadioGroup和RadioButton来实现. 这种方法全然没问题.可是在后来的开发过程中,却遇到了一些困扰非常久的小 ...
- 项目中通过单元测试代码中的spring事务是否起作用
今儿没事,想对代码中事务进行测试,于是乎就创建了一个单元测试进行测试,发现在方法中加上@Transactional注解后,发现在想数据库中插入数据时,代码执行成功,但数据库中却没有数据,于是各种检查, ...
- Java - 你的 Java 代码有这些坏味道吗?
列举一些 Java 开发中常见的"不良实践",来源于代码扫描(https://github.com/pmd/pmd),和诸君一起学习参考: 1 - 关闭资源 CloseResour ...
随机推荐
- unittest单元测试框架简单说明
unittest单元测试框架不仅可以适用于单元测试,还可以适用WEB自动化测试用例的开发与执行,该测试框架可组织执行测试用例,并且提供了丰富的断言方法,判断测试用例是否通过,最终生成测试结果.今天笔者 ...
- 简述serializable和transient关键字作用
transient的作用及使用方法,官方解释为: Variables may be marked transient to indicate that they are not part of the ...
- ABP框架服务层的接口与实现(增删改查)
public interface ITaskAppService : IApplicationService { IList<TaskDto> GetAllTasks();//所有 Get ...
- NAT介绍
NAT 即网络地址转换NAT作用:实现内网IP地址和公网IP地址之间的转换可以有效地缓解IP地址危机可以隐藏内网地址实现负载均衡实现内网和内网之间的通信 NAT分类:分类静态NAT:将内网IP地址一对 ...
- 分布式理论(六)—— Raft 算法
前言 我们之前讲述了 Paxos 一致性算法,虽然楼主尝试用最简单的算法来阐述,但仍然还是有点绕.楼主最初怀疑自己太笨,后来才直到,该算法的晦涩难懂不是只有我一个人这么认为,而是国际公认! 所以 Pa ...
- [转]magento2项目上线注意事项 切换到产品模式
本文转自:https://segmentfault.com/a/1190000007929783 切换到产品模式 php magento deploy:mode:set production 执行以上 ...
- [转]关于Linux安装mysql默认配置文件位置
本文转自:https://blog.csdn.net/smile___you/article/details/54409073 在linux下面安装mysql如果在/etc下面没有存在my.cnf配置 ...
- RowFilter遇上特殊字符*%'[]\
开发时,时不时需要分析DataView的数据.计算,过滤等 .但是处理的值有可能会遇上是带有特殊字符*.%.'.[.]和\ .需要手动在表达式中,处理这些特殊字符,需要再使用"[" ...
- WPF备忘录(3)如何从 Datagrid 中获得单元格的内容与 使用值转换器进行绑定数据的转换IValueConverter
一.如何从 Datagrid 中获得单元格的内容 DataGrid 属于一种 ItemsControl, 因此,它有 Items 属性并且用ItemContainer 封装它的 items. 但是,W ...
- 24.Linux-Nand Flash驱动(分析MTD层并制作NAND驱动)
1.本节使用的nand flash型号为K9F2G08U0M,它的命令如下: 1.1我们以上图的read id(读ID)为例,它的时序图如下: 首先需要使能CE片选 1)使能CLE 2)发送0X90命 ...