'Switch methods in object using protoype
I have a piece of code that switches the processing used on an object, a, outside of a function test. The function, test, uses properties of a and a.b and a.c:
let a;
let fB = {method: function(o){o.b.count++;return o.b.value}};
let fC = {method: function(o){o.c.count++;return o.c.value}};
a = new class A {value='Method:'; b={value:'B',count:0}; c={value:'C',count:0}};
function test (a,f) {
console.log(a.value+f.method(a));
}
test(a,fB); // method:B
test(a,fC); // method:C
console.log('Counts:',a.b.count,a.c.count) // Counts: 1 1
I want to write it in a more OO way, something like :
function test (a) {
console.log(a.value+a.method());
}
class B {
constructor(){this.val='B';this.count=0}
method (){this.count++;return this.val}
}
class C {
constructor(){this.val='C';this.count=0}
method (){this.count++;return this.val}
}
class A {
value='Method:';
}
a = new A();
Object.setPrototypeOf(a,new B());
test(a); // method:B
Object.setPrototypeOf(a,new C());
test(a); // method:C
console.log('Counts:',a.b.count,a.c.count) // a.b is undefined
The best I could come up with was :
class B {
constructor(){this.value='B';this.count=0}
method (){this.count++;return this.value}
}
class C {
constructor(){this.value='C';this.count=0}
method (){this.count++;return this.value}
}
class A {
value='Method:';
b=new B();
c=new C();
x=this.b;
switchToB(){this.x = this.b}
switchToC(){this.x = this.c}
method(){return this.x.method()}
}
a = new A();
function test (a) {
console.log(a.value+a.method());
}
test(a); // method:B
a.switchToC();
test(a); // method:C
console.log('Counts:',a.b.count,a.c.count) // Counts: 1 1
I don't much like this, nor an alternative :
class B {
constructor(a){this.value=a.value;this.val='B';this.count=0}
method (){this.count++;return this.val}
}
class C {
constructor(a){this.value=a.value;this.val='C';this.count=0}
method (){this.count++;return this.val}
}
class A {
value='Method:';
b=new BB(this);
c=new CC(this);
}
a = new A();
test(a.b); // method:B
test(a.c); // method:C
console.log('Counts:',a.b.count,a.c.count) // Counts: 1 1
Is there a better way to do this in JS ?
Solution 1:[1]
Looking at the first OO attempt that you provided, there are these observations:
- No
borcproperties are created ona, yet the testing code needs them. - Also the switching mechanism should work with a reference to these
bandcproperties BandCare look-alikes so that could be generalised to oneCounterclass whose constructor takes an argument that distinguishes them (determining thevalueproperty).- We could also think of a more lazy initialisation, where a counter object is only created when
aswitches to it. - There seems to be a relationship between the name "b" and the output "B". Similar between "c" and "C". So one could be derived from the other.
This leads to the following implementation:
class Counter {
constructor(value) {
this.value = value.toUpperCase();
this.count = 0;
}
method() {
this.count++;
return this.value;
}
}
class A {
constructor() {
this.value = 'Method:';
}
activateCounter(name) {
// Create a new counter if not used before
this[name] ??= new Counter(name);
// Redirect the method to use this counter's method
this.method = () => this[name].method();
}
}
function test(a) {
console.log(a.value + a.method());
}
let a = new A();
a.activateCounter("b");
test(a); // method:B
a.activateCounter("c");
test(a); // method:C
console.log('Counts:', a.b.count, a.c.count) // Counts: 1 1
Here a.method() should only be called after a counter was activated with a call of a.activateCounter. This is because the latter will (re)define the method method on a.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | trincot |
