What is more important: following the SOLID principles, or having a simpler interface?
Interface vis-a-vis SOLID
These are not mutually exclusive. The interface should express the nature of your business model ideally in business model terms. The SOLID principles is a Koan for maximizing Object Oriented code maintainability (and I mean "maintainability" in the broadest sense). The former supports the use and manipulation of your business model and the latter optimizes code maintenance.
Open/Closed Principle
"don't touch that!" is too simplistic an interpretation. And assuming we mean "the class" is arbitrary, and not necessarily right. Rather, OCP means that you have designed your code such that modifying it's behavior does not (should not) require you to directly modify existing, working code. Further, not touching the code in the first place is the ideal way to preserve the integrity of existing interfaces; this is a significant corollary of OCP in my view.
Finally I see OCP as an indicator of existing design quality. If I find myself cracking open classes (or methods) too often, and/or without really solid (ha, ha) reasons for doing so then this may be telling me I've got some bad design (and/or I don't know how to code OO).
Give it your best shot, we have a team of doctors standing by
If your requirements analysis tells you that you need to express the Product-Review relationship from both perspectives, then do so.
Therefore, Wolfgang, you may have a good reason for modifying those existing classes. Given the new requirements, if a Review is now a fundamental part of a Product, if every extension of Product needs Review, if doing so makes client code appropriately expressive, then integrate it into the Product.
It's a lot more simple than that quote makes it sound, accurate as it is.
When you look at an inheritance hierarchy, imagine a method which receives an object of the base class. Now ask yourself, are there any assumptions that someone editing this method might make which would be invalid for that class.
For example originally seen on Uncle Bob's site (broken link removed):
public class Square : Rectangle
{
public Square(double width) : base(width, width)
{
}
public override double Width
{
set
{
base.Width = value;
base.Height = value;
}
get
{
return base.Width;
}
}
public override double Height
{
set
{
base.Width = value;
base.Height = value;
}
get
{
return base.Height;
}
}
}
Seems fair enough, right? I've created a specialist kind of Rectangle called Square, which maintains that Width must equal Height at all times. A square is a rectangle, so it fits with OO principles, doesn't it?
But wait, what if someone now writes this method:
public void Enlarge(Rectangle rect, double factor)
{
rect.Width *= factor;
rect.Height *= factor;
}
Not cool. But there's no reason that the author of this method should have known there could be a potential problem.
Every time you derive one class from another, think about the base class and what people might assume about it (such as "it has a Width and a Height and they would both be independent"). Then think "do those assumptions remain valid in my subclass?" If not, rethink your design.
Best Answer
LSP applies to passing an instance of a class into a method, having the method do some stuff with that instance, and often produce some sort of result. This doesn't matter for static classes since in C# you cannot create an instance of a static class.
Even more importantly, static classes are sealed and therefore cannot be inherited. This makes your question moot far as C# goes.
You could say that static classes are always LSP-compliant since you cannot ever produce a subclass that would violate that principle. You could also say that static classes are never LSP-compliant for the same reason.
In Java, static classes are slightly different. You cannot mark a top-level class as "static", so if you want to create a utility class similar to C#'s static classes you have to declare it as
final
and hide its constructor. Once you do that, though, they behave similarly to C# - you cannot instantiate them or subclass them. You can declare an inner class asstatic
, but that does not mean the same thing as it does in C#: it simply denotes a nested top-level class.VB.NET behaves exactly the same way as C# in this case, far as I know.
You didn't mention whether you're interested in the other principles, but I'm going to include them anyway for completeness.
Single responsibility principle: a static class easily follow this principle.
Open/closed principle: since static classes are sealed, they cannot ever follow this principle.
Liskov substitution principle: as above.
Interface segregation principle: doesn't apply to a single class, but splitting one large static class into smaller, more specialized ones could be a step towards following this principle.
Dependency inversion principle: static classes cannot implement interfaces, so any class using it will always depend on whatever implementation exists at the time. Static classes therefore violate this principle.
Since static classes do not satisfy all 5 criteria, they are not SOLID.