如何做網(wǎng)站的搜索欄網(wǎng)絡(luò)營(yíng)銷的推廣方法有哪些
面向?qū)ο缶幊?/h1>
Go語言的面向?qū)ο缶幊毯推渌Z言有非常大的差別。
Go語言的面向?qū)ο缶幊毯推渌Z言有非常大的差別。
Go 是一種面向?qū)ο蟮恼Z言嗎?
是和不是。雖然 Go 有類型和方法,并允許面向?qū)ο蟮木幊田L(fēng)格,但沒有類型層次結(jié)構(gòu)(繼承)。Go 中的“接口”概念提供了一種不同的方法,我們認(rèn)為這種方法易于使用,并且在某些方面更通用。還有一些方法可以將類型嵌入到其他類型中,以提供類似于(但不完全相同)子類化的東西。此外,Go 中的方法比 C++ 或 Java 中的方法更通用:它們可以為任何類型的數(shù)據(jù)定義,甚至是內(nèi)置類型,例如普通的“未裝箱”整數(shù)。它們不限于結(jié)構(gòu)(類)。
此外,缺少類型層次結(jié)構(gòu)使得 Go 中的“對(duì)象”感覺比 C++ 或 Java 等語言中的“對(duì)象”輕量級(jí)得多。
封裝數(shù)據(jù)和行為
結(jié)構(gòu)體定義
實(shí)例創(chuàng)建及初始化
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 = "2"e2.Name = "Rose"e2.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()) }
所以這種寫法會(huì)有復(fù)制的開銷。
-
第二種
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()) }
更推薦這種。
接口(定義交互協(xié)議)
Go的接口和很多主流編程語言的接口有很大的區(qū)別。
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// 計(jì)算函數(shù)操作的時(shí)長(zhǎng)
func timeSpend(inner IntConv) IntConv { // 以前的方法特別的長(zhǎng) 我們可以用自定義類型做替換// 類似裝飾者模式,對(duì)原來的函數(shù)進(jìn)行了一層包裝return func(n int) int {start := time.Now()ret := inner(n)fmt.Println("time spend:", time.Since(start).Seconds())return ret}
}
擴(kuò)展和復(fù)用(類似繼承)
Go語言無法天然支持繼承,但是又想要實(shí)現(xiàn)面向?qū)ο蟮奶匦浴?/p>
即父類對(duì)象 使用子類對(duì)象初始化,那么該父類對(duì)象調(diào)用的函數(shù)就是子類實(shí)現(xiàn)的函數(shù) ,從而滿足LSP(子類交換原則)。
案例一: Go語言 支持?jǐn)U展父類的功能,如下代碼:
package oriented_testimport ("fmt""testing"
)// Pet 類
type Pet struct {
}func (p *Pet) Speak(){ // Pet類的函數(shù)成員fmt.Print("Pet speak.\n")
}func (p *Pet) SpeakTo(host string) { // Pet類的函數(shù)成員p.Speak()fmt.Println("Pet SpeakTo ", host)
}// Dog 擴(kuò)展Pet的功能
type Dog struct {p *Pet
}// 擴(kuò)展父類的方法
func (d *Dog) Speak(){d.p.Speak()
}// 擴(kuò)展父類的方法
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")
}
以上測(cè)試代碼的輸出如下:
dog 的 SpeakTo 中調(diào)用了 dog 的 Speak,其中調(diào)用了 Pet 的 Speak,所以輸出正常。
Pet 和 Dog 調(diào)用不會(huì)相互影響,完全由用戶決定。
但是這和我們所想需要的不同,Pet 類有自己的方法,Dog 類有自己的方法,兩者作用域完全不同。
這里Go語言推出了匿名嵌套類型,即 Dog 類不用實(shí)現(xiàn)自己的和 Pet 類同名的方法即可,通過在 Dog 類的聲明中變更 Pet 成 員。
匿名嵌套類型
案例二: Go語言支持匿名函數(shù)類型
// Dog 擴(kuò)展Pet的功能
type Dog struct {Pet
}
這樣即不需要 Dog 聲明自己的同名函數(shù)成員,默認(rèn)的調(diào)用即為 Pet 成員函數(shù)的調(diào)用。
package oriented_testimport ("fmt""testing"
)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 擴(kuò)展Pet的功能
type Dog struct {Pet // 支持匿名嵌套類型
}func TestDog(t *testing.T) {var dog Dogdog.Speak()dog.SpeakTo("Test dog")
}
最終的輸出如下:
調(diào)用的都是 Pet 的成員函數(shù),感覺像是繼承了,因?yàn)槔^承默認(rèn)就是子類能夠使用父類的公有成員。
在匿名嵌套類型下,我們想要完整嘗試一下Go語言是否真正支持繼承,可以像之前的代碼一樣在 Dog 中實(shí)現(xiàn) Pet 的同名函數(shù),且能夠通過父類對(duì)象調(diào)用子類的成員方法,像 C++/Java 這樣進(jìn)行向上類型轉(zhuǎn)換(本身是不可能的,Go語言不支持顯式類型轉(zhuǎn)換)。
案例三: Go語言不支持繼承,如下代碼:
package oriented_testimport ("fmt""testing"
)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 擴(kuò)展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) // 這里就會(huì)編譯錯(cuò)誤dog.Speak()dog.SpeakTo("Test dog")
}
cannot use new(Dog) (value of type *Dog) as Pet value in variable declaration
不支持將Pet類型轉(zhuǎn)換為Dog類型
總結(jié)一下,Go語言并不支持繼承,能夠支持接口的擴(kuò)展 和 復(fù)用**(匿名嵌套類型)**,內(nèi)嵌這種方式是完全不能當(dāng)成繼承來用的,因?yàn)樗恢С衷L問子類的方法數(shù)據(jù)(重載),不支持LSP原則。
其中擴(kuò)展 就是不同類實(shí)現(xiàn)相同的成員函數(shù),能夠?qū)崿F(xiàn)類似于案例一中的擴(kuò)展接口形態(tài)。
復(fù)用則是通過匿名嵌套類型實(shí)現(xiàn) 類似于重載的功能,可以看看案例二的代碼。
多態(tài)
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\")"
}// 多態(tài)
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接口最佳實(shí)踐
錯(cuò)誤機(jī)制
error
package errorimport ("errors""testing"
)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)}
}
最佳實(shí)踐
盡早失敗,避免嵌套!
例:
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"))
}
其實(shí)上面這種修復(fù)方式是非常危險(xiǎn)的。
我們一定要當(dāng)心自己 revocer
在做的事,因?yàn)槲覀?revocer 并不檢測(cè)到底發(fā)生了什么錯(cuò)誤,而只是記錄了一下或者直接忽略掉了,這時(shí)可能系統(tǒng)某些核心資源消耗完了,但我們把他強(qiáng)制恢復(fù)之后系統(tǒng)依然是不能正常工作的,還會(huì)導(dǎo)致我們的健康檢查程序 health check
檢查不出當(dāng)前系統(tǒng)的問題,因?yàn)楹芏?health check 只是檢查當(dāng)前系統(tǒng)的進(jìn)程在還是不在, 因?yàn)槲覀兊倪M(jìn)程是在的,所以就會(huì)形成僵尸進(jìn)程
,它還活著,但它不能提供服務(wù)。
如果出現(xiàn)了這種問題,我們可以用 “Let is Crash”
可恢復(fù)的設(shè)計(jì)模式,我們直接 crash 掉,這樣守護(hù)進(jìn)程就會(huì)重新把服務(wù)進(jìn)程提起來(說的有點(diǎn)高大上,其實(shí)就是重啟),重啟是恢復(fù)不確定性錯(cuò)誤的最好方法。
包 package
構(gòu)建可復(fù)用的模塊(包)
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
引用另一個(gè)包中的方法:
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
}
如何使用遠(yuǎn)程的package
ConcurrentMap for GO
https://github.com/easierway/concurrent_map
使用 go get
命令導(dǎo)入
go get -u github.com/easierway/concurrent_map
package remoteimport (cm "github.com/easierway/concurrent_map" // 導(dǎo)入遠(yuǎn)程包"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路徑
常用的依賴管理工具
- godep:https://github.com/tools/godep
- glide:https://github.com/Masterminds/glide
- dep:https://github.com/golang/dep
安裝glide
-
mac環(huán)境,使用 brew 安裝 glide
brew install glide
安裝成功
-
初始化 glide
glide init
-
glide init 執(zhí)行完畢后,生成了一個(gè)
yaml
文件,并把依賴的包和版本號(hào)定義在了里面 -
在之前的目錄下執(zhí)行
glide install
然后就會(huì)在我們的指定的文件下面生成一個(gè)
vender
目錄和glide.lock
文件。 -
到此為止,Go 就能 搜索到 vender 目錄下面的 package 了,我們就通過 vender 來指定了包的路徑和版本號(hào),即實(shí)現(xiàn)了在同一環(huán)境下使用同一個(gè)包的不同版本依賴了。
筆記整理自極客時(shí)間視頻教程:Go語言從入門到實(shí)戰(zhàn)