Core principles and patterns from Lectures 2 & 3. Each card: definition, key rule, classic violation, fix.
A class should have one and only one reason to change.
Each class encapsulates exactly one responsibility.
An Invoice class that calculates totals, sends emails, and saves to DB.
Extract into Mailer, SalesTax, and InvoiceRepository classes.
Open for extension, closed for modification.
Add new behavior without changing existing code. Rely on abstractions.
AreaCalculator.sum() with if/else chains for each shape type.
Define ShapeInterface with area(); each shape implements it. Calculator iterates the interface.
Subtypes must be substitutable for their base types without breaking behavior.
Child classes must not remove base class behavior or violate invariants.
A Square overriding volume() to throw an exception or return 0.
Separate ShapeInterface (area) from ThreeDShapeInterface (volume).
Clients should not be forced to depend on methods they do not use.
Several specialized interfaces are better than one all-purpose interface.
A Shape interface with both area() and volume() — flat shapes forced to implement volume().
Split into ShapeInterface and SolidShapeInterface.
Depend on abstractions, not concretions.
High-level modules should not depend on low-level modules. Both depend on abstractions.
SumCalculatorSaver directly instantiates MySQLDatabase.
Inject a DatabaseInterface. Swap MySQL, Postgres, etc. without changing the saver.
Define a family of algorithms, encapsulate each, make them interchangeable. Behavioral.
Uses composition: context holds a reference to a strategy interface, swappable at runtime.
Giant if/else in Hero.attack() for each attack type.
AttackStrategy interface; MeleeAttack, RangedAttack classes. Hero calls strategy.attack().
Define the skeleton of an algorithm in a superclass; subclasses override specific steps. Behavioral.
Uses inheritance: the template method is final in the base class; only hook/abstract steps are overridden.
Duplicated sorting algorithms that differ only in the comparison step.
Abstract SortingTool with sort() template method; subclasses override compare().
Ensure a class has only one instance with a global access point. Creational.
Private constructor + static getInstance() that lazily creates or returns the cached instance.
Multiple Hero objects created via new Hero() in different parts of the game.
Private constructor, static instance field, public Hero.getInstance().
Design Principles (SOLID): Abstract guidelines, high-level, language-agnostic, not a concrete solution.
Design Patterns: Concrete, proven solutions to recurring problems, low-level technical details.
Strategy = composition + interface (swap at runtime). Template Method = inheritance + abstract class (fixed skeleton, override steps).
Click True or False. Rationale is revealed immediately. Grouped by topic.
Original scenarios at tiered difficulty. Pick an answer, then read the detailed explanation.
UserService class handles user registration, sends a welcome email, and writes a log entry to a file. A teammate asks you to review it. Which principle is violated?UserRegistrar, WelcomeMailer, and Logger classes, each with a single responsibility.NotificationSender class has a method send() with a switch statement: case "sms", case "email", case "push". To add Slack notifications, a developer must edit this method. What is the issue?NotificationStrategy interface with send(), and create SmsNotification, EmailNotification, PushNotification, SlackNotification as concrete strategies. The sender calls strategy.send() without needing to change.Vehicle base class with a fly() method. Car extends Vehicle and overrides fly() to throw UnsupportedOperationException. What is the primary violation?Vehicle expects fly() to work. Car breaks this contract by throwing an exception. Fix: separate into Drivable and Flyable interfaces. Only classes that actually fly implement Flyable. This also fixes the ISP issue of forcing non-flying vehicles to have fly().DeliveryFeeStrategy interface with calculateFee(). Concrete classes: StandardFee, ExpressFee, DroneFee. The Order context holds a reference to the strategy and can switch dynamically.ConnectionPool constructor private, add a static getInstance() that lazily creates or returns the cached pool. All services call ConnectionPool.getInstance().ReportGenerator class creates reports in PDF, Excel, and HTML. It has a method generate(String type) that switches on the type to build each format. The same class also emails the report. A new CSV format is requested. What are the violations?ReportMailer. Second, OCP: the switch on type means adding CSV requires modifying existing code. Fix: ReportFormatStrategy interface with generate(), concrete classes per format. The generator is closed for modification when new formats arrive.checkout then build then deploy. The checkout and deploy steps are always the same, but the build step differs: Java projects use Maven, Node projects use npm, Python projects use pip. Which pattern fits best?Pipeline with a final run() method that calls checkout(), build(), deploy(). The build() method is abstract, overridden by JavaPipeline, NodePipeline, PythonPipeline. This is inheritance-based because the structure is fixed.OrderProcessor (high-level) directly depends on MySQLDatabase (low-level concrete). OCP: switching databases requires modifying the processor. Fix: define DatabaseInterface with save(). Inject via constructor: OrderProcessor(DatabaseInterface db). Now both MySQL and Postgres implement the interface, and the processor never changes.MediaPlayer interface has methods: playAudio(), playVideo(), streamLive(). A PodcastPlayer class implements it but only needs playAudio(). It throws exceptions for playVideo() and streamLive(). Identify the violations.playVideo() and streamLive() which it doesn't use. LSP: any code expecting a MediaPlayer will break when calling video/stream on a PodcastPlayer. Fix: split into AudioPlayable, VideoPlayable, LiveStreamable. PodcastPlayer implements only AudioPlayable.Logger with final log() calling format(), filter(), write(). Subclasses override only write(). Strategy would also work (inject a Writer), but you'd lose the guaranteed shared structure. When the skeleton is fixed and only specific steps vary, Template Method is the better fit.GameServer that must be a single instance. Players can choose combat styles (Melee, Ranged, Stealth) that are swappable during gameplay. The game also processes turn sequences: startTurn, then executeAction, then endTurn — where executeAction varies by game mode (PvP vs PvE). Identify all applicable patterns.CombatStrategy interface with Melee, Ranged, Stealth implementations. (3) Template Method: Turn processing has a fixed skeleton (start, execute, end) with only executeAction varying between PvP and PvE modes — abstract TurnProcessor with final processTurn().validate, then transform, then write — but the transform and write steps differ per format. The exporter is directly used by a CloudUploader that instantiates it as new DocExporter(). Identify all violations and suggest a redesign.Exporter with final export() calling validate(), abstract transform(), abstract write(). Concrete: PdfExporter, HtmlExporter, DocxExporter. For DIP: CloudUploader depends on Exporter (abstraction), not a concrete class.DeviceController interface with methods: turnOn(), turnOff(), setTemperature(), setBrightness(), lock(), unlock(). All devices (lights, thermostats, door locks) implement this interface. A SmartHomeApp (high-level) directly creates new PhilipsLight() and new NestThermostat(). Identify violations and fix.Switchable (turnOn/Off), TemperatureControllable (setTemperature), Dimmable (setBrightness), Lockable (lock/unlock). Each device implements only relevant interfaces. DIP: SmartHomeApp (high-level) directly instantiates concrete device classes. Fix: inject devices via constructor using their interface types.fetchData, then analyze, then formatOutput. The analysis step differs, but everything else is the same. Currently, both report types independently implement all three steps with copy-pasted code. A new quarterly report is needed. Additionally, the output format (PDF vs CSV) should be swappable at runtime. Which combination of patterns?Report with final generate(), abstract analyze(). Subclasses: WeeklyReport, MonthlyReport, QuarterlyReport. For the output format (PDF vs CSV) that must be swappable at runtime, use Strategy: OutputFormatStrategy interface with PdfFormat and CsvFormat. The Report class holds a strategy reference for formatting.FareCalculator, RideRepository, ReceiptMailer, RideLogger.FareStrategy interface with EconomyFare, PremiumFare, PoolFare, LuxuryFare.MySQLDatabase (concrete), EmailService (concrete), and FileLogger (concrete). Fix: Define abstractions: DatabaseInterface, NotificationService, Logger. Inject via constructor.LearningApp class does new RecordedCourse() directly. Enrollment follows the same flow for all courses: register, then enroll, then startLearning — but startLearning differs per course type. Identify all violations and design the fix.Course interface forces all course types to implement methods they don't support. Fix: Split into Watchable (playVideo), Downloadable (downloadPDF), Assignable (submitAssignment), Quizzable (takeQuiz), LiveJoinable (joinLiveSession). Each course implements only relevant interfaces.new RecordedCourse() — depends on concrete type. Fix: inject via interface types.EnrollmentProcess with final enroll() template method; subclasses override startLearning().PostgresDB inside saveToDatabase().getInstance().PickingStrategy interface with NearestFirst, PriorityFirst, FIFO, BatchPick.TaskExecutor with final execute(), abstract scan(). StandardTaskExecutor and FragileTaskExecutor override scan().WarehouseController (Singleton) — thin coordinator onlyPickingStrategy interface — injected into controller (Strategy)TaskExecutor — skeleton for task sequence (Template Method)DatabaseInterface — injected, not instantiated (DIP)WarehouseRepository — handles persistence (SRP)