在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 ...
随机推荐
- Openstack单元测试工具简单说明
一.Openstack 的单元测试工具介绍 1.unittest unittest: 是 Python 的标准库,提供了最基本的单元测试功能,包括 单元测试运行器(简称runner) 和 单元测试框架 ...
- ICANN认证注册商小全 英、德、法
转载 在ICANN认证注册商小全(一)和ICANN认证注册商小全(二)中,QQPCC介绍了美洲的各ICANN认证注册商.今天我们移师欧洲,介绍欧洲的ICANN认证注册商,欧洲的注册商也很多,不可能在一 ...
- iconfont 批量把图标加入购物车的方法
在浏览器中按 f12 打开[开发人员工具],找到[console(控制台)],输入以下代码,再按回车,稍等片刻即可把全部图标加入购物车 var ll = document.getElementsByC ...
- JSPatch动态更新APP
JSPatch,只需在项目中引入极小的引擎,就可以使用JavaScript调用任何Objective-C的原生接口,获得脚本语言的能力:动态更新APP,替换项目原生代码修复bug. 用途 是否有过这样 ...
- Element ui tree树形控件获取父节点id
Element-ui官网给的方法 getCheckedKeys() { console.log(this.$refs.tree.getCheckedKeys()); }, 这种只有在所有子级都被选中的 ...
- 世界上最短的bash脚本
世界上最短的bash脚本长这样: #!/bin/bash 为啥呢?见下图: 推荐一篇文章,讲解为啥shell脚本开头总是"#!/bin/bash".文风太清奇,不好翻译,看原文吧: ...
- WebMagic实现分布式抓取以及断点抓取
访问我的博客 前言 从去年到今年,笔者主要负责的是与合作方的内容对接,新增的合作商不是很多的情况下,在我自从去年引入了 WebMagic 这个爬虫框架之后,基本很少需要去关注维护爬虫,做的最多的是新接 ...
- C语言中求字符串的长度
在C语言中求字符串的长度,可以使用sizeof()函数和strlen()函数,后者需要引入string.h (#include <string.h>) 因为C语言字符串是以 \0 结尾表示 ...
- Solidity之mapping类型
映射是一种引用类型,存储键值对.它的定义是:mapping(key => value),概念上与java中的map,python中的字典类型类似,但在使用上有比较多的限制. 一.mapping定 ...
- Spring总结 2.装配bean
本随笔内容要点如下: bean的作用域 占位符 一.bean的作用域 在默认情况下,Spring管理的bean的单例的.也就是说,无论注入多少次,都是同一个bean对象.一般情况下,单例模式是足以应付 ...