lundi 8 août 2022

Dependency injection and method restriction

I have a class that manages a WebSocket connection and provides the methods to subscribe to this WebSocket. An instance of this class may be passed to many child objects that subscribe to this WebSocket via these methods:

const client = new WebSocketManager();
await client.connect(); 

const objA = new ClassA(client);
const objB = new ClassB(client);

client.close(); // at a future point in time

The problem is that client exposes critical methods like connect and close. If objA calls close, objB would fail for no apparent reason. Those two methods should be private to the children, but public to the initiator of the parent object. Is there a pattern to solve this problem? Two methods are presented below. Are any of those two patterns acceptable or are there better solutions? I would like to hear your opinion.

Method 1: Define an owner of the parent object. Restricted methods require a reference to the owner.

class Parent {
    #owner;

    constructor(owner) {
        this.#owner = owner;
    }

    restricedMethod(owner) {
        if(owner !== this.#owner)
            throw Error('not authorized');

        console.log('restricedMethod called');
    }

    accessibleMethod() {
        console.log('accessibleMethod called');
    }
}

class Child {
    constructor(dependency) {
        this.dependency = dependency;
    }

    callAccessibleMethod() {
        this.dependency.accessibleMethod();
    }

    callRestricedMethod() {
        this.dependency.restricedMethod();
    }
}



const parent = new Parent(this);
// 'this' is the owner of 'parent'

const child = new Child(parent);

child.callAccessibleMethod();
// accessibleMethod called

try { child.callRestricedMethod(); } catch(e) { console.log(e.message); }
// Error: not autherized

parent.restricedMethod(this); // only the owner can call this method
// restricedMethod called

Method 2: Create a new parent object on the fly that only contains the methods that the child may access.

class Parent {
    restricedMethod(){
        console.log('restricedMethod called');
    }

    accessibleMethod(){
        console.log('accessibleMethod called');
    }
}

class Child {
    constructor(dependency) {
        this.dependency = dependency;
    }

    callAccessibleMethod() {
        this.dependency.accessibleMethod();
    }

    callRestricedMethod() {
        this.dependency.restricedMethod();
    }
}

class MethodSelection {
    constructor(object, methods) {
        for (const method of methods)
            this[method] = object[method];
    }
}



const parent = new Parent();

// select methods from parent object and create new object
const restricted = new MethodSelection(parent, ['accessibleMethod']);

console.log(restricted.accessibleMethod === parent.accessibleMethod);
// true

const child = new Child(restricted);

child.callAccessibleMethod();
// accessibleMethod called

try { child.callRestricedMethod(); } catch(e) { console.log(e.message); }
// TypeError: this.dependency.restricedMethod is not a function

parent.restricedMethod();
// restricedMethod called

Aucun commentaire:

Enregistrer un commentaire