让代码变成程序员的艺术(小白必须看)

刚刚接触IT行业的程序员都会有一个痛点,自己写的代码觉得很好,但是别人看你的代码的时候,总觉得太乱了,没有规范。后面维护的程序员就痛苦了。为什么我会提出这个问题,我就是从这一样走过来。今天推荐看的一本书《代码整洁之道》。

思维导图

在这里插入图片描述

总结:

代码是团队沟通方式。
  1. 设计思想流程图编写。
  2. 代码结构清晰,注释合理,方法参数备注。
  3. 遵循团队制定的代码规范,就必须遵循下去,新手进来也要遵循。
有意义的命名
  1. 使用有意义且可拼写的变量名
#反面
$ymdstr = $moment->format('y-m-d');

#正面
$currentDate = $moment->format('y-m-d');
  1. 同种类型的变量使用相同词汇
#反面
getUserInfo();
getClientData();
getCustomerRecord();

#正面
getUser();
  1. 使用易检索的名称
#反面 
//What the heck is 86400 for?(86400到底是干什么的)
addExpireAt(86400);

#正面
// Declare them as capitalized `const` globals.(用常量大写来声明变量)
interface DateGlobal {
  const SECONDS_IN_A_DAY = 86400;
}
addExpireAt(DateGlobal::SECONDS_IN_A_DAY);
  1. 使用解释型变量
#反面
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/';
preg_match($cityZipCodeRegex, $address, $matches);
saveCityZipCode($matches[1], $matches[2]); //*对比

#正面
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/';
preg_match($cityZipCodeRegex, $address, $matches);
list(,$city, $zipCode) = $matchers; //list()把数组中的值赋给一组变量
saveCityZipCode($city, $zipCode); //这里只用了 $city 和 $zipCode 变量。
  1. 避免心理映射

    不要让读者在脑中将你的名称译为他们熟知的名称。

  2. 命名
    类名:不应当时动词,避免使用Manager、Processor、Data、Info这样的类名。
    方法名:动词或者动词短语,言到意到,意到言到 。eg:getName()
    使用领域名称:eg :AccountVisitor =>Visitor模式
    别用双关语:避免将同一单词用于不同的目的。
    不添加没用的语境: eg:GSDAccountAddress =>Address

函数
  1. 方法有多短小才合适没有定论,但是长达500行的一个方法,绝对让阅读者起杀人之心。
#反面
//  获取个人信息
Private UserDTO getUserDTO(Integer userId)
{
    //获取基本信息 
    … 此处写了10//获取最近的一次订单信息
    …  此处写了30// 获取钱包余额、可用优惠券张数等
    ...   此处写了30return userDTO;
}

#正面
//  获取个人信息
Private UserDTO getUserDTO(Integer userId)
{
    //获取基本信息 
    UserDTO userDTO= getUserBasicInfo(userId);

    //获取最近的一次订单信息
    userDTO.setUserLastOrder(getUserLastOrder(userId));

    // 获取钱包、可用优惠券张数等
    userDTO.setUserAccount(getUserAccount(userId));  
    return userDTO;
}

Private  UserDTO getUserBasicInfo(userId);
Private  UserLastOrder getUserLastOrder(userId);
Private  UserAccount getUserAccount(userId);
  1. 单一责任(一个函数只负责一件事情)

  2. 每一个函数一个抽象层级(阅读代码自顶向下代码:向下规则)
    总分的写法(个人理解):总是写步骤,分是写步骤需要的方法。

  3. 使用描述性的名称(动词+关键词)

eg:writeField
  1. 减少if/else嵌套,多使用多态,一个函数做一件事,遵从单一责任
#反面
// 修改用户密码,这个例子只有3层嵌套,很温柔了
public boolean modifyPassword(Integer userId, String oldPassword, String newPassword) {
      if (userId != null && StringUtils.isNotBlank(newPassword) && SpringUtils.isNotBlank(oldPassword)) {
    User user = getUserById(userId);
    if(user != null) {
         if(user.getPassword().equals(oldPassword) {
              return updatePassword(userId, newPassword)
         }
    }
      }
}

#正面
// 修改用户密码 
Public Boolean modifyPassword(Integer userId, String oldPassword, String newPassword) {
     if (userId == null || StringUtils.isBlank(newPassword) || StringUtils.isBlank(oldPassword)) {
            return false;
     }
     User user = getUserById(userId);
     if(user == null) {
           return false;
      }
     if(!user.getPassword().equals(oldPassword) {
           return false;    
     }
     return updatePassword(userId, newPassword);
}
  1. 函数方法参数过多,如何优化。
#反面
Class Book
{
    public function create($name, $cateId, $author, $year, $price, $publish, $country, $language)
    {
        $params = [
            'name' => $name,
            'cateId' => $cateId,
            'author' => $author,
            'year' => $year,
            'price' => $price,
            'publish' => $publish,
            'country' => $country,
            'language' => $language,
        ];
    }
}
#真面
class BookModel
{
    protected $name;
    protected $cateId;
    protected $author;
    protected $year;
    protected $price;
    protected $publish;
    protected $country;
    protected $language;

    public function getName()
    {
        return $this->name;
    }

    public function setName($name)
    {
        $this->name = $name;
    }

    public function getCateId()
    {
        return $this->cateId;
    }

    public function setCateId($cateId)
    {
        $this->cateId = $cateId;
    }

    public function getAuthor()
    {
        return $this->author;
    }

    public function setAuthor($author)
    {
        $this->author = $author;
    }

    public function getYear()
    {
        return $this->year;
    }

    public function setYear($year)
    {
        $this->year = $year;
    }

    public function getPrice()
    {
        return $this->price;
    }

    public function setPrice($price)
    {
        $this->price = $price;
    }

    public function getPublish()
    {
        return $this->publish;
    }

    public function setPublish($publish)
    {
        $this->publish = $publish;
    }

    public function getCountry()
    {
        return $this->country;
    }

    public function getLanguage()
    {
        return $this->language;
    }

    public function setLanguage($language)
    {
        $this->language = $language;
    }
}
#控制器调用
Class Book
{
    public function create(BookModel $bookModel)
    {
        $params = [
            'name' => $bookModel->getName(),
            'cateId' => $bookModel->getCateId(),
            'author' => $bookModel->getAuthor(),
            'year' => $bookModel->getYear(),
            'price' => $bookModel->getPrice(),
            'publish' => $bookModel->getPublish(),
            'country' => $bookModel->getCountry(),
            'language' => $bookModel->getLanguage(),
        ];
    }
}
  1. 抽离try/catch
    1、 使错误处理代码从主路径代码中分离出来得到简化。
    2、关键try应该是这个函数的第一个单词且catch/finally后无其他内容。
    3、消灭错误码
#反面
//  获取个人信息
Private UserDTO getUserDTO(Integer userId)
{
   try { 
       //获取基本信息 
       ... 此处写了10//获取最近的一次订单信息.
       ...此处写了20// 获取钱包、可用优惠券张数等
       ...此处写了20}catch (Exception e) {
        logger.error(e);
        return null;
    }
}
   return userDTO;
}
#正面
//  获取个人信息
Private UserDTO getUserDTO(Integer userId)
{
    //获取基本信息 
    UserDTO userDTO= getUserBasicInfo(userId);
    
    //获取最近的一次订单信息    
    userDTO.setUserLastOrder(getUserLastOrder(userId));

    // 获取钱包、可用优惠券张数等
    userDTO.setUserAccount(getUserAccount(userId));  
    return userDTO;
}
Private  UserDTO getUserBasicInfo(userId);
Private  UserLastOrder getUserLastOrder(userId);
Private  UserAccount getUserAccount(userId)try{ // TODO } catch( Exception e) { //TODO}
  1. 别重复自己

比如:算法重复了多次,还与其他代码混在一起,修改是需要修改4个地方,整体的可读性下降。

  1. 注释
    1、法律信息(版权及著作权声明)
    2、提供信息的注释,函数名
    3、对意图的解释——某个决定后面的意图
    4、阐释
    5、警示
    6、TODO注释(工作中将需要完成的功能列出来,完成就注释)

  2. 格式
    1、垂直格式:用大多数为200行、最长500行的单个文件构造出色的系统是可能的,尽管这并非不可违背的原则,也应该乐于接受。短文通常比长文件易于理解。
    1.1、源文件名称简单,程序的结构先总再分,阅读从上往下读。
    1.2、每组代码行展示一条完整的思路。这些思路用空白行区隔开。每个空白行都是一条线索,标识出新的独立概念。
    1.3、如果说空白行隔开了概念,靠近的代码则暗示了它们之间的紧密关系。所以,关系紧密的代码应该互相靠近。
    1.4、方法相关的方法应给放在附近,避免跳来跳去。
    2、横向格式:保持代码短小。
    2.1、水平方向的间隔和靠近:使用空格字符分隔,不在函数名和左圆括号加空格。
    2.2、水平对齐:不是把强调的声明变量拉开距离。
    2.3、缩进:突显层次感
    2.4、空范围:有时while或for的语句体为空。空范围缩进,用括号包围起来。
    2.5、开发前先定好团队规则,缩进,命名类、变量和方法。

  3. 对象与数据结构
    1、在不改动既有数据结构前提下添加新函数,面向对象在不改动既有的函数前提下添加新类
    2、得墨忒耳定律是什么?

得墨忒耳定律-对象 O 的 M 方法,可以访问/调用如下的:

1.这个对象自己拥有的方法;
2.传入该方法的参数的方法;
3.该方法创建的对象的方法;
4.该对象直接拥有的对象的方法;

换言之:每个单元(对象或方法)应当对其他单元只拥有有限的了解。
打个比方:假设我在便利店购物。付款时,我是应该将钱包交给收银员,让她打开并取出钱?还是我直接将钱递给她?(不要和陌生人说话。)

class A{ 
        private B b = new B();
        private void methodE(){} 
        public void methodA(C c){
                D d = new D(); 
                methodE(); //1这个对象自己拥有的方法,可调用 
               c.print(); //2传入该方法的参数的方法,可调用 
               d.invert(); //3该方法创建的对象的方法,可调用 
               b.kill(); //4该对象直接拥有的对象的方法,可调用 
               F f = b.getF(); 
               f.rock(); //5 该对象依赖对象的实现的模块,不可调用。 
         }
}

3、最精炼的数据结构,是一个 只有公共变量、没有函数的类,叫数据传送对象(DTO).

  1. 错误处理
    1、使用异常而非返回码。只要新增Exception的异常了而无需修改错误码枚举类
    2、先写try-catch-finally语句。先构造try代码块的事物范围,维护好该范围的事务特征。
    3、依调用者需要定义异常了将第三方API打包是好的实践手段。
    4、定义常规流程。特例模式:创建一个雷或配置一个对象用来处理特例,客户就不用应付异常行为了
    5、别返回NULL值
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值