A God Object in software development occurs when a single class (or other item) either knows too much or does too much (or, for extra fun, both).
The solution to this anti-pattern is fairly straightforward and rather ironic: smite the God object.
Although you may prefer the term "refactor".
The Rundown
- Name: God Object
- Summary: A class or other object knows too much or does too much, and is therefore difficult to test or change.
- Type: Programming
- Common?: Omnipresent.
An Example
Let's say you are building an employee management application (yes I know, very original). Let's also say that you have the following class:
public class Employee
{
public int ID { get; set; }
public DateTime BirthDate { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string SpouseFirstName { get; set; }
public string SpouseLastName { get; set; }
public string ChildOneFirstName { get; set; }
public string ChildOneLastName { get; set; }
...
}
This is a bit of a silly example, I'll admit, but it's a God object nonetheless. This Employee
class has WAY too much information. What happens if some employee has seven kids? Are they all going to be properties in this class?
Clearly this class needs to be refactored, split into many classes (possibly including Spouse
and Child
classes). But what about a more... insidious example?
public static class EmployeeManager
{
public static void HireEmployee(Employee employee) {...}
public static void TerminateEmployee(int employeeID) {...}
public static void EditEmployee(Employee employee) {...}
public static void AddVacationTime(int employeeID, DateTime vacationDate) {...}
public static void CancelVacationTime(int employeeID, DateTime vacationDate) {...}
public static void AddAddress(int employeeID, Address address) {...}
public static void RemoveAddress(int employeeID, int addressID) {...}
public static void GiveBonus(int employeeID, decimal bonus) {...}
public static void AssignEquipment(int employeeID, Equipment equipment) {...}
public static void GiveRaise(int employeeID, decimal amount) {...}
public static void DockPay(int employeeID, decimal amount) {...}
public static void AddSchedule(int employeeID, Schedule schedule) {...}
public static void AddPhoneNumber(int employeeID, string phoneNumber) {...}
}
On the surface, this EmployeeManager
class looks like it is organized properly. Every method has to do directly with employees; heck, every method takes an employee ID as the first parameter! It is what these methods most likely do, however, makes this tricky.
For example, take the GiveRaise
and DockPay
methods. In most countries, this process is, well, a process, taking multiple steps and needing multiple people's opinion. How much do you want to bet these two methods should be in their own class?
The same argument can be made for AddSchedule
, or the Address functions. The fact is, this class is way too large, has too many methods, for it to function on its own. Can you imagine trying to test this?
When Does This Happen?
Like many software anti-patterns, the God object rears its ugly head most often when the programmer is lacking experience (or, alternately, getting terrible advice from his superiors). You will generally hear these common "arguments" for the validity of a God object:
- "But I need all this info in one easily-accessible place." OR
- "It's less work to change a single file."
Both of these arguments are fine in a vacuum, but both fall apart completely once the system in question needs to change in any manner. Aggregating lots of information under a single class means any time any of that information has to change, the class itself has to change, and that means with each change comes the possibility of introducing bugs. One bug is not a problem, but having more than one compounds the effort needed to track them down (e.g. how long does it take to solve one solitary bug versus two bugs which interact?).
So now we have two problems: first, how do we prevent God objects from occurring, and second, how do we fix them if they already exist? Luckily for us, they have the same solution.
Strategies to Solve
Let's start with fixing God objects that already exist. The solution is to smite refactor the object so as to split related functionality into smaller, manageable pieces.
For example, let's refactor the Employee
object from earlier:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
}
public class Employee : Person
{
public int ID { get; set; }
}
public class Spouse : Person
{
public int EmployeeID { get; set; }
}
public class Child : Person
{
public int EmployeeID { get; set; }
}
Now, if we need to change the data tracked for an employee's spouse, we need only change the Spouse
class, not any others. This makes it easier to refactor and test.
But what about preventing God objects from occurring in the first place? In this case, we also need to refactor, but not the code; rather, we need to refactor the design, which is far more difficult. Changing the design to support more "object-oriented" procedures saves you much time in the long run, because it allows your system to grow and adapt to change more readily.
Let's try this on the EmployeeManager
class from earlier.
public static class EmployeeManager
{
public static void HireEmployee(Employee employee) {...}
public static void TerminateEmployee(int employeeID) {...}
public static void EditEmployee(Employee employee) {...}
public static void AddSchedule(int employeeID, Schedule schedule) {...}
public static void AddPhoneNumber(int employeeID, string phoneNumber) {...}
}
public static class VacationTimeManager
{
public static void AddVacationTime(int employeeID, DateTime vacationDate) {...}
public static void CancelVacationTime(int employeeID, DateTime vacationDate) {...}
}
public static class EmployeeAddressManager
{
public static void AddAddress(int employeeID, Address address) {...}
public static void RemoveAddress(int employeeID, int addressID) {...}
}
public static class PayManager
{
public static void GiveBonus(int employeeID, decimal bonus) {...}
public static void GiveRaise(int employeeID, decimal amount) {...}
public static void DockPay(int employeeID, decimal amount) {...}
}
public static class EquipmentManager
{
public static void AssignEquipment(int employeeID, Equipment equipment) {...}
}
And that's only the beginning. You could easily make an argument for AddSchedule
and AddPhoneNumber
to be in their own classes. But now, as before, if any of these need to change, only their corresponding classes will be affected, and not any of the others.
Summary
God Objects happen in poorly-designed systems, where an assumption happens that all necessary data needs to be kept tightly coupled. This assumption is almost always false. The solution is to refactor, either the code or the design, so as to split related functionalities into their own independent groupings, which allow them to change independently of one another and thus adapt to changes in the system.
In other words: smite the God Object.
Have you encountered God Objects in the wild? Are you too busy dodging lightning bolts to tell me about them? Find some shelter and let me know about your experiences in the comments! Or else head on back to the series index page to see some more anti-patterns.
Happy Coding!