Naming Classes Effectively
A class is a cohesive collection of data, functionality, or both. Class names reveal the purpose of a collection without a dive into the implementation.
Specific, descriptive nouns. Just like with variables, no abbreviations.
Data classes are often the easiest to name. Use the noun for the record you're trying to keep. You can optionally use a "Model" suffix to explicitly declare that your record only stores information.
OrderModel License BicycleMaintenanceHistory ShipmentLine Sandwich
Functionality classes contain no fields or properties. They are pure and stateless. Often developers call them "Helper" or "Utility" classes.
When you create a class which only exposes functionality, think of it as a personality interacting with your app. Give it domain, then responsibility.
For instance, if your class builds http query strings, use QueryString for the domain and Builder for the responsibility. Together, "QueryStringBuilder". If you have a class that creates receipts, call it "ReceiptCreator".
Here are more examples:
TapeRewinder HarddriveEraser FileCreator ShipmentUpdator OrderValidator
"Both" classes (data and functionality together)
There are some concepts in a domain which need to have methods and properties working alongside each other. As an example, Entity Framework is a common C# library for connecting to databases. It exposes a property for the database connection string and a function to execute a SQL query against the database. In cases where you need to expose state and functionality to the caller, use a data-style naming convention (with no Model suffix).
Here are some examples:
// Exposes connection string property and Query() so that anyone with a reference to the database can query it. Database // Exposes both port number property and Dock() for any ships that want to dock there. ShippingPort
There are other times when classes should store state when they are constructed, but should not expose that state to the outside world. In these cases, your code is more like a person being dropped into a context. Use functionality-style naming for these.
// Needs a reference to a database to check how much inventory is left for a certain item. // The database connection should not be exposed to the outside world. InventoryAuditor
Creating a record class and then creating a functionality class to expose CRUD operations for the record class is a common anti-pattern. These sorts of classes are bad for two reasons:
- EntityManagers revolve around the records you're modeling instead of your user flow. How the user thinks of your system should be the basis on how you model the system.
- "Manager" is a wide responsibility. You're saying, "everything that happens in this domain has to go through you."
When you see an EntityManager, find out whether is modeled after user flow or just extending a data class. If it's extending a data class, figure out what user flows it's part of and refactor the functionality into those domains and responsibilities. Use a responsibility more specific than manager.
Using namespaces to your advantage
Languages often offer ways to create sub namespaces within your domain. C# encourages the namespace convention:
This allows us to create smaller class names. A developer may be tempted to create a class name such as "PriceListLineMassUploadValidator" but this is a really long name. Instead, one could identify that the feature is MassUpload, the subnamespace is PriceListLine and the class is Validator.
When you use namespaces to your advantage, you can sometimes get away with an implicit domain like in the example above.
Long class name smell
Even with good namespacing techniques, we will often end up with class names that are too long or extremely complicated. This is good. Our class names should be a reflection on the cohesion of our class.
Long or complicated class names reveal to us a poor concept of what the class is supposed to be. Our class names should be the first thing that screams "refactor me!" when we are examining our code base.
- Data: DataModel
- Functionality; DomainResponsibility
- Functionality and private state: DomainResponsibility
- Functionality and public state: Data
Where to go from here
Classes are by far the hardest concept to create naming rules around. It's very possible I've missed something or misrepresented something. Let me know if you see a way this article could be improved.
Respond to this post and join the conversation on DEV