Follow these best practices to eliminate God objects and design classes that are loosely coupled, cohesive, and easy to maintain Credit: geralt In object-oriented programming (OOP), so-called God objects are an anti-pattern because they violate a number of good design principles. God objects are objects of classes that know too much or do too much, and hence make your code tightly coupled and difficult to maintain. This article talks about this anti-pattern and how you can get rid of it. To work with the code examples provided in this article, you should have Visual Studio 2019 installed in your system. If you don’t already have a copy, you can download Visual Studio 2019 here. Create a console application project in Visual Studio First off, let’s create a .NET Core console application project in Visual Studio. Assuming Visual Studio 2019 is installed in your system, follow the steps outlined below to create a new .NET Core console application project in Visual Studio. Launch the Visual Studio IDE. Click on “Create new project.” In the “Create new project” window, select “Console App (.NET Core)” from the list of templates displayed. Click Next. In the “Configure your new project” window shown next, specify the name and location for the new project. Click Create. This will create a new .NET Core console application project in Visual Studio 2019. We’ll use this project in the subsequent sections of this article. What is a God object? Writing code without following the best practices might lead to code that is hard to debug, extend, and maintain over time. Object-oriented programming has become extremely popular over the past few decades because it is adept at helping you to eliminate poorly written code from your application. However, anti-patterns might often emerge if you take shortcuts and your primary objective is getting the task done rather than getting it done the right way. Whereas a design pattern is a proven solution to a common design problem in software development, an anti-pattern is an ineffective pattern that shows us how not to solve a problem, because doing so would result in poor design. In other words, an anti-pattern is a common solution to a given problem that can have negative consequences — a solution that is ineffective or counterproductive in practice. A God object is one such anti-pattern. A God object contains cluttered code that is difficult to maintain, extend, use, test, and integrate with other parts of the application. God objects violate the single responsibility principle and the Law of Demeter. I will discuss the single responsibility principle below. The Law of Demeter, or principle of least knowledge, reduces dependencies between classes and helps build components that are loosely coupled. Here is a list of the characteristics of a God object: Too many functions in a class Knows too much about the application and can interact with all the data Difficult to maintain over time because changes to the class of the God object typically introduce problems or side effects Extremely complicated since it maintains the state of most of the application God objects and the single responsibility principle When multiple responsibilities are assigned to a single class, the class violates the single responsibility principle. Again, such classes are difficult to maintain, extend, test, and integrate with other parts of the application. The single responsibility principle states that a class (or subsystem, module, or function) should have one and only one reason for change. If there are two reasons for a class to change, the functionality should be split into two classes with each class handling one responsibility. Some of the benefits of the single responsibility principle include orthogonality, high cohesion, and low coupling. A system is orthogonal if changing a component changes the state of that component only, and doesn’t affect other components of the application. In other words, the term orthogonality means that operations change only one thing without affecting other parts of the code. Coupling is defined as the degree of interdependence that exists between software modules and how closely they are connected to each other. When this coupling is high, then the software modules are interdependent, i.e., they cannot function without each other. Cohesion denotes the level of intra-dependency among the elements of a software module. In other words, Cohesion is a measure of the degree to which the responsibilities of a single module or a component form a meaningful unit. God object example in C# The solution to the God object anti-pattern is to split related functionality into smaller and more manageable classes — i.e., to design classes that have strong cohesion and are loosely coupled with one another. Let’s dig into some code to understand the God object anti-pattern better. Let’s say you are building a recruitment management system. Create the following class in the Program.cs file. public class Candidate { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } public DateTime InterviewDate { get; set; } public int RecruiterId { get; set; } public string Recruiter { get; set; } public int InterviewerId { get; set; } public string Interviewer { get; set; } public string InterviewerRemarks { get; set; } public decimal CurrentSalary { get; set; } public decimal ExpectedSalary { get; set; } } The Candidate class shown here contains several properties that shouldn’t be part of it. In essence, this class is doing too many things, and thus violates the single responsibility principle. This is a good example of a God object. In other words, an instance of the Candidate class is a God object because it contains too much information. Refactor a God object in C# To fix a God object problem, you can follow these steps: Create unit tests. Create a comprehensive unit test suite before you start refactoring the classes. This would help you to know if any functionality in the application is broken due to a change. Group related methods and properties. Group the common methods and properties in a class while keeping the classes loosely coupled. Divide large classes and methods. If classes or methods have a lot of code or functionality in them, break them into simple, manageable, testable classes and methods. Remove the God object. Finally, delete or deprecate the God object. Referring back to our example above, what if there are multiple rounds of interviews and each of those rounds are taken by a different interviewer? The current version of the Candidate class wouldn’t be able to support this. Let’s now refactor this class to make it simple, testable, and more manageable. The following code snippet illustrates the new version of the Candidate class. Note that the new Candidate class contains information pertaining to the candidate only. public class Candidate { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } public decimal CurrentSalary { get; set; } public decimal ExpectedSalary { get; set; } } The Recruiter and Interviewer classes are given below. public class Recruiter { public int RecruiterId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } public class Interviewer { public int InterviewerId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } Finally, the Interview class contains the candidate, the recruiter, and other information related to the interview as shown below. public class Interview { public int Id { get; set; } public int CandidateId { get; set; } public int RecruiterId { get; set; } public DateTime InterviewDate { get; set; } public string InterviewerRemarks { get; set; } } And that’s it! If a candidate attends several rounds of interviews, then you might want to create multiple instances of the Interview class. That would suffice for storing candidate information for multiple interview rounds. God objects are common in poorly designed systems. The solution is to refactor either the source code or the design or both, and create types (classes, structs, and interfaces) that are highly cohesive and loosely coupled. Despite all of its pitfalls, a God object is a good design choice in micro-controllers because centralized control is of utmost importance rather than flexibility and ease of maintenance. How to do more in C#: How to use ValueTask in C# How to use immutability in C How to use const, readonly, and static in C# How to use data annotations in C# How to work with GUIDs in C# 8 When to use an abstract class vs. interface in C# How to work with AutoMapper in C# How to use lambda expressions in C# How to work with Action, Func, and Predicate delegates in C# How to work with delegates in C# How to implement a simple logger in C# How to work with attributes in C# How to work with log4net in C# How to implement the repository design pattern in C# How to work with reflection in C# How to work with filesystemwatcher in C# How to perform lazy initialization in C# How to work with MSMQ in C# How to work with extension methods in C# How to us lambda expressions in C# When to use the volatile keyword in C# How to use the yield keyword in C# How to implement polymorphism in C# How to build your own task scheduler in C# How to work with RabbitMQ in C# How to work with a tuple in C# Exploring virtual and abstract methods in C# How to use the Dapper ORM in C# How to use the flyweight design pattern in C# Related content feature What is Rust? Safe, fast, and easy software development Unlike most programming languages, Rust doesn't make you choose between speed, safety, and ease of use. Find out how Rust delivers better code with fewer compromises, and a few downsides to consider before learning Rust. By Serdar Yegulalp Nov 20, 2024 11 mins Rust Programming Languages Software Development how-to Kotlin for Java developers: Classes and coroutines Kotlin was designed to bring more flexibility and flow to programming in the JVM. Here's an in-depth look at how Kotlin makes working with classes and objects easier and introduces coroutines to modernize concurrency. By Matthew Tyson Nov 20, 2024 9 mins Java Kotlin Programming Languages analysis Azure AI Foundry tools for changes in AI applications Microsoft’s launch of Azure AI Foundry at Ignite 2024 signals a welcome shift from chatbots to agents and to using AI for business process automation. By Simon Bisson Nov 20, 2024 7 mins Microsoft Azure Generative AI Development Tools news Microsoft unveils imaging APIs for Windows Copilot Runtime Generative AI-backed APIs will allow developers to build image super resolution, image segmentation, object erase, and OCR capabilities into Windows applications. By Paul Krill Nov 19, 2024 2 mins Generative AI APIs Development Libraries and Frameworks Resources Videos