Jump into concepts and practice - no test required
or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
▶
Steps
setup
Define Abstract Class Shape
We start by defining an abstract class named 'Vehicle' with protected fields and an abstract method 'move'. This sets the base for shared behavior.
💡 Defining an abstract class establishes a common template with some implemented details and some abstract methods to be implemented by subclasses.
Line:abstract class Vehicle {
protected int speed;
public abstract void move();
}
💡 Abstract classes can have fields and abstract methods, allowing partial implementation.
setup
Define Interface Shape
Next, we define an interface named 'Movable' with a single method 'move'. Interfaces declare behavior without implementation or fields.
💡 Interfaces specify a contract that implementing classes must fulfill, focusing purely on method signatures.
Line:interface Movable {
void move();
}
💡 Interfaces cannot have fields and only declare methods to be implemented.
fill_row
Create Concrete Class Car Extending Abstract Class
We define a concrete class 'Car' that extends the abstract class 'Vehicle' and implements the abstract method 'move'.
💡 Concrete classes provide implementations for abstract methods and inherit fields from abstract classes.
Line:class Car extends Vehicle {
public void move() {
// implementation
}
}
💡 Abstract classes allow sharing code and state while requiring subclasses to implement abstract methods.
fill_row
Create Concrete Class Bike Implementing Interface
We define a concrete class 'Bike' that implements the 'Movable' interface and provides the 'move' method implementation.
💡 Implementing an interface requires providing concrete implementations for all declared methods.
Line:class Bike implements Movable {
public void move() {
// implementation
}
}
💡 Interfaces enforce a contract without providing state or implementation.
compare
Highlight Abstract Class Advantages
We highlight that abstract classes can have fields and implemented methods, allowing code reuse and shared state.
💡 Understanding what abstract classes provide beyond interfaces clarifies when to use them.
Line:// Abstract class can have fields and implemented methods
💡 Abstract classes support partial implementation and state sharing.
compare
Highlight Interface Advantages
We highlight that interfaces allow multiple inheritance and define a strict contract without state.
💡 Interfaces enable flexible design by allowing classes to implement multiple behaviors.
Line:// Interface supports multiple inheritance and no state
💡 Interfaces provide a pure abstraction layer for behavior specification.
traverse
Show Concrete Class Using Abstract Class State
We illustrate that 'Car' inherits the 'speed' field from 'Vehicle' and can use it in its methods.
💡 This shows how abstract classes can share state with subclasses, reducing duplication.
Line:class Car extends Vehicle {
public void move() {
speed += 10;
}
}
💡 Abstract classes enable sharing fields and behavior with subclasses.
traverse
Show Concrete Class Implementing Interface Method
We show 'Bike' implements the 'move' method from 'Movable' interface without any inherited state.
💡 This highlights that interfaces only require method implementation, no shared fields.
Line:class Bike implements Movable {
public void move() {
// implementation without inherited fields
}
}
💡 Interfaces enforce behavior contracts without state inheritance.
decision
Decision: Use Abstract Class When Sharing State
We decide that abstract classes are preferred when subclasses share common state or behavior that can be partially implemented.
💡 This decision clarifies when to choose abstract classes over interfaces.
Line:// Use abstract class if shared state or partial implementation needed
💡 Abstract classes enable code reuse and state sharing among related classes.
decision
Decision: Use Interface for Multiple Behavior Contracts
We decide that interfaces are preferred when multiple unrelated classes need to share behavior contracts without sharing state.
💡 This decision clarifies when interfaces provide design flexibility through multiple inheritance.
Line:// Use interface for multiple inheritance and pure abstraction
💡 Interfaces enable flexible design by allowing classes to implement multiple behaviors.
reconstruct
Summarize When to Use Abstract Class vs Interface
We summarize that abstract classes are best for shared code and state, while interfaces are best for defining multiple behavior contracts without state.
💡 This final step consolidates the key differences and usage guidelines.
Line:// Abstract class: shared state and partial implementation
// Interface: multiple inheritance and pure abstraction
💡 Clear criteria help choose the right abstraction mechanism in OOP design.
from abc import ABC, abstractmethod
# STEP 1
class Vehicle(ABC):
def __init__(self):
self._speed = 0 # protected field
@abstractmethod
def move(self):
pass
# STEP 2
class Movable(ABC):
@abstractmethod
def move(self):
pass
# STEP 3
class Car(Vehicle):
def move(self):
self._speed += 10 # implementation
# STEP 4
class Bike(Movable):
def move(self):
# implementation without shared state
pass
# STEP 7
car = Car()
car.move() # uses inherited speed
# STEP 8
bike = Bike()
bike.move() # implements interface method
📊
Abstraction - Abstract Class vs Interface - When to Use Which - Watch the Algorithm Execute, Step by Step
Watching this visualization helps you grasp the conceptual differences and usage scenarios of abstract classes and interfaces by seeing their structure and relationships evolve incrementally.
Step 1/11
·Active fill★Answer cell
Demonstrates abstraction by defining an abstract class with fields and abstract methods.
«abstract»Vehicle
#speed: int
+move()
Defines an interface to specify behavior without implementation.
«abstract»Vehicle
#speed: int
+move()
«interface»Movable
+move()
Shows concrete class inheriting from abstract class and implementing abstract methods.
«abstract»Vehicle
#speed: int
+move()
«interface»Movable
+move()
Car
+move()
Car ▷ Vehicle (1:1)
Shows concrete class implementing an interface and fulfilling its contract.
«abstract»Vehicle
#speed: int
+move()
«interface»Movable
+move()
Car
+move()
Bike
+move()
Car ▷ Vehicle (1:1)Bike → Movable (1:1)
Emphasizes abstract class capabilities for code reuse and state.
«abstract»Vehicle
#speed: int
+move()
«interface»Movable
+move()
Car
+move()
Bike
+move()
Car ▷ Vehicle (1:1)Bike → Movable (1:1)
Emphasizes interface role in multiple inheritance and pure abstraction.
«abstract»Vehicle
#speed: int
+move()
«interface»Movable
+move()
Car
+move()
Bike
+move()
Car ▷ Vehicle (1:1)Bike → Movable (1:1)
Demonstrates inherited fields usage in concrete subclass.
«abstract»Vehicle
#speed: int
+move()
«interface»Movable
+move()
Car
#speed: int
+move()
Bike
+move()
Car ▷ Vehicle (1:1)Bike → Movable (1:1)
Shows interface implementation without inherited fields.
«abstract»Vehicle
#speed: int
+move()
«interface»Movable
+move()
Car
#speed: int
+move()
Bike
+move()
Car ▷ Vehicle (1:1)Bike → Movable (1:1)
Decision step emphasizing abstract class use for shared state.
«abstract»Vehicle
#speed: int
+move()
«interface»Movable
+move()
Car
#speed: int
+move()
Bike
+move()
Car ▷ Vehicle (1:1)Bike → Movable (1:1)
Decision step emphasizing interface use for multiple behavior contracts.
«abstract»Vehicle
#speed: int
+move()
«interface»Movable
+move()
Car
#speed: int
+move()
Bike
+move()
Car ▷ Vehicle (1:1)Bike → Movable (1:1)
Final summary of abstraction usage guidelines.
«abstract»Vehicle
#speed: int
+move()
«interface»Movable
+move()
Car
#speed: int
+move()
Bike
+move()
Car ▷ Vehicle (1:1)Bike → Movable (1:1)
Key Takeaways
✓ Abstract classes allow sharing code and state among related classes while enforcing some methods to be implemented.
This is hard to see from code alone because the combination of implemented and abstract members is subtle without visualization.
✓ Interfaces define pure behavior contracts without state, enabling multiple inheritance and flexible design.
Visualizing the lack of fields and the implementation relationship clarifies interface purpose better than reading code.
✓ Choosing between abstract class and interface depends on whether shared state or multiple behavior contracts are needed.
The decision steps explicitly show criteria that are often implicit or scattered in textual explanations.
Practice
(1/5)
1. Trace the sequence of events when a client calls a method on a subclass instance that violates the Liskov Substitution Principle by strengthening a postcondition. What happens step-by-step?
easy
A. The client receives a result that meets the superclass contract, so no issues arise.
B. The subclass method returns a stricter result than expected, potentially causing client failures.
C. The client silently ignores the stricter postcondition, so behavior is unaffected.
D. The subclass method throws an exception due to the strengthened postcondition.
Solution
Step 1: Recall LSP postcondition rule
Subclasses must not strengthen postconditions; they can only maintain or weaken them.
Step 2: Trace client call
The client expects results conforming to the superclass contract. If subclass returns stricter results, some clients expecting broader results may fail.
Step 3: Analyze options
The subclass method returns a stricter result than expected, potentially causing client failures. correctly identifies potential client failures due to stricter postconditions. The client receives a result that meets the superclass contract, so no issues arise. is false because stricter postconditions can break clients. The client silently ignores the stricter postcondition, so behavior is unaffected. is incorrect; clients cannot ignore contract violations silently. The subclass method throws an exception due to the strengthened postcondition. is not guaranteed; exceptions are not implied by postcondition strengthening.
Hint: Strengthening postconditions breaks client assumptions and causes failures.
Common Mistakes:
Assuming stricter postconditions are safe
Believing clients ignore contract violations
Confusing exceptions with contract violations
2. Which of the following statements about the Adapter pattern is INCORRECT?
medium
A. Adapter changes the interface of an existing object to match what the client expects
B. Adapter can be implemented using inheritance or composition
C. Adapter adds new functionality to the adapted object without modifying it
D. Adapter is used to simplify a complex subsystem by providing a unified interface
Solution
Step 1: Review Adapter intent
Adapter converts incompatible interfaces to make them compatible.
Step 2: Check each statement
A is correct: Adapter changes interface. B is correct: Adapter can use inheritance or composition. C is correct: Adapter can add behavior without modifying original object. D is incorrect: Simplifying a complex subsystem is Facade's role, not Adapter's.
Confusing Adapter with Facade's simplification role
Thinking Adapter only uses inheritance
Assuming Adapter cannot add new behavior
3. Which of the following statements about composition and inheritance is INCORRECT?
medium
A. Composition leads to tighter coupling between classes than inheritance.
B. Inheritance models 'is-a' relationships, while composition models 'has-a' relationships.
C. Favoring composition improves flexibility and maintainability of code.
D. Inheritance can cause fragile base class problems when base classes change.
Solution
Step 1: Analyze each statement
Options A, C, and D are correct statements. Composition leads to tighter coupling between classes than inheritance. is incorrect because composition reduces coupling by separating concerns, whereas inheritance creates tighter coupling due to dependency on base classes.
Final Answer:
Option A -> Option A
Quick Check:
Composition reduces coupling; inheritance increases it.
Confusing which relationship leads to tighter coupling
Assuming composition increases coupling
4. Which of the following statements about the Liskov Substitution Principle is INCORRECT?
medium
A. A subclass can strengthen preconditions of an inherited method to ensure better input validation.
B. A subclass must not weaken postconditions of an inherited method.
C. Covariance in return types is allowed under LSP.
D. Contravariance in method parameter types is allowed under LSP.
Solution
Step 1: Recall LSP precondition rule
Subclasses must not strengthen preconditions; they can only maintain or weaken them.
Step 2: Analyze each statement
A subclass can strengthen preconditions of an inherited method to ensure better input validation. is incorrect because strengthening preconditions breaks substitutability. A subclass must not weaken postconditions of an inherited method. is correct; subclasses can weaken postconditions. Covariance in return types is allowed under LSP. is correct; covariance in return types is allowed. Contravariance in method parameter types is allowed under LSP. is correct; contravariance in parameter types is allowed.
Final Answer:
Option A -> Option A
Quick Check:
Strengthening preconditions violates LSP.
Hint: Preconditions can only be weakened, not strengthened, in subclasses.
Common Mistakes:
Confusing precondition and postcondition rules
Believing strengthening preconditions is safe
Misunderstanding covariance and contravariance
5. In a language that supports multiple inheritance with MRO, what happens if two parent classes define a method with the same name, but one parent class inherits from the other (forming a diamond), and the child class overrides that method? How does the MRO affect which method is called when the child class method calls super()?
hard
A. super() follows the MRO linearization, calling the next method in the MRO sequence, which may skip some classes.
B. super() calls the method from the immediate parent class only, ignoring the diamond structure.
C. super() calls all parent methods with the same name in parallel, combining their effects.
D. super() always calls the method from the base class at the top of the diamond first.
Solution
Step 1: Understand super() in multiple inheritance
super() does not simply call the immediate parent but follows the MRO linearization.
Step 2: MRO linearization
MRO creates a linear order of classes to avoid ambiguity and duplication, so super() calls the next method in this order.
Step 3: Eliminate incorrect options
super() calls the method from the immediate parent class only, ignoring the diamond structure is incorrect because super() is not limited to immediate parent. super() calls all parent methods with the same name in parallel, combining their effects is wrong because super() does not call methods in parallel. super() always calls the method from the base class at the top of the diamond first is incorrect because super() does not always start at the base class.