Felipe Wagner
Felipe Wagner

Reputation: 148

Force the use of setters instead of straight assignments in a JS ES6 Class

I have a CameraBuilder class that looks like this:

class CameraBuilder {
    constructor() {
        if (arguments.length) {
            throw new Error('[CameraBuilder constructor ERROR] class constructor does not accept parameters.');
        }
        this.camera = {};
    }

    withFarmLabel(farmLabel) {
        this.camera.farm_label = farmLabel;
        return this;
    }

    // more methods here

    build() {
        const missingProps = [];
        if (!this.camera.farm_label) {
            missingProps.push('\nMissing farm_label property. Use the withFarmLabel method in order to assign it.');
        }
        
        // more validations like the one above here

        if (missingProps.length) {
            const errorMsg = missingProps.join('');
            throw new Error(`[CameraBuilder build ERROR] ${errorMsg}`);
        }

        return this.camera;
    }
}

Since most of my validations are on the build() method and there are some business logic on some of these methods associated with how the user is building an instance of CameraBuilder, I wouldn't want anyone assigning cameraBuilderObj.camera directly. Is there any way I can enforce the use of the Class methods in order to assign properties to the Camera object?

Upvotes: 0

Views: 48

Answers (2)

ray
ray

Reputation: 27245

CertainPerformance's answer probably makes more sense--don't expose it in the first place--but if for some reason you didn't want to go that route (or if you're in an environment where private fields aren't supported) you could define setters on it, so that direct assignments go through your function.

class Foo {
  constructor () {
    this._bar = 'baz';
  }

  set bar (value) {
    this._bar = value;
    console.log('do whatever you want to do here.');
  }
}

const f = new Foo();
f.bar = 'hey'; // direct assignment invokes the setter

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 370779

You could make the camera property private by putting # in front of it, ensuring that only CameraBuilder's internals can reference it:

class CameraBuilder {
    #camera = {};
    constructor() {
        if (arguments.length) {
            throw new Error('[CameraBuilder constructor ERROR] class constructor does not accept parameters.');
        }
    }

    withFarmLabel(farmLabel) {
        this.#camera.farm_label = farmLabel;
        return this;
    }

    // more methods here

    build() {
        const missingProps = [];
        if (!this.#camera.farm_label) {
            missingProps.push('\nMissing farm_label property. Use the withFarmLabel method in order to assign it.');
        }
        
        // more validations like the one above here

        if (missingProps.length) {
            const errorMsg = missingProps.join('');
            throw new Error(`[CameraBuilder build ERROR] ${errorMsg}`);
        }

        return this.#camera;
    }
}

const c = new CameraBuilder();
c.withFarmLabel('label');
console.log(c.camera);
console.log(c.build().farm_label);

Upvotes: 3

Related Questions