Reputation: 55
abstract class Animal {
abstract sound(): void;
}
class Cat extends Animal {
sound(){
console.log("meow");
}
}
class Dog extends Animal {
sound(){
console.log("bark");
}
}
enum AnimalType {
Cat = 1,
Dog = 2,
}
class AnimalFactory {
static createAnimal(type: AnimalType): Animal {
switch (type) {
case AnimalType.Cat:
return new Cat();
case AnimalType.Dog:
return new Dog();
default:
throw new Error("unsupported animal type");
}
}
}
const cat = AnimalFactory.createAnimal(AnimalType.Cat);
const dog = AnimalFactory.createAnimal(AnimalType.Dog);
cat.sound();
dog.sound();
I'm trying to get better at the Open–closed principle and I'm trying to figure out how to avoid using switch statements and decouple the factory class with the enum when I add more subclasses of Animal. The user via an API is sending over what animal is to be created via a simple enum. Is it possible to modify the AnimalFactory in a way that I don't need to modify the factory class every time I add another enum value and subclass of Animal?
Am I going at this wrong and I need more than an enum to use as an argument?
Upvotes: 2
Views: 699
Reputation: 97302
You can define a constant that contains a mapping of enum
values to class types, and use a mapped type to create a type mapping:
abstract class Animal {
abstract sound(): void;
}
class Cat extends Animal {
sound() {
console.log("meow");
}
}
class Dog extends Animal {
sound() {
console.log("bark");
}
}
enum AnimalType {
Cat = 1,
Dog = 2,
}
const animals = {
[AnimalType.Cat]: Cat,
[AnimalType.Dog]: Dog
};
type Animals = {
[A in keyof typeof animals]: InstanceType<typeof animals[A]>
};
class AnimalFactory {
static createAnimal<A extends keyof Animals>(type: A): Animals[A] {
return new animals[type]();
}
}
const cat = AnimalFactory.createAnimal(AnimalType.Cat);
const dog = AnimalFactory.createAnimal(AnimalType.Dog);
cat.sound();
dog.sound();
See it on the playground
Upvotes: 2