The parent note gave you one rule (one class, one reason to change ) and one
formula (R ( A ) = 1 − ( 1 − p ) A − 1 ). This page throws every kind of situation at that
rule so you never meet a case you haven't already practised: clean classes, dirty
classes, edge counts like "zero actors" and "one method used by two bosses", the risk
formula at its boundaries, a word problem, and a sneaky exam twist that looks like a
violation but isn't.
This is a companion to the parent SRP note .
Read that first — here we only apply it, exhaustively.
Before working examples, let us list every cell a SRP question can fall into. Think
of it like listing every quadrant before doing trigonometry: if a case is on this table,
you will see a worked example for it below.
#
Cell (case class)
What makes it distinct
Worked in
C1
Clean class, many methods
Many operations but ONE actor → not a violation
Ex 1
C2
Two actors, shared helper
The classic god-class; DRY re-couples actors
Ex 2
C3
Three+ actors
Full extract + Facade; risk formula bites hardest
Ex 3
C4
A = 1 boundary
Degenerate: one actor → R = 0
Ex 4
C5
A = 0 boundary
Degenerate: no actor → dead code, R undefined-ish
Ex 4
C6
Risk formula limiting behaviour
p → 0 , p → 1 , A → ∞
Ex 5
C7
Over-split (SRP misapplied)
Too many tiny classes → shotgun surgery
Ex 6
C8
Word problem (real org)
Map methods → stakeholders from a story
Ex 7
C9
Exam twist: looks-bad-but-clean
Two methods, but same actor → trap
Ex 8
C10
Numeric comparison
Quantify "before vs after" a split
Ex 9
Each example below is tagged with the cell it fills. Together they cover all ten .
Every numeric example uses R ( A ) = 1 − ( 1 − p ) A − 1 . Before plugging numbers, see it.
Look at the red curve: with break-probability p = 0.2 fixed, adding actors (A on the
horizontal axis) pushes risk R upward, fast at first then flattening toward 1 . The
single black dot at A = 1 sits exactly on R = 0 — an SRP-clean class carries zero
cross-actor risk. That dot is the goal of every refactor.
A Vector3D class has add, subtract, dot, cross, normalize, length.
Six methods. Does it violate SRP?
Forecast: Six methods sounds like a lot — guess yes or no before reading on.
List the methods. All six operate on the same (x, y, z) data.
Why this step? SRP starts from methods, per the parent's procedure.
Tag each with an actor (who requests changes?). Every one of them changes only
if the definition of 3D vector maths changes — one mathematical stakeholder.
Why this step? SRP counts actors , not methods.
Group by actor. One group ⇒ A = 1 .
Why? A single reason to change means a single responsibility.
Conclusion: No violation. Many methods, one actor.
Verify: Risk R ( 1 ) = 1 − ( 1 − p ) 0 = 1 − 1 = 0 for any p . Zero cross-actor risk,
so nothing to split. ✓ (This is the trap the parent's [!mistake] warned about.)
class Invoice {
double totalWithTax () { return subtotal () * 1.18 ; } // Accounting
String printReceipt () { return "Total: " + subtotal (); } // Customer-facing
private double subtotal () { ... } // shared helper
}
Accounting asks: "change tax from 18% to 20%." Where is the danger?
Forecast: Which method might break that nobody asked to change?
Tag actors. totalWithTax → Accounting . printReceipt → Customer/Sales .
Why? Two different bosses request changes here.
Spot the shared code. Both call subtotal().
Why matters? A change to subtotal() reaches both actors.
Trace the change. Accounting edits tax in totalWithTax only — safe so far .
But if they "optimise" subtotal() (rounding), the receipt total shifts too.
Why a problem? Sales never asked; SRP boundary crossed.
Fix. Extract Subtotal data, split into TaxCalculator (Accounting) and
ReceiptPrinter (Sales). Now A = 1 each.
Why? Aligns boundaries with the two stakeholders.
Verify: Before split A = 2 , p = 0.2 : R ( 2 ) = 1 − 0. 8 1 = 0.2 → 20% of changes risky.
After split each class has A = 1 → R = 0 . ✓
The parent's Employee god-class serves Accounting (calculatePay), HR (reportHours),
DBA (save). A = 3 . Refactor and quantify the improvement.
Forecast: By how much does the risk drop, with p = 0.2 ?
Group by actor → three classes: PayCalculator, HourReporter,
EmployeeRepository.
Why? Three distinct bosses ⇒ three responsibilities.
Extract EmployeeData (passive struct).
Why? Its only reason to change is the schema — its own single actor.
Add EmployeeFacade delegating to the three.
Why? Restores one call-site for clients — see Facade Pattern .
Compute risk drop. Before: R ( 3 ) = 1 − 0. 8 2 = 1 − 0.64 = 0.36 .
After: each class A = 1 ⇒ R = 0 .
Why? No two actors share code anymore.
Verify: 0.36 − 0 = 0.36 absolute risk removed, i.e. 36% of changes made safe. ✓
The red block (the tangled god-class) fans out into three isolated black boxes, each
owned by exactly one actor; the Facade is the single door back in.
What does the risk formula say at its smallest inputs?
Forecast: Guess R for a class with (a) exactly one actor, (b) no actor.
Case A = 1 . R ( 1 ) = 1 − ( 1 − p ) 1 − 1 = 1 − ( 1 − p ) 0 = 1 − 1 = 0 .
Why ( 1 − p ) 0 = 1 ? Anything (non-zero) to the power 0 is 1 — the empty product,
"multiply nothing," which is 1 . There are A − 1 = 0 other actors, so there is
literally nobody to break.
Case A = 0 . R ( 0 ) = 1 − ( 1 − p ) − 1 = 1 − 1 − p 1 .
With p = 0.2 : R = 1 − 0.8 1 = 1 − 1.25 = − 0.25 .
Why is that nonsense? A negative probability is impossible. The formula was
derived assuming "fix the one requesting actor, count the A − 1 others" — that
story needs at least one actor. A = 0 means dead code nobody owns : not an SRP
question at all, just code to delete.
Rule of thumb. The formula is valid for integer A ≥ 1 .
Why? A − 1 ≥ 0 keeps the exponent non-negative and R ∈ [ 0 , 1 ) .
Verify: R ( 1 ) = 0 ✓ ; R ( 0 ) = − 0.25 (flagged as out-of-domain, not a real risk). ✓
Push p and A to their extremes with the formula R ( A ) = 1 − ( 1 − p ) A − 1 .
Forecast: What happens as (a) p → 0 , (b) p → 1 , (c) A → ∞ ?
p → 0 (helpers never break). ( 1 − 0 ) A − 1 = 1 , so R → 0 for any A .
Why? If changes never leak, sharing code is harmless — SRP is a stylistic concern,
not a safety one, in this idealised world.
p → 1 (every change breaks everything). For A ≥ 2 , ( 1 − 1 ) A − 1 = 0 , so
R → 1 . Why? One actor's edit is certain to hit another — total coupling.
A → ∞ with 0 < p < 1 . ( 1 − p ) A − 1 → 0 , so R → 1 .
Why? Add enough actors and someone is almost surely broken. This is the
mathematical shape of a God Object (anti-pattern) .
Concrete check at p = 0.5 : R ( 2 ) = 0.5 , R ( 3 ) = 0.75 , R ( 5 ) = 0.9375 .
Why grows? Each extra actor multiplies another survival factor ( 1 − p ) < 1 in,
shrinking the "nobody broken" chance.
Verify: R ( 5 ) at p = 0.5 = 1 − 0. 5 4 = 1 − 0.0625 = 0.9375 . ✓
Three red curves for p = 0.2 , 0.5 , 0.8 : all start at R = 0 when A = 1 and climb toward
the black ceiling line R = 1 — steeper for bigger p .
A developer splits Vector3D (from Ex 1) into VectorAdder, VectorSubtractor,
VectorDotter, VectorCrosser, VectorNormalizer, VectorLengther — one method each.
What smell did they create?
Forecast: They "obeyed single = one method." What breaks now?
Count actors, not methods. All six still change for the same reason (vector
maths). Why? SRP counts reasons to change — still one.
What did splitting buy? Nothing — no actor was separated. It only scattered
logic across six files.
New cost. To change the number type from double to float, you now edit
six classes. Why bad? That is Shotgun Surgery — one conceptual change,
many files.
Fix. Merge back into one Vector3D. A was 1 the whole time.
Verify: Before and after the wrong split, R = 0 either way (one actor), so the
split reduced no risk yet multiplied edit-sites 6× — pure loss. ✓
A ticketing startup has a Booking class:
reserveSeat(), chargeCard(), emailConfirmation(), logToWarehouse().
The teams are: Operations (seats), Payments , Marketing (emails),
Data (warehouse logs). How many classes after refactor, and what is the risk drop
at p = 0.15 ?
Forecast: Guess the actor count first.
Tag each method with its team. reserve→Ops, charge→Payments, email→Marketing,
log→Data. Four distinct actors ⇒ A = 4 .
Why? Each is a different boss who can request changes.
Compute before-risk. R ( 4 ) = 1 − ( 0.85 ) 3 = 1 − 0.614125 = 0.385875 .
Why 0.85 ? 1 − p = 1 − 0.15 . Why cubed? A − 1 = 3 other actors.
Refactor → 4 responsibility classes + BookingData + BookingFacade.
Why Facade? Front-end still calls one book() — see Facade Pattern .
After-risk = 0 per class.
Verify: R ( 4 ) ≈ 0.3859 ; drop ≈ 38.6% of changes made safe. ✓
Final class count = 4 policies + 1 data + 1 facade = 6 . ✓
class TemperatureConverter {
double toFahrenheit ( double c ) { return c * 9 / 5 + 32 ; }
double toCelsius ( double f ) { return (f - 32 ) * 5 / 9 ; }
}
Two methods, two conversions — an exam asks "SRP violation?" Many students answer
yes . Are they right?
Forecast: Commit to yes or no before step 1.
Tag actors. Both methods change only if the temperature-conversion definition
changes (a physics/units decision) — one actor.
Why? No second stakeholder pulls on either method independently.
Check for shared mutable state. None; both are pure functions.
Why matters? No hidden cross-actor coupling to worry about.
Verdict. Not a violation. A = 1 . This is the [!mistake] from the parent
("one thing ≠ one method count") disguised as an exam question.
When would it violate? If you added logConversion() owned by a Data team
and formatForUI() owned by Frontend — then A = 3 , split it.
Verify: As written R ( 1 ) = 0 ; the hypothetical extended version R ( 3 ) at p = 0.2
= 0.36 . ✓
A UserProfile class serves 5 actors (A = 5 ) with per-change break prob p = 0.1 .
You split it into 5 clean classes. Quantify the exact improvement.
Forecast: Guess the before-risk to one decimal place.
Before. R ( 5 ) = 1 − ( 0.9 ) 4 . Compute 0. 9 4 = 0.6561 , so R = 0.3439 .
Why 0. 9 4 ? survival 0.9 per each of A − 1 = 4 other actors.
After. 5 classes, each A = 1 ⇒ R = 0 .
Improvement. 0.3439 − 0 = 0.3439 , i.e. ~34.4% of changes de-risked.
Why care? This is the Cohesion and Coupling payoff in one number.
Verify: R ( 5 ) = 1 − 0. 9 4 = 0.3439 . ✓
Recall
Which cell does a class with 8 methods but one stakeholder fall in? ::: C1 — clean, many methods, A = 1 , no violation.
Why is R ( 0 ) negative and what does it really mean? ::: The derivation assumes ≥1 actor; A = 0 is dead unowned code, outside the formula's domain (not a real probability).
As A → ∞ with 0 < p < 1 , what does R approach and why? ::: R → 1 ; each extra actor multiplies in another ( 1 − p ) < 1 , driving the "nobody broken" chance to 0 .
What smell does over-splitting into one-method classes create? ::: Shotgun surgery — one conceptual change now edits many files.
For A = 4 , p = 0.15 , what is the before-refactor risk? ::: 1 − 0.8 5 3 ≈ 0.386 .
Count bosses, not buttons. A dozen buttons (methods) with one boss (actor) is fine;
two bosses on one button is the violation.
SOLID — Single Responsibility Principle — the parent rule these examples exercise.
God Object (anti-pattern) — Ex 3 & the A → ∞ limit in Ex 5.
Shotgun Surgery — the over-split smell in Ex 6.
Facade Pattern — the single door-back in Ex 3 & Ex 7.
Cohesion and Coupling — the risk drop is coupling, measured.
DRY Principle — the shared helper trap in Ex 2.
Open-Closed Principle — after splitting, extend without editing.