Golang如何才能写出值得阅读的代码

2019-08-21 17:56:19 浏览数 (1)

不忘初心,砥砺前行

作者 | 陌无崖

转载请联系授权

导语

什么样的代码,才算优雅的代码,身为程序员,写代码就像写文章,写出好的文章不仅自己读着赏心悦目,同时也会让读者受到启发。然而事实上,大多数我们去回顾或者维护我们之前的代码,浮现在你眼前的是对自己代码的厌恶,会严重怀疑当时写这些代码时,大概是脑子进了水。那么该如何写好的优雅的代码?

Hello World

我们仍然从最简单的开始,编写一个hello函数

代码语言:javascript复制
func Hello(name string) string {
    return "Hello,"   name
}

现在我们进行需求变更,我们希望用不同的语言发送hello….

代码语言:javascript复制
func Hello(name string, language string) string {
    if language == "Franch" {
        return "用Franch进行问好:Hello,"   name
    } else if language == "English" {
        return "用English进行问好:Hello,"   name
    } else if language == "Spanish" {
        return "用Spanish进行问好:Hello,"   name
    }
    return "hello,"   name
}

我们暂时只用这个例子,想一下,如果我们的语言非常多,我们将会出现很多if else的语句,而且我们也发现在返回数据时,有一些前缀,我们是否可以将这些统一进行变量进行更好的管理呢?因此我们需要用到switch语句。 首先需要定义前缀

代码语言:javascript复制
const (
    frenchHelloPrefix  = "用Franch进行问好:"
    englishHelloPrefix = "用English进行问好:"
    SpanishHelloPrefix = "用Spanish进行问好:"
    common             = "Hello,"
    English            = "English"
    Franch             = "Franch"
    Spanish            = "Spanish"
)

重新修改我们的函数

代码语言:javascript复制
func Hello(name string, language string) string {
    prefix := ""
    switch language {
    case Franch:
        prefix = frenchHelloPrefix
    case English:
        prefix = englishHelloPrefix
    case Spanish:
        prefix = SpanishHelloPrefix
    default:
        prefix = ""
    }
    return prefix   common   name
}

这样我们很轻易的对我们的整体的思路进行了很好的组织,在case中只是专注了前缀,只用一个return返回结果便能达到我们的要求,并且我们的代码阅读时也会更加显得简洁。

type类型的巧妙使用

反面案例

首先需要明确我们的需求,我们将会定义一个钱包,实现存钱和取钱 首先定义一个结构体

代码语言:javascript复制
type Wallet struct {
    balance int
}

存钱

代码语言:javascript复制
func (w *Wallet) Deposit(amount int) {
    w.balance  = amount
}

取钱

代码语言:javascript复制
func (w *Wallet) Withdraw(amount int) error {
    if amount > w.balance {
        return errors.New("cannot withdraw, insufficient funds")
    }
    w.balance -= amount
    return nil
}

上面是一个非常简单的程序,但是却有一个缺点,在现实生活中,我们的钱包里面是什么样的呢?除了现金,我们也存了各种银行卡,如何才能准确的定义这些属性呢?可能有的同学会按照下面的格式进行定义。

代码语言:javascript复制
type Wallet struct {
    balance       int //零钱
    creditbalance int //信用卡
    Bankbalance   int //银行卡

}

然后我们或许会猜到,他将会定义三个存储的函数进行存钱,是这样的

代码语言:javascript复制
func (w *Wallet) Deposit(amount int) {
    w.balance  = amount
}
func (w *Wallet) Depositcreditbalance (amount int) {
    w.creditbalance  = amount
}
func (w *Wallet) DepositBankbalance   (amount int) {
    w.Bankbalance   = amount
}

这些代码看起来“简洁”大概是因为少吧,如果说在真正存钱时会出现验证,转账,等这些大概就不能称之为简洁了,代码中将会出现大量重复的代码那应该怎么改呢?

正面案例

我们需要为我们的各种类型重新定义变量,像这样

代码语言:javascript复制
type coin int
type creditCard int
type BankCard int

有了这些类型,我们需要存钱是指定我们的类型所有我们同样也需要定义一些常量

代码语言:javascript复制
const (
    CREDITCARD = "creditCard"
    BANKCARD   = "BankCard"
)

编写我们的函数

代码语言:javascript复制
func (w *Wallet) Deposit(amount int, card string) {
    switch card {
    case CREDITCARD:
        w.creditbalance  = creditCard(amount)
    case BANKCARD:
        w.Bankbalance  = BankCard(amount)
    default:
        w.balance  = coin(amount)
    }
}

这样我们只用了一个函数就完成了需求,同时逻辑因为有了变量也变得更加的清晰,代码也会给人一种阅读文章的感觉。 现在我们写一个取钱的函数,我们需要注意取钱时需要注意总金额不能超出金额的取钱。

代码语言:javascript复制
func (w *Wallet) Withdraw(amount int, card string) error {
    switch card {
    case CREDITCARD:
        if creditCard(amount) > w.creditbalance {
            return InsufficientFundsError
        }
        w.creditbalance -= creditCard(amount)
    case BANKCARD:
        if BankCard(amount) > w.Bankbalance {
            return InsufficientFundsError
        }
        w.Bankbalance -= BankCard(amount)
    default:
        if coin(amount) > w.balance {
            return InsufficientFundsError
        }
        w.balance  = coin(amount)
    }
    return nil
}

以上需要注意的是,在返回错误时,我同样定义了一个变量,这样做的好处是,将来如果错误的类型较多,我们可以提前预制好一些错误信息,进行返回,然后统一放在一个文件中,使用的时候,直接调用,修改的时候也会方便查找。在这里我的定义如下:

代码语言:javascript复制
var InsufficientFundsError = errors.New("超出了总金额")

总结

当然除了以上的代码习惯,或许也有更好的方式,都在与平时写代码时多思考,多参考,多积累。我们的代码才会写的越来越好,以上的总结是我今天在学习测试驱动开发时的突然的代码启发,整理成的笔记。因为测试驱动前面的基础比较简单,等到我学到后面的,再总结分享。

END

今日推荐阅读

RabbitMQ系列笔记广播模式和路由模式 RabbitMQ系列笔记入门篇

RabbitMQ系列笔记work模式

RabbitMQ系列笔记work模式

protoc语法详解及结合grpc定义服务

Golang中Model的使用

基于Nginx和Consul构建高可用及自动发现的Docker服务架构

▼关注我,一起成长

主要分享 学习心得、笔记、随笔▼

0 人点赞