ProgramGuru

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.

  1. Separate reasons for change

    Identify calculation and notification as different responsibilities.

  2. Supply a collaborator

    Make external effects visible as dependencies.

  3. Avoid unnecessary structure

    Choose the simplest design that keeps the real rule understandable.

A report coordinates focused pieces

  1. Calculate

    Apply the marks rule.

    40 + 35 → 75
  2. Coordinate

    Create the report message.

    Total: 75
  3. Notify

    Use a supplied collaborator.

    Local message collector
Read these stages alongside the complete example. They explain the relationship; they do not execute code.

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: 75

A 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: 75

Apply 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: 55

Recognize 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

Why receive notify rather than hard-code email inside total?
Choose one answer. You can try again.

Answer and explanation

It separates calculation from an external effect and exposes the collaborator.

Yes. Each part has a focused responsibility.

Apply the idea to a new situation

When should a tiny formatting task gain a class hierarchy?
Choose one answer. You can try again.

Answer and explanation

When real responsibilities and contracts justify that structure.

Yes. Use added structure to clarify meaningful boundaries.

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.

    Next lesson: Syntax, Runtime and Logic Errors →