Empty object design pattern is a behavioral design pattern, which is mainly used to deal with the inspection of empty objects. Using this design pattern avoids checking empty objects. That is, in this mode, using empty objects does not cause exceptions.
The components of the empty object pattern include:
- Entity: interface, which defines the methods to be implemented by the child struct
- ConcreteEntity: implements the concrete struct of Entity
- NullEntity: This represents an empty object. Although the Entity interface is also implemented, its values are empty
- Client: this class will get the instance of the Entity interface implementation class and use it. The implementation class is not concerned here. It is ConcreteEntity still NullEntity, the same processing will be performed for both.
Take an example: suppose there is a university with multiple departments, and each department has a certain number of professors.
A department can be represented by an interface:
type department interface { getNumberOfProfessors() int getName() string }
college is also an interface:
type college struct { departments []department }
Now suppose an organization wants to count the number of professors in each department of the University.
In this example, assuming that there is no department in the University, we will use the empty object mode. Here, a nullDepartment is defined to represent the nonexistent system.
nullDepartment.go:
type nullDepartment struct { numberOfProfessors int } func (c *nullDepartment) getNumberOfProfessors() int { return 0 } func (c *nullDepartment) getName() string { return "nullDepartment" }
The statistical code is in 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 }
Note this Code:
- agency.go doesn't care whether a department exists in the University. When the Department does not exist, the university only needs to return a nullDepartment object
- agency.go handles the nullDepartment object and other department implementation class objects in the same way. There is no need to check the null value. Just call getNumberOfProfessors() directly
These are the benefits of using empty object mode.
Here are other codes.
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{} }
Department of computer science, computerscience.go:
type computerScience struct { numberOfProfessors int } func (c *computerScience) getNumberOfProfessors() int { return c.numberOfProfessors } func (c *computerScience) getName() string { return "computerScience" }
Department of mathematics, mechanical.go:
type mechanical struct { numberOfProfessors int } func (c *mechanical) getNumberOfProfessors() int { return c.numberOfProfessors } func (c *mechanical) getName() string { return "mechanical" }
Execute agency.go, and the output is as follows:
Total number of professors in college1 is 9 Total number of professors in college2 is 2
Code uploaded to GitHub: zhyea / go-patterns / null-object-pattern
End!!