
- package demo
-
- import "fmt"
-
-
- type name struct {
- firstname string
- lastname string
- }
-
- // Go 中有一个习惯用法,用于创建一个“构造函数”函数,该函数的名称前面带有单词“ New”,如下所示:
- func NewName(first, last string) name {
- return name{firstname: first, lastname: last}
- }
-
- // 我现在有一个“构造函数”函数,但是我需要能够访问这些字段。为此,我需要一种“方法”:
-
- func (n name) Fullname() string {
- return fmt.Sprintf("name: %s %s", n.firstname, n.lastname)
- }
-
-
- // main 包
- func main() {
- n := demo.NewName("Joe", "Soap")
- fmt.Println(n.Fullname())
- }
- type employee struct {
- employeeID string
- firstname string
- lastname string
- }
-
- func NewEmployee(id, first, last string) employee {
- return employee{employeeID: id, firstname: first, lastname: last}
- }
- func (e employee) ID() string {
- return e.employeeID
- }
-
- func (e employee) Fullname() string {
- return fmt.Sprintf("name: %s %s", e.firstname, e.lastname)
- }
-
- type client struct {
- clientID string
- firstname string
- lastname string
- }
-
- func NewClient(id, first, last string) client {
- return client{clientID: id, firstname: first, lastname: last}
- }
- func (c client) ID() string {
- return c.clientID
- }
- func (c client) Fullname() string {
- return fmt.Sprintf("name: %s %s", c.firstname, c.lastname)
- }
-
-
- // main 包
- func main() {
- employee1 := NewClient("C0001234", "Joe", "Soap")
- client1 := NewEmployee("1122", "Fred", "Bloggs")
- fmt.Println(employee1.ID())
- fmt.Println(employee1.Fullname())
- fmt.Println(client1.ID())
- fmt.Println(client1.Fullname())
- }
-
-
- // 输出
- C0001234
- name: Joe Soap
- 1122
- name: Fred Bloggs
但这样非常死板,如果我想添加一个字段,会发生什么?现在我有三个结构和三个方法要更新。语法很快变得复杂,有些东西可能会被遗漏。
好了,是时候简化代码了。我只需要将name嵌入到雇员employee和客户client中,然后只需要一个 FullName ()函数:(注意:代码没有拆包,为了测试方便放到一个包里面了,后面一样。)
- package main
-
- import "fmt"
-
- type name struct {
- firstname string
- lastname string
- }
-
- func NewName(first, last string) name {
- return name{firstname: first, lastname: last}
- }
-
- func (n name) Fullname() string {
- return fmt.Sprintf("name: %s %s", n.firstname, n.lastname)
- }
-
- type employee struct {
- employeeID string
- name
- }
- func NewEmployee(id, first, last string) employee {
- return employee{
- employeeID: id,
- name: name{
- firstname: first,
- lastname: last,
- },
- }
- }
- type client struct {
- clientID string
- name
- }
- func NewClient(id, first, last string) client {
- return client{
- clientID: id,
- name: name{
- firstname: first,
- lastname: last,
- },
- }
- }
-
- func (e employee) ID() string {
- return e.employeeID
- }
-
- func (c client) ID() string {
- return c.clientID
- }
-
-
- func main() {
- employee1 := NewClient("C0001234", "Joe", "Soap")
- client1 := NewEmployee("1122", "Fred", "Bloggs")
- fmt.Println(employee1.ID())
- fmt.Println(employee1.Fullname())
- fmt.Println(client1.ID())
- fmt.Println(client1.Fullname())
- }
上面可以进一步简化,上面代码仍然需要构造三次 name。我已经有了一个名称构造函数,所以我可以像这样直接将名称传递给 NewEmployee ()和 NewClient () :
func NewEmployee(id string, name name) employee {
return employee{
employeeID: id,
name: name,
}
}func NewClient(id string, name name) client {
return client{
clientID: id,
name: name,
}
}
修改后:
- package main
-
- import "fmt"
-
- type name struct {
- firstname string
- lastname string
- }
-
- func NewName(first, last string) name {
- return name{firstname: first, lastname: last}
- }
-
- func (n name) Fullname() string {
- return fmt.Sprintf("name: %s %s", n.firstname, n.lastname)
- }
-
- type employee struct {
- employeeID string
- name
- }
-
- type client struct {
- clientID string
- name
- }
-
- func NewEmployee(id string, name name) employee {
- return employee{
- employeeID: id,
- name: name,
- }
- }
- func NewClient(id string, name name) client {
- return client{
- clientID: id,
- name: name,
- }
- }
-
-
- func (e employee) ID() string {
- return e.employeeID
- }
-
- func (c client) ID() string {
- return c.clientID
- }
-
-
- func main() {
- employee1 := NewClient("C0001234", NewName("Joe", "Soap"))
- client1 := NewEmployee("1122", NewName("Fred", "Bloggs"))
- fmt.Println(employee1.ID())
- fmt.Println(employee1.Fullname())
- fmt.Println(client1.ID())
- fmt.Println(client1.Fullname())
- }
需要修改的地方:体验一般 ...7处改动,少改一个地方也不行
type name struct { // struct
salutation string
firstname string
lastname string
}
func NewName(sal, first, last string) name { // 构造函数
return name{
salutation: sal,
firstname: first,
lastname: last,
}
}func (n name) Fullname() string { // 函数
return fmt.Sprintf("name: %s %s %s", n.salutation, n.firstname, n.lastname)
}
还有:main方法的入参也要修改
employee1 := NewClient("C0001234", NewName("Dr","Joe", "Soap"))
client1 := NewEmployee("1122", NewName("Mr","Fred", "Bloggs"))
看到ID方法做同样的事情。代码中有两个 ID:ClientID和 Employee.EmployeeID。它们都是字符串类型。因此,让我们创建一个带有构造函数和打印方法的通用 Type:
效果就是:只保留一个ID函数即可。清理了每个type自己定义的ID()函数。
- package main
-
- import "fmt"
-
- // add new Type id
- type id string
-
- func NewID(id id) id {
- return id
- }
-
- func (i id) ID() string {
- return string(i)
- }
-
- type name struct {
- firstname string
- lastname string
- }
-
- func NewName(first, last string) name {
- return name{firstname: first, lastname: last}
- }
-
- func (n name) Fullname() string {
- return fmt.Sprintf("name: %s %s", n.firstname, n.lastname)
- }
-
- type employee struct {
- id
- name
- }
-
- type client struct {
- id
- name
- }
-
- func NewEmployee(id id, name name) employee {
- return employee{
- id: id,
- name: name,
- }
- }
- func NewClient(id id, name name) client {
- return client{
- id: id,
- name: name,
- }
- }
-
-
- func main() {
- employee1 := NewClient(NewID("C0001234"), NewName("Joe", "Soap"))
- client1 := NewEmployee(NewID("1122"), NewName("Fred", "Bloggs"))
- fmt.Println(employee1.ID())
- fmt.Println(employee1.Fullname())
- fmt.Println(client1.ID())
- fmt.Println(client1.Fullname())
- }
前面几步都是使用Type做的简化;接口就是对一些共性的抽象;上面的我们定义了id类型(type id string),现在需求变更了,我们希望我们的 EmployeeID 是一个数字,并且我们希望以一个部门作为前缀。使用简单的字符串 Type 不再能cover住我们的变化了。我们希望能够存储代码和部门。我们还希望将 EmployeeID 显示为两个字段,用连字符分隔。为此,我们需要一个 struct 和一些新方法。 这意味着,我们前面定义的id类型将被废弃掉!(但是Client并没有需要扩展的需求,即ClientID还是一个字符串)换成如下 employeeID struct ....这样其实和name struct没有啥区别了....
- type employeeID struct {
- department string
- id int
- }
- func NewEmployeeID(department string, id int) employeeID {
- return employeeID{
- department: department,
- id: id,
- }
- }
- func (eid employeeID) ID() string {
- return fmt.Sprintf("%s-%d", eid.department, eid.id)
- }
思考下,如果我们能不能换成一个接口呢?需要及兼容clientID 的原有逻辑,又能承接employeeID新的数据类型,什么样的接口呢? 这个接口肯定有ID()的输出能力...整理代码如下:
- package main
-
- import "fmt"
-
- type name struct {
- firstname string
- lastname string
- }
-
- func NewName(first, last string) name {
- return name{firstname: first, lastname: last}
- }
-
- func (n name) Fullname() string {
- return fmt.Sprintf("name: %s %s", n.firstname, n.lastname)
- }
-
-
-
- // new Person interface
- type Person interface {
- ID() string
- }
- //- ----------------------通用ID type begin--------------------------------------
-
- // add new Type id
- type id string
-
- func NewID(id id) id {
- return id
- }
-
- func (i id) ID() string {
- return string(i)
- }
- //- ----------------------通用ID type end --------------------------------------
-
-
- // ------------------- employeeID begin------------------
-
- type employeeID struct {
- department string
- id int
- }
- func NewEmployeeID(department string, id int) employeeID {
- return employeeID{
- department: department,
- id: id,
- }
- }
- func (eid employeeID) ID() string {
- return fmt.Sprintf("%s-%d", eid.department, eid.id)
- }
- // ------------------- employeeID end ------------------
-
- // add person to employee and client structs and constructors
- type employee struct {
- Person
- name
- }
-
- type client struct {
- Person
- name
- }
-
- func NewEmployee(person Person, name name) employee {
- return employee{
- Person: person,
- name: name,
- }
- }
-
- func NewClient(person Person, name name) client {
- return client{
- Person: person,
- name: name,
- }
- }
-
- func main() {
- employee1 := NewClient(NewEmployeeID("Sales", 1234), NewName("Joe", "Soap"))
- client1 := NewEmployee(NewID("1122"), NewName("Fred", "Bloggs"))
- fmt.Println(employee1.ID())
- fmt.Println(employee1.Fullname())
- fmt.Println(client1.ID())
- fmt.Println(client1.Fullname())
- }
现在,看看这段代码,我们已经在员工和客户端中嵌入了 Person 接口,GOOD!但是我们可以进一步简化。首先,我们可以对 name struct 做同样的事情,就像我们对 id 所做的那样,添加一个接口,然后将这两个接口都嵌入。go语言定义接口,通常使用动词作为名称,例如,一个读取的接口是一个 Reader,一个写入的接口是一个 Writer,嵌入了 Reader 和 Writer 接口的接口被称为 ReadWriter。
注意:NewEmployee ()和 NewClient 现在返回的接口是 Person,而不是像以前一样返回 Employee 和 client,这对于外面的框架使用提供了非常友好的便利。比如,外面统一接受person类型的参数,做通用的业务处理逻辑下面的 Details(p Person) 方法。现在我的代码更短、更清晰,希望更具描述性了。
- package main
-
- import "fmt"
-
- // new interface embedding the interfaces Identifier and Namer 合并了所有接口
- type Person interface {
- Identifier
- Namer
- }
-
-
- // renaming Person as Namer 接口:实现 --------
- type Namer interface {
- Fullname() string
- }
-
- type name struct {
- salutation string
- firstname string
- lastname string
- }
-
- func NewName(sal, first, last string) name {
- return name{
- salutation: sal,
- firstname: first,
- lastname: last,
- }
- }
-
- func (n name) Fullname() string {
- return fmt.Sprintf("name: %s %s %s", n.salutation, n.firstname, n.lastname)
- }
- // renaming Person as Namer 接口:实现 --------
-
-
- // ------ Identifier 接口 :实现 -----------
- type Identifier interface {
- ID() string
- }
- type employeeID struct {
- department string
- id int
- }
- func NewEmployeeID(department string, id int) employeeID {
- return employeeID{
- department: department,
- id: id,
- }
- }
- func (eid employeeID) ID() string {
- return fmt.Sprintf("%s-%d", eid.department, eid.id)
- }
-
- type id string
-
- func NewID(id id) id {
- return id
- }
-
- func (i id) ID() string {
- return string(i)
- }
- // ------ Identifier 接口 :实现 -----------
-
- type employee struct {
- Identifier
- Namer
- }
-
- func NewEmployee(id Identifier, name Namer) Person {
- return employee{
- Identifier: id,
- Namer: name,
- }
- }
-
- type client struct {
- Identifier
- Namer
- }
-
- func NewClient(id Identifier, name Namer) Person {
- return client{
- Identifier: id,
- Namer: name,
- }
- }
-
- func Details(p Person) {
- fmt.Printf("ID:[%s] Name: %s\n", p.ID(), p.Fullname())
- }
-
- func main() {
- employee1 :=NewEmployee(NewEmployeeID("Sales", 1234), NewName("Dr", "Joe", "Soap"))
- client1 := NewClient(NewID("1122"), NewName("Mr", "Fred", "Bloggs"))
- id := NewID("foo")
- fmt.Println(employee1.ID())
- fmt.Println(employee1.Fullname())
- fmt.Println(client1.ID())
- fmt.Println(client1.Fullname())
- fmt.Printf("%s\n", id.ID())
-
- Details(employee1)
- Details(client1)
- }
-
- // output
- Sales-1234
- name: Dr Joe Soap
- 1122
- name: Mr Fred Bloggs
- foo
- ID:[Sales-1234] Name: name: Dr Joe Soap
- ID:[1122] Name: name: Mr Fred Bloggs