9

Golang 面向对象编程

 3 years ago
source link: https://www.cyningsun.com/03-12-2021/oop-in-go.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Golang 是面向对象的么?

是,也不是。尽管 Go 有类型和方法,并且允许面向对象风格的编程,但没有类型层次结构。Go 的『接口』概念提供了一种不同的实现方式,在某些方面更通用。同时,缺少类型层次结构使 Go 的『对象』感觉上比 C++ 或 Java 等语言中的『对象』轻很多。本文的目的就是通过示例来说明,如何使用 Golang 进行面向对象编程

以下是一个关于身份证ID的示例,用于从身份证中提取生日。通常的实现版本如下:

func Birthday(id string) string {
	return id[6:14]
}
const (
	id = "412717199109031697"
)
func Test_Birthday(t *testing.T) {
	found := Birthday(id)
	wanted := "19910903"
	if found != wanted {
		t.Errorf("unexpected birthday, wanted:%v, found:%v", wanted, found)
	}
}

简单的数据,如此实现倒也问题不大。但生产环境中往往遇到的都是复杂的多的数据和操作。此时就需要将数据和操作封装在一起

Golang 中可以通过 type 关键字创建新的类型,同时使用 NewXXX 的风格创建对象。

type ID string

func NewID(id string) (ID, error) {
	if len(id) != 18 {
		return "", errors.New(fmt.Sprintf("error id length:%v", len(id)))
	}
	return ID(id), nil
}

func (i ID) Birthday() string {
	return string(i[6:14])
}
func TestID_Birthday(t *testing.T) {
	found := ID(id).Birthday()
	wanted := "19910903"
	if found != wanted {
		t.Errorf("unexpected birthday, wanted:%v, found:%v", wanted, found)
	}
}

当业务变得更加复杂,同一种功能,存在多种实现方式,比如支付方式,微信支付、京东支付、支付宝、银联等不同渠道的大概流程大抵相似,但实现细节有所区别。此时就需要借助多态的动态绑定来进行业务抽象。

Golang 中动态绑定方法的唯一方式是通过接口(interface)来实现的。结构或其他具体类型上的方法始终是静态的。

type ID interface {
	Birthday() string
}

type id string

func NewID(i string) (ID, error) {
	if len(i) != 18 {
		return nil, errors.New(fmt.Sprintf("error id length:%v", len(i)))
	}
	return id(i), nil
}

func (i id) Birthday() string {
	return string(i[6:14])
}
const (
	fakeid = "412717199109031697"
)

func TestID_Birthday(t *testing.T) {
	var i ID
	i, _ = NewID(fakeid)
	found := i.Birthday()
	wanted := "19910903"
	if found != wanted {
		t.Errorf("unexpected birthday, wanted:%v, found:%v", wanted, found)
	}
}

除了线性的业务逻辑处理场景,在生产中还会遇到层状的业务流,但是不同层次之间又有很多功能是相通的,此时就需要借助“继承”之类的能力,来实现代码复用。遗憾的是 Golang 中并不存在继承,下面我们介绍它的替代者

在最知名的语言中,面向对象编程很多讨论是关于类型之间关系的。Go 采用了不同的实现 —— 隐藏类式的类型依赖。

在 Go 中,类型会自动满足指定其方法子集的任何接口,无需提前声明两种类型相关联。类型可以一次满足许多接口,而没有传统的多重继承的复杂性。 类型和接口之间没有明确的关系,所以不涉及类型层次结构。类似想法可以用来构造像类型安全的 Unix 管道一样的实现。

所有的“继承”之类的实现,在 Golang 中都能以组合(或内嵌)的方式来实现,组合和内嵌的对象可以是具体的类型,也可以抽象的接口。以下示例介绍了两种风格的实现:

type Man interface {
	Birthday() string
}

func NewMan(name, id string) (Man, error) {
	i, err := NewID(id)
	return &man{
		id:   i,
		name: name,
	}, err
}

func NewManEmbedding(name, id string) (Man, error) {
	i, err := NewID(id)
	return &manEmbedding{
		ID:   i,
		name: name,
	}, err
}

type man struct {
	id   ID
	name string
}

func (m *man) Birthday() string {
	return m.id.Birthday()
}

type manEmbedding struct {
	ID
	name string
}
const (
	peterid = "412717199109031697"
	samid   = "312717199109036148"
)

func TestMan_Birthday(t *testing.T) {
	peter, _ := NewMan("peter", peterid)
	sam, _ := NewMan("sam", samid)
	mans := []Man{peter, sam}
	for _, man := range mans {
		found := man.Birthday()
		wanted := "19910903"
		if found != wanted {
			t.Errorf("unexpected birthday, wanted:%v, found:%v", wanted, found)
		}
	}

}

Write go in go way. Golang 很多经典的思想都可以通过官方文档获得,例如:FAQ

源代码:https://github.com/cyningsun/go-test/tree/master/20210311-oop-in-go

本文作者:cyningsun
本文地址https://www.cyningsun.com/03-12-2021/oop-in-go.html
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-ND 3.0 CN 许可协议。转载请注明出处!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK