Polymorphism to the rescue
Polymorphism is one of the core principles of Object-Oriented Programming (OOP). It allows objects of different classes to be treated as objects of a common superclass. The most common use of polymorphism is when a method in a subclass overrides a method in its superclass.
Here’s my situation
I was working in a TypeScript codebase where I have a class extending a base class which represents an HTTP resource.
Call that class AdminService. Now the designers of the class did not make it easy for me to pass custom headers or override existing ones.
The base class Resource which ErrorResource extends has a method getHeaders() which returns a map of headers to be sent with every request.
After some very dumb attempts such as this one:
export default class AdminService extends ErrorResource {
constructor() {
super(config.admin);
/...
}
getHeaders() {
return super.getHeaders().then((headers) => {
const { xAccessToken } = JSON.parse(sessionStorage.getItem('params'));
headers.set('XToken', xAccessToken);
return headers;
});
}
doSomeStuff(newState: lolStateEnum) {
const { clientNode, xAccessToken } = JSON.parse(sessionStorage.getItem('params'));
return from(
this.getHeaders().then((headers) => {
headers.set('XToken', xAccessToken);
return this.PUT(`clients/${clientNode}/lol?lol=${newState}`, {}, headers);
})
);
}
}
This wouldn’t work because this.getHeaders() creates a new instance of headers with each PUT call.
async PUT<TResult, TPayload>(
endpoint: string,
payload: TPayload,
noHandler = false
): Promise<TResult & T204Response> {
try {
const response = await makeCall<TResult & T204Response>(
`${this.base}/${endpoint}`,
METHOD.PUT,
payload,
await this.getHeaders(), // <--- here
this.host,
this.timeout
);
RunTime.digest();
return response;
} catch (e) {
this.catchHandler(e, METHOD.PUT, noHandler);
}
}
and here’s where in getHeaders we create a new instance of headers:
...
const headers = new Headers();
...
So I thought wait, we could use the power of polymorphism here.
We could override the getHeaders method in the AdminService class to return the headers we want. Simply extending the original headers with our custom headers.
This is because this.PUT will use this.getHeaders() internally to get the headers to be sent with the request. We want it to use the overridden version in AdminService.
So I went to work and confirmed my theory with something such as this:
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return "Yo " + this.name;
}
public void doThing() {
IO.println(getName());
}
}
class BritishPerson extends Person {
BritishPerson(String name) {
super(name);
}
public String getName() {
return super.getName() + " Innit";
}
}
void main() {
BritishPerson person = new BritishPerson("John");
person.doThing();
}
I was a really rusty. So Co-Pilot was able to confirm this is indeed how OOP works. I was like thank the OOP Gods. When I ran the above code, I got the output:
Yo John Innit
This worked just how I wanted it, my overridden method was able to be called and hence solved my issue.
Summary
So polymorphism can allow us to override methods in subclasses to replace or supplement the behavior of the base class methods. Sometimes we forget the power of OOP principles such as polymorphism, inheritance, and encapsulation.