Preface - I have looked at similar posts to this one on SO, but the answers did not seem applicable.
I have an abstract class with the following method (simplified):
playAnimation() {
let animator = this.createAnimator(this.animatorData)
await animator.animate()
}
Create animator has the following definition inside that abstract class:
abstract createAnimator(animatorData: Object): Animator
A subclass might implement createAnimator
like this:
createAnimator(animatorData: StandardAnimatorData) {
return new RiverAnimator(animatorData.addMessage, animatorData.assetsDir)
}
or like this:
createAnimator(animatorData: StandardAnimatorData) {
return new BridgeAnimator(animatorData.addMessage, animatorData.assetsDir)
}
As you can see - both of the sub classes's implementations of createAnimator
are roughly identical, except for the type of the Animator
being returned.
Sidenote BridgeAnimator
and RiverAnimator
both implement the Animator
interface, but if the solution requires Animator
to be an abstract class, I can change it.
Is there a way to move createAnimator
into the abstract base class?
Ideally the abstract class would have an abstract variable that is the type of the class that createAnimator
should return. Subclasses would merely implement that variable. Then createAnimator
would use that variable to return the proper type of Animator
.
Yes, you can do something like this (I made one big code snippet so I can verify that everything compiles):
interface Animator {
animate(): Promise<void>;
}
class RiverAnimator implements Animator {
async animate() { }
constructor(addMessage: unknown, assetsDir: unknown) { }
}
class BridgeAnimator implements Animator {
async animate() { }
constructor(addMessage: unknown, assetsDir: unknown) { }
}
interface StandardAnimatorData {
addMessage: unknown;
assetsDir: unknown;
}
// Common interface for the constructor functions RiverAnimator and BridgeAnimator
interface AnimatorConstructor {
new(addMessage: unknown, assetsDir: unknown): Animator;
}
abstract class AbstractOuterClass {
abstract animatorConstructor: AnimatorConstructor;
animatorData: StandardAnimatorData;
createAnimator(data: StandardAnimatorData) {
return new this.animatorConstructor(data.addMessage, data.assetsDir);
}
async playAnimation() {
let animator = this.createAnimator(this.animatorData);
await animator.animate();
}
}
class SubOuterClass1 extends AbstractOuterClass {
animatorConstructor = RiverAnimator;
}
class SubOuterClass2 extends AbstractOuterClass {
animatorConstructor = BridgeAnimator;
}
But it's not clear to me that this is any better than implementing createAnimator
in SubOuterClass1
and SubOuterClass2
.