SOLID principles are an effective way to write good quality software. So it got me thinking, given the long list of principles isn't better if you could see them in your everyday work? In this take, I would like to take a tour around the SOLID principles and how these apply to the ASP.NET MVC framework. The goal is to have these ingrained into muscle memory so when you write code, it will be right where you need it.
S: Single Responsibility
An easy way to remember this principle is in the M
of the MVC framework. Models are exemplars of this principle because each one shows a single entity. The single responsibility follows from a single concern. With this, you get only one reason to change your specific model. What is radical is we are just dealing with a POCO, which is a plain old C# object. Each model encapsulates a concern through basic C# properties. For example, say I have a Post
model, each property in the class belongs to this abstraction. When you need change this code, it will only be because of conceptual changes in the Post
data model.
O: Open / Close
The request pipeline is open to extension and closed for modification. One great example are action filters. For example:
public class PostAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return true;
}
}
This builds on the framework's security mechanism and I extend it with custom code. What is nice is how we stay SOLID with minimal code. When I add [PostAuthorize]
to an action method, I leverage the security pipeline. This action filter executes at the right time when the request comes to the server. This extensible inheritance mechanism gets baked into the framework. In C#, this is a textbook example of the open / close principle using inheritance.
L: Liskov Substitution
To illustrate this principle, every controller must inherit from the Controller
class. The crux of this principle is that I can swap type T for any of its subtypes S. Classical inheritance conveys a is a
relationship. So, for example a Square is also a Rectangle. A Square inherits properties from a Rectangle because they both share width and height. This is a canonical example of code reuse. But, the principle goes deeper since it's talking about behavior not just relationships. A Square fails to act like a Rectangle because every width must also equal to its height. When I add a controller, its behavior gets tied to the parent class. I am free to assume that every child of a Controller
will behave exactly like its parent. The Liskov substitution principle comes into play when I no longer need to check for subtypes. The Controller
class is awesome because every child acts like any other controller. For example a PostController
is a controller and behaves like one.
I: Interface Segregation
My favorite display of this principle are custom binders. You can wire up your own binder by implementing this interface:
namespace System.Web.Mvc
{
public interface IModelBinder
{
object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext);
}
}
All you need to put in place binders gets encapsulated into this one interface. Interface segregation states no client gets forced to depend on methods it does not use. Here, a single method communicates all basic requirements for model binding. This contract, tells me I need to return an object
which is the data model. I take in ControllerContext
and ModelBindingContext
and turn this into a C# object. The beauty here is how well this abstracts away a somewhat complex process. Instead of turning simple problems into complex solutions, this principles suggests the opposite. Multi-level coupling leads fellow coders astray in getting the intent of the code. The framework decouples the complexity by offering this one simple level of abstraction.
D: Dependency Inversion
Dependency inversion gets shown with object composition on each controller:
public class MyController : Controller
{
private readonly IMyFirstDependency _myFirstDependency;
private readonly IMySecondDependency _mySecondDependency;
public MyController(
IMyFirstDependency myFirstDependency,
IMySecondDependency mySecondDependency)
{
_myFirstDependency = myFirstDependency;
_mySecondDependency = mySecondDependency;
}
}
This principle gets summed up as coding to abstractions, not implementation details. What is elegant here is we use both composition and proper inheritance. Object composition conveys a has a
relationship. For example, MyController
has a MyFirstDependency
. This design pattern suggests an interface that I use as a dependency. Then, I wire up the constructor with all dependencies through a dependency injection framework. MyController
only knows about the interfaces that abstract away implementation details. I have no choice but to code to abstractions. This lends itself to beautiful, well written and testable code.
Conclusion
These principles come in handy when delivering high quality software. I hope you can see they are not out of mortal reach. Any person with basic OOP skills can code to these principles. The trick is to ingrain them into memory. So the next time you feel like throwing extra layers, stop and think about staying SOLID.
The post The SOLID Principles in ASP.NET MVC appeared first on BeautifulCoder.NET.