具有口碑的柳州网站建设推荐,东莞常平中学,做贸易的网站有哪些,信息如何优化上百度首页公司面向对象编程 Go语言的面向对象编程和其他语言有非常大的差别。 Go 是一种面向对象的语言吗#xff1f;
是和不是。虽然 Go 有类型和方法#xff0c;并允许面向对象的编程风格#xff0c;但没有类型层次结构#xff08;继承#xff09;。Go 中的“接口”概念提供了一种不…面向对象编程 Go语言的面向对象编程和其他语言有非常大的差别。 Go 是一种面向对象的语言吗
是和不是。虽然 Go 有类型和方法并允许面向对象的编程风格但没有类型层次结构继承。Go 中的“接口”概念提供了一种不同的方法我们认为这种方法易于使用并且在某些方面更通用。还有一些方法可以将类型嵌入到其他类型中以提供类似于但不完全相同子类化的东西。此外Go 中的方法比 C 或 Java 中的方法更通用它们可以为任何类型的数据定义甚至是内置类型例如普通的“未装箱”整数。它们不限于结构类。
此外缺少类型层次结构使得 Go 中的“对象”感觉比 C 或 Java 等语言中的“对象”轻量级得多。
封装数据和行为
结构体定义 实例创建及初始化 type Employee struct {Id stringName stringAge int
}func TestCreateEmployeeObj(t *testing.T) {e : Employee{0, Bob, 20}e1 : Employee{Name: Mike, Age: 30}e2 : new(Employee) // 返回指针e2.Id 2e2.Name Rosee2.Age 22t.Log(e)t.Log(e1)t.Log(e1.Id)t.Log(e2)t.Logf(e is %T, e)t.Logf(e2 is %T, e2)
}行为方法定义 第一种 func (e Employee) String() string {fmt.Printf(Address is %x, unsafe.Pointer(e.Name))fmt.Println()return fmt.Sprintf(ID:%s-Name:%s-Age:%d, e.Id, e.Name, e.Age)
}func TestStructOperations(t *testing.T) {e : Employee{0, Bob, 20}fmt.Printf(Address is %x, unsafe.Pointer(e.Name))fmt.Println()t.Log(e.String())
}所以这种写法会有复制的开销。 第二种 func (e *Employee) String() string {fmt.Printf(Address is %x, unsafe.Pointer(e.Name))fmt.Println()return fmt.Sprintf(ID:%s-Name:%s-Age:%d, e.Id, e.Name, e.Age)
}func TestStructOperations(t *testing.T) {e : Employee{0, Bob, 20} // 传递引用fmt.Printf(Address is %x, unsafe.Pointer(e.Name))fmt.Println()t.Log(e.String())
}更推荐这种。
接口定义交互协议 Go的接口和很多主流编程语言的接口有很大的区别。 Java代码的示例 Duck Type式接口
Go语言的interface type Programmer interface {WriteHelloWorld() string
}type GoProgrammer struct {
}func (g *GoProgrammer) WriteHelloWorld() string { // duck type 鸭子类型return Hello World
}func TestClient(t *testing.T) {var p Programmerp new(GoProgrammer)t.Log(p.WriteHelloWorld())
}Go接口 接口变量 自定义类型 type IntConv func(op int) int// 计算函数操作的时长
func timeSpend(inner IntConv) IntConv { // 以前的方法特别的长 我们可以用自定义类型做替换// 类似装饰者模式对原来的函数进行了一层包装return func(n int) int {start : time.Now()ret : inner(n)fmt.Println(time spend:, time.Since(start).Seconds())return ret}
}扩展和复用类似继承
Go语言无法天然支持继承但是又想要实现面向对象的特性。
即父类对象 使用子类对象初始化那么该父类对象调用的函数就是子类实现的函数 从而满足LSP子类交换原则。
案例一 Go语言 支持扩展父类的功能如下代码
package oriented_testimport (fmttesting
)// Pet 类
type Pet struct {
}func (p *Pet) Speak(){ // Pet类的函数成员fmt.Print(Pet speak.\n)
}func (p *Pet) SpeakTo(host string) { // Pet类的函数成员p.Speak()fmt.Println(Pet SpeakTo , host)
}// Dog 扩展Pet的功能
type Dog struct {p *Pet
}// 扩展父类的方法
func (d *Dog) Speak(){d.p.Speak()
}// 扩展父类的方法
func (d *Dog) SpeakTo(host string) {d.Speak()fmt.Println(Dog Speakto , host)
}func TestDog(t *testing.T) {dog : new(Dog)dog.SpeakTo(Test dog)
}以上测试代码的输出如下 dog 的 SpeakTo 中调用了 dog 的 Speak其中调用了 Pet 的 Speak所以输出正常。 Pet 和 Dog 调用不会相互影响完全由用户决定。
但是这和我们所想需要的不同Pet 类有自己的方法Dog 类有自己的方法两者作用域完全不同。
这里Go语言推出了匿名嵌套类型即 Dog 类不用实现自己的和 Pet 类同名的方法即可通过在 Dog 类的声明中变更 Pet 成 员。
匿名嵌套类型
案例二 Go语言支持匿名函数类型
// Dog 扩展Pet的功能
type Dog struct {Pet
}这样即不需要 Dog 声明自己的同名函数成员默认的调用即为 Pet 成员函数的调用。
package oriented_testimport (fmttesting
)type Pet struct {
}func (p *Pet) Speak(){fmt.Print(Pet speak.\n)
}func (p *Pet) SpeakTo(host string) {p.Speak()fmt.Println(Pet SpeakTo , host)
}// Dog 扩展Pet的功能
type Dog struct {Pet // 支持匿名嵌套类型
}func TestDog(t *testing.T) {var dog Dogdog.Speak()dog.SpeakTo(Test dog)
}最终的输出如下 调用的都是 Pet 的成员函数感觉像是继承了因为继承默认就是子类能够使用父类的公有成员。
在匿名嵌套类型下我们想要完整尝试一下Go语言是否真正支持继承可以像之前的代码一样在 Dog 中实现 Pet 的同名函数且能够通过父类对象调用子类的成员方法像 C/Java 这样进行向上类型转换本身是不可能的Go语言不支持显式类型转换。
案例三 Go语言不支持继承如下代码
package oriented_testimport (fmttesting
)type Pet struct {
}func (p *Pet) Speak(){fmt.Print(Pet speak.\n)
}func (p *Pet) SpeakTo(host string) {p.Speak()fmt.Println(Pet SpeakTo , host)
}// Dog 扩展Pet的功能
type Dog struct {//p *PetPet // 支持匿名嵌套类型
}// 重载父类的方法
func (d *Dog) Speak(){fmt.Print(Dog speak.\n)
}// 重载父类的方法
func (d *Dog) SpeakTo(host string) {d.Speak()fmt.Println(Dog Speakto , host)
}func TestDog(t *testing.T) {var dog Pet new(Dog) // 这里就会编译错误dog.Speak()dog.SpeakTo(Test dog)
}cannot use new(Dog) (value of type *Dog) as Pet value in variable declaration
不支持将Pet类型转换为Dog类型总结一下Go语言并不支持继承能够支持接口的扩展 和 复用**匿名嵌套类型**内嵌这种方式是完全不能当成继承来用的因为它不支持访问子类的方法数据重载不支持LSP原则。
其中扩展 就是不同类实现相同的成员函数能够实现类似于案例一中的扩展接口形态。
复用则是通过匿名嵌套类型实现 类似于重载的功能可以看看案例二的代码。
多态 type Programmer interface {WriteHelloWorld() string
}type GoProgrammer struct {
}func (g *GoProgrammer) WriteHelloWorld() string {return fmt.Println(\Hello World\)
}type JavaProgrammer struct {
}func (j *JavaProgrammer) WriteHelloWorld() string {return System.out.println(\Hello World\)
}// 多态
func writeFirstProgram(p Programmer) {fmt.Printf(%T %v\n, p, p.WriteHelloWorld())
}func TestClient(t *testing.T) {goProgrammer : new(GoProgrammer)javaProgrammer : new(JavaProgrammer)writeFirstProgram(goProgrammer)writeFirstProgram(javaProgrammer)
}空接口与断言 func DoSomething(p interface{}) {// . 断言p.(int)p断言为int类型if i, ok : p.(int); ok {fmt.Println(Integer, i)return}if s, ok : p.(string); ok {fmt.Println(string, s)return}fmt.Println(UnKnow type)
}func TestEmptyInterface(t *testing.T) {DoSomething(10)DoSomething(10)DoSomething(10.00)
}Go接口最佳实践 错误机制
error package errorimport (errorstesting
)func GetFibonacci(n int) ([]int, error) {if n 2 || n 100 {return nil, errors.New(n should be in [2, 100])}fibList : []int{1, 1}for i : 2; i n; i {fibList append(fibList, fibList[i-1]fibList[i-2])}return fibList, nil
}func TestGetFibonacci(t *testing.T) {if v, err : GetFibonacci(-10); err ! nil {t.Error(err)} else {t.Log(v)}
}最佳实践 尽早失败避免嵌套
例
func GetFibonacci2(str string) {var (i interr errorlist []int)if i, err strconv.Atoi(str); err ! nil {fmt.Println(Error, err)return}if list, err GetFibonacci(i); err ! nil {fmt.Println(Error, err)return}fmt.Println(list)
}panic panic vs os.Exit revocer recover 类似于 java 的 catch。
func TestRecover(t *testing.T) {defer func() {if err : recover(); err ! nil {fmt.Println(recovered from, err)}}()fmt.Println(Start)panic(errors.New(something wrong))
}其实上面这种修复方式是非常危险的。
我们一定要当心自己 revocer 在做的事因为我们 revocer 并不检测到底发生了什么错误而只是记录了一下或者直接忽略掉了这时可能系统某些核心资源消耗完了但我们把他强制恢复之后系统依然是不能正常工作的还会导致我们的健康检查程序 health check 检查不出当前系统的问题因为很多 health check 只是检查当前系统的进程在还是不在 因为我们的进程是在的所以就会形成僵尸进程它还活着但它不能提供服务。 如果出现了这种问题我们可以用 “Let is Crash” 可恢复的设计模式我们直接 crash 掉这样守护进程就会重新把服务进程提起来说的有点高大上其实就是重启重启是恢复不确定性错误的最好方法。
包 package
构建可复用的模块包 my_series.go
package seriesfunc GetFibonacci(n int) ([]int, error) {ret : []int{1, 1}for i : 2; i n; i {ret append(ret, ret[i-1]ret[i-2])}return ret, nil
}package_test.go
引用另一个包中的方法
package clientimport (mygoland/geekvideo/ch13/series // 包路径要从自己的gopath开始写起testing
)func TestPackage(t *testing.T) {t.Log(series.GetFibonacci(10))
}init方法 func init() {fmt.Println(init1)
}func init() {fmt.Println(init2)
}func GetFibonacci(n int) []int {ret : []int{1, 1}for i : 2; i n; i {ret append(ret, ret[i-1]ret[i-2])}return ret
}如何使用远程的package ConcurrentMap for GO
https://github.com/easierway/concurrent_map
使用 go get 命令导入
go get -u github.com/easierway/concurrent_mappackage remoteimport (cm github.com/easierway/concurrent_map // 导入远程包testing
)func TestConcurrentMap(t *testing.T) {m : cm.CreateConcurrentMap(99)m.Set(cm.StrKey(key), 10)t.Log(m.Get(cm.StrKey(key)))
}依赖管理
Go未解决的依赖问题 vendor路径 常用的依赖管理工具
godephttps://github.com/tools/godepglidehttps://github.com/Masterminds/glidedephttps://github.com/golang/dep
安装glide mac环境使用 brew 安装 glide brew install glide安装成功 初始化 glide glide initglide init 执行完毕后生成了一个 yaml 文件并把依赖的包和版本号定义在了里面 在之前的目录下执行glide install 然后就会在我们的指定的文件下面生成一个 vender 目录和 glide.lock 文件。 到此为止Go 就能 搜索到 vender 目录下面的 package 了我们就通过 vender 来指定了包的路径和版本号即实现了在同一环境下使用同一个包的不同版本依赖了。 笔记整理自极客时间视频教程Go语言从入门到实战