Skip to main content

Practice Problems for Liskov Substitution Principle

Liskov Substitution Principle (LSP)

Practice Problems

To master the Liskov Substitution Principle, it's essential to engage in practical exercises and projects. Here are some complete coding exercises, sample projects, quizzes, and multiple-choice questions (MCQs) to help reinforce your understanding.

Code Refactoring Exercise

Given:

class Bird {
public void fly() {
System.out.println("Flying");
}
}

class Ostrich extends Bird {
@Override
public void fly() {
throw new UnsupportedOperationException("Ostriches can't fly");
}
}

public class Main {
public static void main(String[] args) {
Bird bird = new Ostrich();
bird.fly(); // Throws UnsupportedOperationException
}
}

Challenge: Refactor the code to adhere to LSP.

Refactored:

abstract class Bird {
public abstract void move();
}

class FlyingBird extends Bird {
@Override
public void move() {
fly();
}

public void fly() {
System.out.println("Flying");
}
}

class NonFlyingBird extends Bird {
@Override
public void move() {
walk();
}

public void walk() {
System.out.println("Walking");
}
}

class Sparrow extends FlyingBird {
// Sparrow inherits fly method and move behavior
}

class Ostrich extends NonFlyingBird {
// Ostrich inherits walk method and move behavior
}

public class Main {
public static void main(String[] args) {
Bird sparrow = new Sparrow();
Bird ostrich = new Ostrich();

sparrow.move(); // Outputs "Flying"
ostrich.move(); // Outputs "Walking"
}
}

Design Challenge

Challenge: Design a class hierarchy for a library management system that adheres to LSP. The system should have base classes for Item, with subclasses like Book, Magazine, and DVD. Each subclass should implement behaviors appropriate to the type of item.

Coding Exercises

  • Exercise 1: Implement an Animal Hierarchy:
    • Description: Create a base class Animal with a method makeSound(). Subclasses Dog, Cat, and Bird should implement the makeSound method. Ensure that replacing an Animal with any subclass does not change the expected behavior.
    • Example:
abstract class Animal {
public abstract void makeSound();
}

class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Bark");
}
}

class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow");
}
}

class Bird extends Animal {
@Override
public void makeSound() {
System.out.println("Chirp");
}
}

public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
Animal cat = new Cat();
Animal bird = new Bird();

dog.makeSound(); // Outputs "Bark"
cat.makeSound(); // Outputs "Meow"
bird.makeSound(); // Outputs "Chirp"
}
}

Sample Projects

  • Project 1: E-commerce Payment System:
    • Description: Design a payment processing system with a base class PaymentMethod and subclasses CreditCardPayment, PayPalPayment, and BitcoinPayment. Each subclass should implement a method processPayment that adheres to LSP.
    • Example:
abstract class PaymentMethod {
public abstract void processPayment(double amount);
}

class CreditCardPayment extends PaymentMethod {
@Override
public void processPayment(double amount) {
System.out.println("Processing credit card payment of " + amount);
}
}

class PayPalPayment extends PaymentMethod {
@Override
public void processPayment(double amount) {
System.out.println("Processing PayPal payment of " + amount);
}
}

class BitcoinPayment extends PaymentMethod {
@Override
public void processPayment(double amount) {
System.out.println("Processing Bitcoin payment of " + amount);
}
}

public class Main {
public static void main(String[] args) {
PaymentMethod payment = new CreditCardPayment();
payment.processPayment(100.0);

payment = new PayPalPayment();
payment.processPayment(200.0);

payment = new BitcoinPayment();
payment.processPayment(300.0);
}
}
  • Project 2: Library Management System:
    • Description: Develop a library management system where the base class Item is extended by subclasses Book, Magazine, and DVD. Each subclass should implement methods specific to their type while adhering to LSP.
    • Example:
abstract class Item {
public abstract void checkOut();
public abstract void returnItem();
}

class Book extends Item {
@Override
public void checkOut() {
System.out.println("Checking out a book");
}

@Override
public void returnItem() {
System.out.println("Returning a book");
}
}

class Magazine extends Item {
@Override
public void checkOut() {
System.out.println("Checking out a magazine");
}

@Override
public void returnItem() {
System.out.println("Returning a magazine");
}
}

class DVD extends Item {
@Override
public void checkOut() {
System.out.println("Checking out a DVD");
}

@Override
public void returnItem() {
System.out.println("Returning a DVD");
}
}

public class Main {
public static void main(String[] args) {
Item item = new Book();
item.checkOut();
item.returnItem();

item = new Magazine();
item.checkOut();
item.returnItem();

item = new DVD();
item.checkOut();
item.returnItem();
}
}

Quizzes

  • Quiz 1: Identifying LSP Violations:
    • Question: Given the following class hierarchy, identify the LSP violation and suggest a way to fix it.
class Vehicle {
public void startEngine() {
System.out.println("Starting engine");
}
}

class Bicycle extends Vehicle {
@Override
public void startEngine() {
throw new UnsupportedOperationException("Bicycles don't have engines");
}
}

public class Main {
public static void main(String[] args) {
Vehicle vehicle = new Bicycle();
vehicle.startEngine(); // Throws UnsupportedOperationException
}
}
  • Answer:
abstract class Vehicle {
public abstract void move();
}

class MotorVehicle extends Vehicle {
@Override
public void move() {
startEngine();
}

public void startEngine() {
System.out.println("Starting engine");
}
}

class Bicycle extends Vehicle {
@Override
public void move() {
pedal();
}

public void pedal() {
System.out.println("Pedaling");
}
}

public class Main {
public static void main(String[] args) {
Vehicle vehicle = new MotorVehicle();
vehicle.move(); // Outputs "Starting engine"

vehicle = new Bicycle();
vehicle.move(); // Outputs "Pedaling"
}
}
  • Quiz 2: LSP and OCP:
    • Question: How does adhering to LSP support the Open/Closed Principle (OCP)? Provide examples.
    • Answer:
      • Explanation: Adhering to LSP supports OCP by ensuring that new subclasses can be added without modifying existing code. This extensibility is a core aspect of OCP, which states that software entities should be open for extension but closed for modification.
      • Example: In a payment processing system, adding a new BitcoinPayment class should not require changes to the existing PaymentProcessor class if LSP is followed. The new class can be used interchangeably with existing payment methods, supporting OCP.

Multiple-Choice Questions (MCQs)

  • MCQ 1: What does the Liskov Substitution Principle state?

    • A) Subclasses should only inherit methods from their base classes
    • B) Objects of a superclass should be replaceable with objects of a subclass without altering the program's correctness
    • C) Classes should be open for extension but closed for modification
    • D) No client should be forced to depend on methods it does not use

    Answer: B) Objects of a superclass should be replaceable with objects of a subclass without altering the program's correctness

  • MCQ 2: Which of the following is a violation of LSP?

    • A) A subclass that adds new methods to the base class
    • B) A subclass that overrides a base class method to throw an exception not thrown by the base class
    • C) A subclass that implements an interface
    • D) A subclass that extends the functionality of the base class

    Answer: B) A subclass that overrides a base class method to throw an exception not thrown by the base class

  • MCQ 3: How can unit testing help verify adherence to LSP?

    • A) By testing only the base class methods
    • B) By ensuring that subclass methods are never called
    • C) By testing both base class and subclass methods to ensure consistent behavior
    • D) By avoiding tests that involve inheritance

    Answer: C) By testing both base class and subclass methods to ensure consistent behavior

By working through these extensive practice problems, quizzes, and MCQs, you'll gain hands-on experience with the Liskov Substitution Principle, helping you to apply it effectively in your projects. These exercises will deepen your understanding and improve your ability to create maintainable, scalable, and reliable code.