ProgramGuru

Encapsulation: Keep State Valid

Prevent an invalid mark from entering a record

A science mark must be finite and between zero and fifty. If any caller can overwrite it with arbitrary text or 99, every report needs to repair the damage later.

Encapsulation keeps internal state behind a supported interface. Updates pass through operations that preserve the object’s rules instead of exposing unrestricted writes.

  1. State a rule

    Describe the valid mark range and type.

  2. Enforce an update

    Reject unsuitable values without damaging the earlier mark.

  3. Explain privacy

    Distinguish a language boundary from security or secret storage.

Validate before changing the stored mark

  1. Current state

    The record holds a valid mark.

    35
  2. Invalid request

    99 is outside the allowed range; preserve state.

    still 35
  3. Valid request

    40 passes the checks.

    now 40
Read these stages alongside the complete example. They explain the relationship; they do not execute code.

An interface makes valid changes explicit

A private JavaScript field begins with # and must be declared in the class. Callers use methods rather than accessing that private field directly.

Privacy alone does not enforce the school rule. The method checks the requested value before storing it and reports whether the update succeeded. This prevents a rejected update from partly changing the record.

Before starting, review instances and methods. These examples use JavaScript; other languages can use different syntax and rules.

Reject an invalid update and accept a valid one

The first set call rejects 99 and returns false. read confirms the initial value is still 35. The next set accepts 40 and returns true.

Number.isFinite rejects nonnumeric values, NaN and Infinity. The range check then enforces the school-specific maximum.

JavaScript · local teaching example

class ScienceMark {
  #value = 35;
  set(value) {
    if (!Number.isFinite(value) || value < 0 || value > 50) return false;
    this.#value = value;
    return true;
  }
  read() { return this.#value; }
}
const mark = new ScienceMark();
console.log(mark.set(99));
console.log(mark.read());
console.log(mark.set(40));
console.log(mark.read());

Expected output

false
35
true
40

Return a value rather than expose an editable container

This getter returns a numeric primitive, so changing the caller’s local variable does not change the private field.

A getter returning a mutable internal array or record would require more care: callers could mutate shared objects. Encapsulation is about the full interface, including results.

JavaScript · local teaching example

class Mark {
  #value = 35;
  read() { return this.#value; }
}
const mark = new Mark();
let local = mark.read();
local = 40;
console.log(mark.read());
console.log(local);

Expected output

35
40

Apply the rule to a small change

Use the same rules to accept a genuine zero mark and reject the string “40.”

Worked answer

Numeric zero satisfies the range. The quoted value is a string, so Number.isFinite rejects it before assignment. Conversion, if desired, should happen at a documented input boundary.

JavaScript · local teaching example

class ScienceMark {
  #value = 35;
  set(value) {
    if (!Number.isFinite(value) || value < 0 || value > 50) return false;
    this.#value = value; return true;
  }
  read() { return this.#value; }
}
const mark = new ScienceMark();
console.log(mark.set(0));
console.log(mark.set('40'));
console.log(mark.read());

Expected output

true
false
0

Recognize mistakes and limits

Private fields protect access under JavaScript’s language rules, not secrets from someone who controls the running environment. Server authorization and secure storage are separate concerns.

Define constructor validation too if initial state comes from external inputs. Avoid silent rejection that leaves callers thinking an update succeeded; a boolean or a meaningful error makes the outcome reviewable.

For the language or tool rules used here, see JavaScript class behavior.

Check the core idea

JavaScript · code for this question

class ScienceMark {
  #value = 35;
  set(value) {
    if (!Number.isFinite(value) || value < 0 || value > 50) return false;
    this.#value = value;
    return true;
  }
  read() { return this.#value; }
}
const mark = new ScienceMark();
console.log(mark.set(99));
console.log(mark.read());
console.log(mark.set(40));
console.log(mark.read());
Why does the invalid set leave the mark at 35?
Choose one answer. You can try again.

Answer and explanation

The validation returns before assigning a new private value.

Yes. The rejection path preserves the previous valid state.

Apply the idea to a new situation

Is returning a mutable internal record always a protected interface?
Choose one answer. You can try again.

Answer and explanation

No; callers may change the same object unless the interface controls that.

Yes. Encapsulation includes what you expose as well as what you hide.

Preserve rules at the state-changing boundary

Encapsulation provides operations that keep state valid. Validate before updating, report rejected requests and examine whether returned objects expose mutable internals.

Connect this idea with supported interfaces and meaningful validation errors.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: Abstraction: A Useful Interface and Contract →