What’s this principle about? Descendant classes shouldn’t conflict with basic classes.

Let’s think about this principle at example. We have basic class Document, that has pdf export function and changing text color function. But sometimes, we use document only for reading. You can export such document to pdf, but you can’t change text color.

    public class Document
    {
        public void ExportToPdf()
        {
            //...
        }
        public virtual void ChangeTextColor(string nameOfNewColor)
        {
            //...
        }
    }

    public class ReadOnlyDocumnent:Document
    {
        public override void ChangeTextColor(string nameOfNewColor)
        {
            throw new NotImplementedException();
        }

    }

Using such approach, we violate Liskov substitution principle. When we will use ReadOnlyDocument as basic Document, while call ChangeTextColor function, we receive exception. It’s not good idea.

How change this situation? Let’s try using interfaces.

    public class BaseDocument
    {
        public void ExportToPdf()
        {
            //...
        }
    }
    public interface IWtitableDocument
    {
        public void ChangeTextColor(string nameOfNewColor);
    }
    public class WritableDocument : BaseDocument, IWtitableDocument
    {
        public void ChangeTextColor(string nameOfNewColor)
        {
            //...
        }
    }
    public class ReadOnlyDocumnent : BaseDocument
    {
        //...
    }

Now we have class BaseDocument, that can export data to PDF. Also we have interface IWritableDocument that contains ChangeTextColor method. You can think, that new code became more complicated. But when we speak about big and scalable application, careful code with SOLID principles is easier to maintain.