Liskov Substitution Principle (LSP)

The Liskov Substitution Principle (LSP) states that if a program is using a base class, it should be able to use any of its subclasses without the program knowing it. In other words, the subclasses should be substitutable for their base class.

Bad Example:

A `Bird` class has a method `Fly`. The `Penguin` class inherits from the `Bird` class but throws an exception in the `Fly` method because penguins cannot fly. This violates LSP because we cannot use `Penguin` wherever we use `Bird`.

                    
                    class Bird {
                        public virtual void Fly() {
                            // Default implementation.
                        }
                    }

                    class Penguin : Bird {
                        public override void Fly() {
                            throw new NotSupportedException("Penguins can't fly.");
                        }
                    }
                    
                    

Corrected Example:

A better design would be to have a separate `FlyingBird` class that inherits from `Bird`. Now, the `Penguin` class can inherit from `Bird` without violating the LSP.

                    
                    class Bird {
                        // Other bird-related methods.
                    }

                    class FlyingBird : Bird {
                        public virtual void Fly() {
                            // Default implementation.
                        }
                    }

                    class Penguin : Bird {
                        // Penguin-specific methods.
                    }