Exercise 3: A Third Sender Type, and Which Finding It Reproduces — Possible Solution ==================================================================== THE NEW CLASS ------------------------------ class PushNotificationSender(NotificationSender): def send(self, message): return f"Push notification: {message}" A third concrete implementation of this chapter's own NotificationSender abstraction. RESULTS ------------------------------ NotificationServiceGood unchanged after adding PushNotificationSender: True NotificationServiceGood + PushNotificationSender: Push notification: hello NotificationServiceGood's own source, captured before and after adding the new sender type, is confirmed byte-identical - and the new sender works correctly when injected, producing "Push notification: hello". WHICH SPECIFIC SOFTWARE ARCHITECTURE FUNDAMENTALS FINDING THIS REPRODUCES ------------------------------ This chapter's own text draws the connection to Software Architecture Fundamentals Chapter 7's own verified finding: PricingEngine's source contained ZERO references to any of its four concrete adapter classes (FakeInventoryAdapter, RealInventoryAdapter, FakeNotificationAdapter, RealNotificationAdapter), confirmed via the identical inspect.getsource() technique used here. This exercise reproduces that exact finding at this chapter's own smaller scale: adding a genuinely NEW concrete implementation (PushNotificationSender, mirroring that chapter's own addition of a third port in ITS OWN Exercise 1) required zero changes to the high-level class depending on the abstraction - the same "core logic never needs to know about a new concrete implementation" property, verified twice, one course apart. WHY THIS IS THE SAME FINDING, NOT JUST A SIMILAR ONE ------------------------------ Both verifications use the identical method (capture source via inspect.getsource(), add a new concrete class, re-capture source, compare for byte-equality) against the identical shape of code (a high-level class holding a reference to an abstract type, with multiple interchangeable concrete implementations). The scale differs (a whole pricing engine with two ports vs. a small notification service with one), but the underlying guarantee being tested - and confirmed - is exactly the same one: depending on an abstraction instead of a concrete class means new concrete classes can be added without ever touching the code that depends on the abstraction. WHY THIS WORKS AS AN ANSWER ------------------------------ A third concrete sender is added following this chapter's own established pattern, the byte-identical result is verified directly, and the connection to Software Architecture Fundamentals Chapter 7 is made specific - naming the exact prior finding (zero adapter references) rather than gesturing vaguely at "similar ideas."