Object-Oriented Design: Focused Responsibilities
Keep a report from becoming every school service
A report class that calculates totals, saves files, sends email and manages permissions changes for several unrelated reasons. A small rule change can disturb unrelated work.
Good object design gives each component a focused responsibility and connects components through explicit contracts. Composition supplies collaborators rather than forcing every behavior into one object or hierarchy.
Separate reasons for change
Identify calculation and notification as different responsibilities.
Supply a collaborator
Make external effects visible as dependencies.
Avoid unnecessary structure
Choose the simplest design that keeps the real rule understandable.
A report coordinates focused pieces
Calculate
Apply the marks rule.
40 + 35 → 75Coordinate
Create the report message.
Total: 75Notify
Use a supplied collaborator.
Local message collector
An explicit dependency exposes an effect
A collaborator is an object or function a component uses. Receiving it in the constructor makes the relationship visible and allows different implementations without changing report logic.
Keep the dependency contract precise: here notify accepts a message synchronously. It does not promise network delivery. A separate pure calculation can be tested without sending or saving anything.
Before starting, review contracts and useful interfaces. These examples use JavaScript; other languages can use different syntax and rules.
Collect a report message locally
The constructor receives a notifier function. publish calculates the total and supplies text to that collaborator. In this example the notifier only pushes into a local array.
No email or database write occurs. This allows the behavior to be checked without pretending a local collector proves delivery in production.
JavaScript · local teaching example
function total(maths, science) { return maths + science; }
class Report {
constructor(notify) { this.notify = notify; }
publish(maths, science) {
this.notify('Total: ' + total(maths, science));
}
}
const messages = [];
const report = new Report(message => messages.push(message));
report.publish(40, 35);
console.log(messages[0]);Expected output
Total: 75A small task may need no class
When the only task is formatting a total, a function with explicit inputs can be clearer than a class and several interfaces.
Add structure when responsibilities and lifetimes demand it. Object-oriented design is not measured by the number of classes.
JavaScript · local teaching example
function reportText(maths, science) {
return 'Total: ' + (maths + science);
}
console.log(reportText(40, 35));Expected output
Total: 75Apply the rule to a small change
Use a supplied collector to publish a total of marks 25 and 30, and inspect the collected message.
Worked answer
The same report contract produces Total: 55. The caller selects the effect implementation; the calculation does not need to know where messages will eventually go.
JavaScript · local teaching example
class Report {
constructor(notify) { this.notify = notify; }
publish(maths, science) { this.notify('Total: ' + (maths + science)); }
}
const messages = [];
new Report(message => messages.push(message)).publish(25, 30);
console.log(messages[0]);Expected output
Total: 55Recognize mistakes and limits
Avoid deep inheritance built only to share lines. A compatible subtype can help, but independent capabilities often compose better. Expose only the operations a caller needs rather than every internal detail.
An injected dependency still needs failure and timing policies. Replacing a synchronous collector with an asynchronous sender requires an explicit Promise contract and error handling. Do not discard failures because the dependency is abstracted.
For the language or tool rules used here, see JavaScript class behavior.
Check the core idea
Apply the idea to a new situation
Design around responsibility and explicit effects
Keep calculation, coordination and external actions understandable. Compose collaborators through clear contracts, preserve valid state and prefer simple functions when a class adds no useful boundary.
Connect this idea with module responsibilities and shared rules without overgeneralization.
Reaching the recap and completing the quizzes are separate milestones.