Ever since I have been speaking and writing, I have talked about proper class design using Object Oriented Programming. OOP has been around since the 1950’s and, to me, is still the best way to properly design classes, for now and the future. Many of the projects I see fail are due to not using OOP or doing it wrong. I still see senior level developers not implementing OOP properly.
Now, I am seeing beginner developers using poor practices by others as a guide and they are learning OOP wrong. So, I want to write a series of articles on how to implement classes with proper OOP. For the first series, I’m going to focus on business classes or what most developers now call, model classes. These types of classes are widely used, especially in ASP.NET Model-View-Controller websites. There is more to OOP than just model classes, but I will tackle that in a different series of articles. So, check back often for a new article (usually every two weeks).
256 Seconds with dotNetDave – Episode 6
Poor Class Design
I have many examples of poor class design, but the worse one that I can remember is the one below that I use at conference sessions from a real, in production project. This is what it looks like.
- public class OrderData
- {
- public string ORDER;
- public string facilityID = "";
- public string OrderNumber = "";
- public string openClosed = "";
- public string transType = "";
- public string dateOpened = "";
- public string dateClosed = "";
- public string dateShop = "";
- public OrderData(string _ORDER)
- {
- this.ORDER = _ORDER;
- }
- //Remainder of code removed for brevity
- }
The real class had 198 public string fields to hold the data. They used no other types, which is just bad! Let’s go over the main issues.
- All the data for the class was held in those 198 public fields. This completely breaks encapsulation, the first pillar of any OOP class design (discussed below).
- All the fields are strings! The proper type for the data should always be used like DateTimeOffset, Integer etc.
- Poor coding standards. They standard even changes from field to field!
- No documentation.
Number 1 & 2 of course are the most important, but so are the other two.
Encapsulation – The 1st Pillar of OOP
There are three main pillars of OOP and they are encapsulation, inheritance and polymorphism. If you are new to OOP, and so we are all on the same page, this is the definition of it on Wikipedia, Object-oriented programming (OOP) is a programming paradigm based on the concept of "objects", which can contain data, in the form of fields (often known as attributes), and code, in the form of procedures (often known as methods). A feature of objects is an object's procedures that can access and often modify the data fields of the object with which they are associated (objects have a notion of "this" or "self"). In OOP, computer programs are designed by making them out of objects that interact with one another. OOP languages are diverse, but the most popular ones are class-based, meaning that objects are instances of classes, which also determine their types.
In this series of articles about model classes, I will mostly just focus on encapsulation. Wikipedia then defines encapsulation as this, Encapsulation is an object-oriented programming concept that binds together the data and functions that manipulate the data, and that keeps both safe from outside interference and misuse. Data encapsulation led to the important OOP concept of data hiding.
If a class does not allow calling code to access internal object data and permits access through methods only, this is a strong form of abstraction or information hiding known as encapsulation. Some languages (Java, for example) let classes enforce access restrictions explicitly, for example denoting internal data with the private keyword and designating methods intended for use by code outside the class with the public keyword. Encapsulation prevents external code from being concerned with the internal workings of an object. This facilitates code refactoring, for example allowing the author of the class to change how objects of that class represent their data internally without changing any external. It also encourages programmers to put all the code that is concerned with a certain set of data in the same class, which organizes it for easy comprehension by other programmers. Encapsulation is a technique that encourages decoupling.
Since encapsulation is the first pillar of OOP, if that isn’t done correctly, then to me, good OOP design is not being practiced. And I always say if you aren’t practicing good OOP design, you will build a house of cards, and they all eventually fall.
So, if we get rid of the fields, and replace them with properties or auto-properties like this:
- public string FacilityID { get; set; }
Is this good OOP design? The short answer is no.
Data Hiding
By practicing good OOP design, it’s your job as the developer to properly implement encapsulation, which is all about data hiding. The only way that code outside of the class or type can get or set the data is to go through methods and properties. For the rest of the articles, I will be using an example type called Person (as seen below) and you will see how it changes as we go along.
- public class Person
- {
- public DateTime BornOn { get; set; }
- public string Address1 { get; set; }
- public string Address2 { get; set; }
- public string CellPhone { get; set; }
- public string City { get; set; }
- public string Country { get; set; }
- public string Email { get; set; }
- public string FirstName { get; set; }
- public string HomePhone { get; set; }
- public string Id { get; set; }
- public string LastName { get; set; }
- public string PostalCode { get; set; }
- }
This example class closely follows the way that I see how over 90% of model classes being designed. But this still is not proper OOP design since there isn’t any data validation. The whole purpose of encapsulation is not only hiding the data but making sure it’s correct in the first place! Never allow bad data into the class, ever! I always say, “Bad data in, bad data out!”. If that bad data gets into the database, then it’s very difficult and very costly to fix.
Unless your model classes will take any length of string, or incorrect date, incorrect number values etc. (and I’m sure they shouldn’t do this), then you need to validate it! Never rely on the database to validate your data since that would be a big performance issue. Also, new document-based databases like Cosmos DB do not have typed columns, so there isn’t really a way to do it unless you want to write a lot of back-end scripts.
Validating the Data
Unless your code really does not care about the value of the data (for example I won’t do this for Boolean values), then the first lines of any property or method must validate the data. Here is how I would write the Address1 property in Person.
- public string Address1
- {
- get
- {
- return _address1;
- }
- set
- {
- // 1. Validate that the current & new value aren't the same.
- if (_address1 == value)
- {
- return;
- }
- // 2. Validate that value isn't null
- if (string.IsNullOrEmpty(value))
- {
- throw new ArgumentNullException(nameof(Address1),
- "Value for Address1 cannot be null or empty.");
- }
- // 3. Validate that the value is within range
- if (value.Length < 10 || value.Length > 256)
- {
- throw new ArgumentOutOfRangeException(nameof(Address1),
- "Address must be between 10 - 256 characters.");
- }
- _address1 = value;
- }
- }
Let’s go through the validation and why it’s important.
- The first validation is to make sure that the new value isn’t the same as the current value. Why set the same value twice? This can be a performance issue. Also, many model classes used in apps, also throw change events when data is modified, so this is very important for those types of classes.
- Validate that we have a string! As you should know, a null value in a string can crash the application if code in the class calls a String method like Length. This is to make sure the string is not null or is empty.
- Last but not lease is the length of the string itself. Most databases set the string length size, so you need to mimic this in the code before it gets to the database. This is even important for databases like Cosmos DB since each document has a size limit. Sending any length of string could cause an error.
Of course, your validation for #3 will be different based on business rules. If you follow what I recently wrote in my Reuse, Reuse and More Code Reuse! article, these model classes should always but put in an reusable assembly (away from the database context) so they can be used by any layer of your application. That way any code that uses it will have the exact same validation… no code duplication!
The Final Person Class
There is more to talk about, but I want to wrap up this first article. Below is the final Person class with proper naming standards and documentation (both are a must in any OOP design).

Mounnad MohamedPosted Sep 12, 2019, 10:40 AM
Thank you sir for this article, But just one thing when I analyze code with StyleCop, it gives me some useless avertissements like "using directives must be placed inside of the namespace".
Javier MontufarPosted Aug 5, 2019, 4:37 PM
Thank you for share, if you can make the articles once a week, it will be great to implement all the topics. Nice work.
Pankajkumar PatelPosted Aug 5, 2019, 1:28 AM
Nice one article