杨 发布的文章

策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,将每个算法封装起来,并且使它们之间可以互相替换。该模式使得算法可以独立于使用它的客户端而变化。

下面是一个使用 Golang 实现策略模式的示例代码:

package main

import "fmt"

type PaymentStrategy interface {
    Pay(amount float64) error
}

type CreditCardStrategy struct{}

func (cc *CreditCardStrategy) Pay(amount float64) error {
    fmt.Printf("Paid %0.2f using Credit Card.\n", amount)
    return nil
}

type PayPalStrategy struct{}

func (pp *PayPalStrategy) Pay(amount float64) error {
    fmt.Printf("Paid %0.2f using PayPal.\n", amount)
    return nil
}

type PaymentContext struct {
    strategy PaymentStrategy
}

func (pc *PaymentContext) SetPaymentStrategy(strategy PaymentStrategy) {
    pc.strategy = strategy
}

func (pc *PaymentContext) MakePayment(amount float64) error {
    return pc.strategy.Pay(amount)
}

func main() {
    creditCardStrategy := &CreditCardStrategy{}
    payPalStrategy := &PayPalStrategy{}

    paymentContext := &PaymentContext{}

    // 使用信用卡支付
    paymentContext.SetPaymentStrategy(creditCardStrategy)
    paymentContext.MakePayment(100.00)

    // 使用 PayPal 支付
    paymentContext.SetPaymentStrategy(payPalStrategy)
    paymentContext.MakePayment(200.00)
}

在上面的代码中,我们定义了一个 PaymentStrategy 接口,该接口包含一个 Pay 方法,用于支付指定金额。接下来,我们定义了两个具体的支付策略类:CreditCardStrategy 和 PayPalStrategy,它们实现了 PaymentStrategy 接口的 Pay 方法。

我们还定义了一个 PaymentContext 结构体,该结构体包含一个 strategy 字段,用于存储当前的支付策略。PaymentContext 结构体还定义了两个方法:SetPaymentStrategy 用于设置支付策略,MakePayment 用于执行支付操作。

最后,在 main 函数中,我们创建了一个 CreditCardStrategy 和 PayPalStrategy 实例,并分别使用它们来进行支付。可以看到,在 MakePayment 方法中,我们只需要调用 strategy.Pay(amount) 来执行支付操作,而无需关心具体的支付策略是什么。这正是策略模式的优势所在,它将算法的实现细节与客户端代码隔离开来,使得客户端代码更加简洁和易于维护。

在 Go 语言中,协程是轻量级的用户态线程,也称为 goroutine,运行在操作系统的线程之上,是 Go 语言的核心特性之一。协程的调度是由 Go 语言运行时(runtime)实现的,下面是协程调度的基本流程:

当一个 goroutine 被创建时,它的执行是由主 goroutine 控制的。

如果该 goroutine 遇到了一个 I/O 操作、系统调用、时间延迟等需要阻塞的事件,则该 goroutine 会将自己从内部的队列中移除,直到该事件完成。

运行时系统会从运行队列中调度一个可运行的 goroutine 继续执行。

当一个 goroutine 执行完毕时,它也会自动从队列中退出。

如果主 goroutine 卡住了,运行时系统会尝试捕获 panic 以便程序能够正常退出。

当所有 goroutine 都结束并且 main 函数退出后,程序退出。

总的来说,Go 语言中的协程调度采用了 M:N 模型,即运行时系统会将多个 goroutine 映射到少量的操作系统线程上进行执行。这种方式有效地利用了多处理器系统的资源,并且可以轻松地处理大量的并发请求,提高了程序的执行效率和性能。

在普通的编程中,编写并发程序时需要特别注意并发安全性,以避免数据竞争和其他并发问题。在Go语言中,下面是一些可能会出现并发不安全问题的情况:

共享变量:如果多个 goroutine 对一个共享变量进行读写,而至少有一个 goroutine 执行写操作时,就有可能会出现并发问题。为了避免这种情况,可以使用 sync 包或通道来同步访问共享变量。

非原子操作:如果一个变量的更新操作不是原子的,就可能会导致并发问题。例如对于 32 位的整数类型,在一个 goroutine 更新该变量的高 16 位,而在另一个 goroutine 更新低 16 位时,就有可能出现错误结果。为了避免这种情况,可以使用原子操作进行变量更新。

无序写入:如果多个 goroutine 向同一个切片(slice)同时写入数据,而没有采取任何措施来同步访问该切片,就有可能出现并发问题。为了避免这种情况,可以使用 sync 包或通道来同步访问共享数据。

共享 map:如果多个 goroutine 对同一个 map 进行读写操作,而至少有一个 goroutine 执行写操作时,就可能会出现并发问题。为了避免这种情况,可以使用 sync 包或者采用只读方式访问 map。

总的来说,Go语言具有强大的并发性能,而并发不安全问题主要是由于多个 goroutine 访问共享数据而引起的。开发人员在编写并发代码的同时,应该采取一些正确地同步机制,保证每个共享变量的访问被合理的同步以避免并发不安全问题。

func (db *DB) Unscoped() (tx *DB) {
    tx = db.getInstance()
    tx.Statement.Unscoped = true
    return
}

Unscoped办法设置tx.Statement.Unscoped为true

gorm的Select、Delete方法都是软删除,Select会自动筛选delete_at为空的记录,Delete会更新delete_at。

如果需要获取已删除的记录,可以tx.Unscoped().Select()。
如果需要物理删除,可以tx.Unscoped().Delete().

去年技术分享,分享了我对redis有序集合的理解,目录包括有序集合的用法、适用场景、底层原理。分享结束后同事们也进行了提问,收获颇多。

zset介绍

● 有序集合(sorted set)是排序的集合(set)。
● 集合是 string 类型元素的集合,且元素不允许重复。
● 有序集合每个元素会关联一个 double 类型的分值 score,通过 score 将集合成员从小到大排序。



- 阅读剩余部分 -

微服务(Microservices)是一种软件架构风格。它以职责单一、细粒度的小型功能模块为基础,并将这些小型功能模块组合成一个复杂的大型系统。

- 阅读剩余部分 -