Software development and a no. 10 socket
My focus is on architecture, design, detail and adding value. My hobbies are DIY car maintenance and simulations. This is my personal project, ownership and for-contract website.

When your base class serves as a common implementation (as opposed to a common data model) you might sometimes want to force an implementation or behavior, but also allow the behavior to be extended without it being modified or omitted completely. What do I mean by this?

Consider when you have a User domain or view model. Some users are players, and other users are spectators. They share a common set of validations and CRUD operations, but also have unique elements that set them apart. So, you write a base class:


public class User
{
	public string Email { get; set; }

	public virtual void Validate()
	{ 
		if (string.IsNullOrEmpty(Email))
		{
			ModelState.AddError("Email", "Email cannot be null");
		}
	}
}

Anything that extends this will have the Validate() method available, but nothing stops the implementer of the Player class to omit the base implementation:


public class Player : User 
{
	public int Health { get; set; }

	public override void Validate()
	{
		//this is omitted, and now Email is not validated anymore
		//base.Validate();

		if (Health < 0)
		{
			Health = 0;
		}
	}
}

This violates the "O" of SOLID, that is, open to extension but closed to modification. The base implementation is effectively modified by the consumer of your base class. In most cases this is an oversight, and one of the leading causes of errors in the code. Static code analysis, and of course unit tests, will help in finding and pointing out such mistakes.

A better design is to not include required functionality in the virtual method. To do this we introduce a protected internal validation method which we make the virtual implementation instead:


public class User
{
	public string Email { get; set; }

	public void Validate()
	{ 
		if (string.IsNullOrEmpty(Email))	
		{
			ModelState.AddError("Email", "Email cannot be null");
		}

		Validate_Internal();
	}


	protected virtual void Validate_Internal() { }
}

Now, the user of your base class can provide any custom validation by overriding Validate_Internal(), yet the required base validation is unaffected and unmodified:


public class Player : User 
{
	public int Health { get; set; }
	
	protected override void Validate_Internal()
	{
		if (Health < 0)
		{		
			Health = 0;
		}
	}
}

And of course, the accessibility (or public signature) of the User class remains unaffected; there is still only the Validate() method available.

This pattern is officially called the template pattern or the nanny pattern. I typically would just refer to it as the internal implementation of Validate. Not to be confused with the `internal` keyword. It stems from the fact that I post-fix _Internal to the method name, but you can choose any name you want here. For example, you could follow the old Microsoft standard for the Windows API and call it ValidateEx, or the event naming standard as used in the WinForms classes and name it OnValidated (specifically past tense because the method is called at the end) or OnValidating (if the method was called first). It doesn’t really matter, as long as you isolate and protect your required base implementation from the user of your class.