GoLang設計模式12 - 空物件模式

robin·張發表於2021-11-06

空物件設計模式是一種行為型設計模式,主要用於應對空物件的檢查。使用這種設計模式可以避免對空物件進行檢查。也就是說,在這種模式下,使用空物件不會造成異常。

空物件模式的元件包括:

  • 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!!

相關文章