空對象設計模式是一種行為型設計模式,主要用於應對空對象的檢查。使用這種設計模式可以避免對空對象進行檢查。也就是說,在這種模式下,使用空對象不會造成異常。
空對象模式的組件包括:
- 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!!
