2

I'm not sure if I am getting this right. In order to observe proper SOLID principles, am I forbidden to inherit from concrete classes? Does that mean that every concrete class that I have more or less be sealed (or at least considered to be sealed)?

This is confusing for me because I encountered this code from our repository:

class FontList : ObservableCollection<string> 
{ 
    public FontList() 
    {
        foreach (FontFamily f in Fonts.SystemFontFamilies)
        {                
            this.Add(f.ToString());                
        }  
    }   
}

Which is inheriting from ObservableCollection<string>, a concrete class (correct?). However looking at ObservableCollection:

[Serializable]
public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged 

It is inheriting from Collection<T>, which is also a concrete class. Can anyone explain the correct interpretation of DIP, especially with regard to concrete class inheritance?

2

Application of DIP should be selective. That means you pick which dependencies you want inverted. Applying DIP increases complexity. So you should employ your software design expertise to choose where this increase in complexity pays off and where not.

Also, the book from these rules come from itself says those are not hard rules and they can be violated :

A somewhat more naïve, yet still powerful interpretation of DIP is the simple heuristic :

(list of rules cited on Wikipedia follows)

... Moreover, there seems no reason to follow this heuristic for classes, that are concrete but nonvolatile. if concrete class is not going to change very much, and no other similar derivatives are going to be created, it does very little harm to depend on it.

In your case both ObservableCollection and Collection are highly nonvolatile. They are part of the .NET framework and it is expected they will not change in a way that will require their user to change.

One more thing is that the class you show is not good, not because it would violated DIP, but because it is stupid to create new class just to create pre-filled collection. Instead, just making a factory method should be enough.

public static class FontList
{ 
    public static ObservableCollection<string> Create() 
    {
        ObservableCollection<string> fonts = new ObservableCollection<string>();
        foreach (FontFamily f in Fonts.SystemFontFamilies)
        {                
            fonts.Add(f.ToString());                
        }
        return fonts;
    }   
}
0

You are not forbidden to inherit from concrete classes, and certainly shouldn't make all classes sealed, doing so would likely lead to violations of the Open-Close Principle (OCP) down the road when you realize you need to inherit from a class.

As long as your concrete inheritance hierarchy does not violate the Liskov Substitution Principle (LSP), and that all references to this hierarchy are dependent on the top level (hopefully abstract) base class, you are probably in good shape.

3
  • That's a good point with OCP. Makes it very confusing though with @Euphoric 's interesting answer, since it sort of says that more volatile classes should employ the DIP. – Tyress Feb 2 '16 at 10:30
  • 3
    "[you] certainly shouldn't make all classes sealed, doing so would likely lead to violations of the OCP". By this logic, making classes internal is also a bad idea as they may later need modifying to public. Changing a class from sealed to unsealed is not a violation of the OCP and is so marking all classes as sealed by default is good practice. – David Arno Feb 2 '16 at 11:01
  • I agree with @davidarno. You should never make a class non-final/sealed until you've thought about which of its methods should be overridable and which shouldn't. Failure to do so results in classes that can't be updated without risk of breaking its subclasses. – Doval Apr 2 '16 at 16:08
0

In Dependency Inversion Principle you should seperate interface (for consumer) from concrete implementation.

So your implementation of FontList : ObservableCollection<string> is perfectly ok. (it is just an implementation detail)

On the other side the consumer of your class should only need to know (or depend on) an interface (i.e. ICollection<string>).

  • following DiP DoSomethingWithFontList(ICollection<string> fonts)
  • not following DiP DoSomethingWithFontList(FontList fonts)
  • unnecessary dependency DoSomethingWithFontList(ObservableCollection<string> fonts)

For example NHibernate replaces ICollection<string> with some special collection that loads the collection as soon as some code calls a methods in the collection. Your consuming program does not need to know that there is a special collection implementation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.