前言转眼成为PHPer已经快两年了。在这期间,我也对如何写出可读性强、易于扩展的代码有了一些想法。使用参考场景一:遍历数组获取新的数据结构也许你会这样写://声明一个新的数组,组装成你想要的数据$tmp=[];foreach($arras$k=>$v){//取出你要的数据$tmp[$k]['youwant']=$v['youwant'];...//一系列的判断得到你想要的数据if(...){$tmp[$k]['youwantbyjudge']='TIGERB';}...}//最后,你要得到你想要的数组$tmp----------------------------------------------------//可能你觉得上面写的不太好,那下面我们换一种写法foreach($arras$k=>$v){//一系列的判断得到你想要的数据if(...){//覆盖你想要的值$arr[$k]['youwantbyjudge']='TIGERB'}...//去掉你不想要的结构unset($arr[$k]['youwantdel']);}//最后我们得到了我们的目标数组$arr接下来我们使用引用值:foreach($arras&$v){//一系列的判断得到你想要的数据if(...){//覆盖你想要的值$v['youwantbyjudge']='TIGERB'}...//去掉你不想要的结构unset($v['youwantdel']);}unset($v);//最后我们得到了我们的目标数组$arrusing引用是不是让我们的代码更简洁了?另外,与第一种写法相比,我们节省了内存空间,尤其是在操作一个大数组的时候,效果极其明显。场景二:传值给函数获取新值,基本符合数组遍历。我们只需要将这个函数的参数声明为引用即可,如下:functiondecorate(&$arr=[]){#code...}$arr=[....];//调用函数decorate($arr);//获取新值$arr同上,好处是节省内存空间使用try...catch...if有以下逻辑:classUserModel{publicfunctionlogin($username='',$password=''){code...if(...){//用户不存在return-1;}code...if(...){//密码错误return-2;}code...}}classUserController{publicfunctionlogin($username='',$password=''){$model=newUserModel();$res=$model->login($username,$password);if($res===-1){return['code'=>'404','message'=>'用户不存在'];}if($res===-2){return['code'=>'400','message'=>'密码错误'];}code...}}我们使用try...catch...重写后:classUserModel{publicfunctionlogin($username='',$password=''){code...if(...){//用户不存在thrownewException('Userdoesnotexist','404');}code...if(...){//密码错误thrownewException('passworderror','400');}code...}}classUserController{publicfunctionlogin($username='',$password=''){try{$model=newUserModel();$res=$model->login($username,$password);//如果需要,我们可以在这里统一提交数据库事务//$db->commit();}catch(Exception$e){//如果需要,我们可以在这里统一回滚数据库事务//$db->rollback();return['code'=>$e->getCode(),'message'=>$e->getMessage()]}}}通过使用try...catch...让我们的代码逻辑更加清晰,在try...我们只需要关注正常的业务,在这种情况下,异常处理统一在catch中。所以,我们写上游代码的时候,直接抛出异常即可。使用匿名函数在函数或方法中构建代码块。如果我们有一段逻辑,我们需要在函数或方法中格式化数据,但是格式化数据的代码片段出现了很多次。如果我们直接写,我们可能会想到下面这样:functiondoSomething(...){...//格式化代码片段......//格式化代码片段[重复代码]...}I相信大部分人应该不喜欢上面可能这样写:functiondoSomething(...){...format(...);...格式(...);...}//声明另一个格式化代码函数或方法functionformat(){//格式化代码段...}上面的写法没有问题,把我们的代码段最小化了,但是如果这样格式化怎么办函数或方法仅由doSomething使用?我一般都是这样写的,为什么呢?因为我认为它是format和doSomething在thiscontext上下文中的一个子集。functiondoSomething(){...$package=function(...)use(...){ //use后的参数也可以通过引用传递//格式化代码段...};...包裹(...);...包裹(...);...}实现类的“懒加载”和设计模式的“最少知道原则”如果有如下代码:classOne{private$instance;//第一类内部依赖于第二类//不符合设计模式最少知识原则publicfunction__construct(){$this->intance=newTwo();}publicfunctiondoSomething(){if(...){//如果某种情况调用类二的实例方法$this->instance->do(...);}...}}...$instance=newOne();$instance->doSomething();...上面有什么问题?不符合设计模式最少知识的原则。类一直接依赖于类二的实例。并不是类二的所有上下文都会被使用,所以资源被浪费了。有人说创建单例,但解决不了实例化又不用的尴尬。所以我们使用匿名函数来解决上面的问题,我们改写成这样:classOne{private$closure;公共函数__construct(Closure$closure){$this->closure=$closure;}publicfunctiondoSomething(){if(...){//使用时实例化//实现延迟加载$instance=$this->闭包();$instance->do(...)}...}}...$instance=newOne(function(){//classOne外部依赖于classTworeturnnewTwo();});$instance->doSomething();...减少if...else...的使用如果遇到这种代码,那一定是黑洞functiondoSomething(){if(...){if(...){...}esle{...}}else{if(...){...}esle{...}}}提前返回异常 如果你细心的话,你可能会发现上面的情况。也许其他大部分代码都在处理异常。更有可能的是异常代码非常简单。通常我会这样做:// 如果是在一个函数中,我会先处理异常情况,然后提前返回代码,最后执行正常逻辑函数doSomething(){if(...){//异常情况返回...;}if(...){//异常情况返回...;}// 正常逻辑...}// 同理,如果是在一个类中,我会先处理异常情况,然后先抛出异常classOne{publicfunctiondoSomething(){if(...){//抛出异常newException(...);}if(...){//异常抛出newException(...);}// 正常逻辑...}}关联数组做map 如果我们在client端做决策,我们通常会判断不同的上下文来选择不同的策略,通常会像下面的if或者switch判断那样使用:classOne{publicfunctiondoSomething(){if(...){$instance=newA();} elseif(...){$instance=newA();}else{$instance=newC();}$instance->doSomething(...);...}}上面的写法通常有很多if语句或者switch语句,一般我用一个map来映射不同的策略,像这样:classOne{private$map=['a'=>'namespace\A',//带上命名空间,因为变量是动态的'b'=>'namespace\B','c'=>'namespace\C'];publicfunctiondoSomething(){...$instance=new$this->map[$strategy];//$strategy是'a'或'b'或'c'$instance->doSomething(...);...}}使用接口为什么要使用接口?非常方便后期的扩展和代码的可读性。比如设计一个折扣系统,不同的产品只是在不同的优惠政策下有不同的优惠行为。我们定义一个优惠行为的接口,最后对这个接口进行编程。没错,伪代码如下..){...}}控制器拒绝直接的数据库操作。最后,我想说的是,你总是拒绝在你的Controller中直接操作DB。为什么?我们程序的大部分操作基本上都是增删改查。可能是查询的where条件和字段不一样,所以有时候这时候我们可以将数据库的增删改查方法抽象写到模型中,通过参数暴露我们的where和fields条件。通常,这可以大大提高效率和代码重用。例如,像这样:classDemoModelimplementModel{publicfunctiongetMultiDate($where=[],$fields=['id'],$orderby='idasc'){$this->where($where)->字段($fields)->orderby($orderby)->get();}}最后,如有不妥之处还请指正,THX~EasyPHP:一个极速轻量级的PHP全栈框架扫描下方关注我的技术公众号在二维码,推送我的原创技术分享及时为大家
