空物件設計模式是一種行為型設計模式,主要用於應對空物件的檢查。使用這種設計模式可以避免對空物件進行檢查。也就是說,在這種模式下,使用空物件不會造成異常。
空物件模式的元件包括:
- Entity:介面,定義了子struct需要實現的方法
- ConcreteEntity:實現了Entity 的具體struct
- NullEntity:這個就表示了空物件,雖然也實現了Entity介面,但它的值都是空的
- Client:這個類會獲取Entity介面實現類的例項並使用它。這裡並不關注實現類是
ConcreteEntity
還是NullEntity
,對二者會進行相同的處理。
用個例子來說一下:假設有一所大學,大學有多個系,每個系都有一定數量的教授。
系(department)可以用一個介面來表示:
type department interface { getNumberOfProfessors() int getName() string }
大學(college)也是一個介面:
type college struct { departments []department }
現在假設有一個機構想統計下大學每個系的教授數量。
在這個例子裡,假設大學裡沒有某個系,我們就會用到空物件模式。這裡定義了一個nullDepartment
來表示不存在的系。
nullDepartment.go:
type nullDepartment struct { numberOfProfessors int } func (c *nullDepartment) getNumberOfProfessors() int { return 0 } func (c *nullDepartment) getName() string { return "nullDepartment" }
統計的程式碼在agency.go裡:
func main() { college1 := createCollege1() college2 := createCollege2() totalProfessors := 0 departmentArray := []string{"computerScience", "mechanical", "civil", "electronics"} for _, departmentName := range departmentArray { d := college1.getDepartment(departmentName) totalProfessors += d.getNumberOfProfessors() } fmt.Printf("Total number of professors in college1 is %d\n", totalProfessors) //Reset the professor count totalProfessors = 0 for _, departmentName := range departmentArray { d := college2.getDepartment(departmentName) totalProfessors += d.getNumberOfProfessors() } fmt.Printf("Total number of professors in college2 is %d\n", totalProfessors) } func createCollege1() *college { college := &college{} college.addDepartment("computerScience", 4) college.addDepartment("mechanical", 5) return college } func createCollege2() *college { college := &college{} college.addDepartment("computerScience", 2) return college }
注意這段程式碼:
- agency.go 並不關心某個系在大學裡是否存在。當這個系不存在時,大學只需要返回一個
nullDepartment
物件即可 - agency.go 對
nullDepartment
物件和其他department
實現類的物件做了相同處理,這之中不需要對空值進行檢查,直接呼叫getNumberOfProfessors()
就可以了
以上就是使用空物件模式的好處了。
下面是其他的程式碼。
college.go:
type college struct { departments []department } func (c *college) addDepartment(departmentName string, numOfProfessors int) { if departmentName == "computerScience" { computerScienceDepartment := &computerScience{numberOfProfessors: numOfProfessors} c.departments = append(c.departments, computerScienceDepartment) } if departmentName == "mechanical" { mechanicalDepartment := &mechanical{numberOfProfessors: numOfProfessors} c.departments = append(c.departments, mechanicalDepartment) } return } func (c *college) getDepartment(departmentName string) department { for _, department := range c.departments { if department.getName() == departmentName { return department } } //Return a null department if the department doesn't exits return &nullDepartment{} }
計算機系,computerscience.go:
type computerScience struct { numberOfProfessors int } func (c *computerScience) getNumberOfProfessors() int { return c.numberOfProfessors } func (c *computerScience) getName() string { return "computerScience" }
數學系,mechanical.go:
type mechanical struct { numberOfProfessors int } func (c *mechanical) getNumberOfProfessors() int { return c.numberOfProfessors } func (c *mechanical) getName() string { return "mechanical" }
執行agency.go,輸出內容如下:
Total number of professors in college1 is 9 Total number of professors in college2 is 2
程式碼已上傳至GitHub: zhyea / go-patterns / null-object-pattern
End!!