CPSA Software Design Principles Questions and Answers — Questions and Answers
Question 1: A software architect is designing a system where a `ReportGenerator` class needs access to customer data. Instead of allowing `ReportGenerator` to directly instantiate a `CustomerRepository` class, the architect mandates that `ReportGenerator` must depend on an `ICustomerRepository` interface. The concrete implementation will be provided at runtime. Which SOLID principle is primarily being applied?
- Single Responsibility Principle
- Open/Closed Principle
- Liskov Substitution Principle
- Dependency Inversion Principle (Correct answer)
Correct answer: Dependency Inversion Principle
The Dependency Inversion Principle (DIP) states that high-level modules should not depend on low-level modules; both should depend on abstractions. [28, 32] In this scenario, `ReportGenerator` (high-level) is decoupled from the concrete `CustomerRepository` (low-level) by depending on the `ICustomerRepository` abstraction (interface). [34] This allows the underlying data access implementation to change without affecting the report generation logic. [14]
Question 2: A team is developing a `PaymentProcessor` class that initially only handles credit card payments. A new requirement emerges to also support payments via a third-party gateway like PayPal. To adhere to the Open/Closed Principle, what is the BEST course of action?
- Modify the `PaymentProcessor` class with an if-else statement to check the payment type and call the appropriate logic.
- Create a `PayPalProcessor` class and have the client code decide which processor class to instantiate.
- Refactor the `PaymentProcessor` to use a strategy pattern, where different payment methods are implemented as separate classes that conform to a common `IPaymentStrategy` interface. (Correct answer)
- Add a new `processPayPalPayment` method directly to the existing `PaymentProcessor` class.
Correct answer: Refactor the `PaymentProcessor` to use a strategy pattern, where different payment methods are implemented as separate classes that conform to a common `IPaymentStrategy` interface.
The Open/Closed Principle states that software entities should be open for extension but closed for modification. [9, 18] Adding if-else statements or new methods to the existing class modifies it, violating the principle. [1] Refactoring to a strategy pattern allows for the addition of new payment methods (extensions) by creating new strategy classes without changing the core `PaymentProcessor` class, which is closed for modification. [7]
Question 3: You are reviewing a class called `UserManager` that performs the following tasks: authenticates user credentials against a database, sends a welcome email to new users, formats user data into a JSON string for an API response, and logs every login attempt to a file. This class most clearly violates which design principle?
- Interface Segregation Principle
- Single Responsibility Principle (Correct answer)
- Low Coupling
- Information Expert
Correct answer: Single Responsibility Principle
The Single Responsibility Principle (SRP) states that a class should have only one reason to change. [21] The `UserManager` class has multiple responsibilities: authentication, notification, data formatting, and logging. [30] A change in the email template, the database schema, the API format, or the logging mechanism would each require a change to this single class, indicating a clear violation of SRP. [2, 30]
Question 4: A system has a base class `Bird` with a method `fly()`. A new requirement calls for adding an `Ostrich` class. Since an ostrich is a bird, the new class inherits from `Bird`. However, ostriches cannot fly, so the developer overrides the `fly()` method to throw a `NotSupportedException`. Why is this a violation of the Liskov Substitution Principle (LSP)?
- Because the `Ostrich` class adds new methods not present in the `Bird` class.
- Because the `fly()` method in the subclass has a different performance profile than the base class method.
- Because an `Ostrich` object cannot be used wherever a `Bird` object is expected without potentially breaking the program's correctness. (Correct answer)
- Because inheritance should not be used for 'is-a' relationships; composition is always preferred.
Correct answer: Because an `Ostrich` object cannot be used wherever a `Bird` object is expected without potentially breaking the program's correctness.
The Liskov Substitution Principle (LSP) states that objects of a superclass should be replaceable with objects of its subclasses without altering the correctness of the program. [5, 11] If a piece of code expects a `Bird` object and calls its `fly()` method, substituting it with an `Ostrich` object would cause an exception, breaking the application's behavior. [26] The subclass alters the expected contract of the superclass, which violates LSP. [35]
Question 5: An architect is designing a modular system. They notice that a change to the `User` module frequently necessitates changes in the `Order`, `Invoice`, and `Shipping` modules. This is a symptom of what undesirable characteristic?
- Low Cohesion
- High Coupling (Correct answer)
- High Cohesion
- Low Coupling
Correct answer: High Coupling
Coupling refers to the degree of interdependence between software modules. [12] High (or tight) coupling means that modules are closely connected, and changes in one module are likely to ripple through and require changes in other modules. [8, 22] This makes the system harder to maintain and evolve. The goal is to have low coupling, where modules are as independent as possible. [25]
Question 6: A developer creates a large, general-purpose interface called `IWorker` with methods for `work()`, `eat()`, and `sleep()`. They then create a `Robot` class that implements `IWorker`. The `Robot` can `work()`, but the `eat()` and `sleep()` methods are irrelevant and are implemented by throwing an exception or doing nothing. Which SOLID principle is being violated?
- Open/Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle (Correct answer)
- Single Responsibility Principle
Correct answer: Interface Segregation Principle
The Interface Segregation Principle (ISP) states that clients should not be forced to depend on methods they do not use. [10, 21] The `Robot` class is forced to implement the `eat()` and `sleep()` methods from the `IWorker` interface even though they are not applicable. [33] The correct approach would be to segregate the 'fat' interface into smaller, more specific ones, like `IWorkable`, `IEatable`, and `ISleepable`. [19, 33]
A software architect is designing a system where a `ReportGenerator` class needs access to customer data.
Instead of allowing `ReportGenerator` to directly instantiate a `CustomerRepository` class, the architect mandates that `ReportGenerator` must depend on an `ICustomerRepository` interface.
The concrete implementation will be provided at runtime.
Which SOLID principle is primarily being applied?