In this example we abstract a Card class so we can have Playing cards or Joker Cards
If we were to go further we could create a Game class that accepted one or more of the Card types depending on the game, blackjack, poker, war, etc…
Interfaces are not required for abstract classes, but can be useful for setting up a root blueprint
interface ICard {
value: number;
getValue: ()=>number;
compareTo: (o: ICard) => boolean;
toString: () => string;
}
Abstract class can instantiate its own methods and variables or declare them as required for classes that extend from it.
abstract class Card implements ICard {
value: number;
constructor(value: number){
this.value = value;
}
getValue(){
return this.value;
}
compareTo(o: Card){
return this.getValue() > o.getValue();
}
// abstract keyword is used to tell classes that extend this class
// that they have to implement this method
abstract toString(): string;
}
// Enum is just for setting the type in the next class PlayingCard
enum SUIT {
Diamonds,
Clubs,
Spades,
Hearts,
}
Extending an abstract class means we only have to implement the methods of Card that are marked abstract after that we can customize the methods to fit our needs
class PlayingCard extends Card {
suit: SUIT;
constructor(value: number, suit: string){
// since we are extending an abstract class we need to instantiate the
// Card class in the constructor with super();
super(value);
this.suit = this.convertStringToSuit(suit);
}
private convertStringToSuit(suit: string){
switch(suit){
case "Diamonds":
return SUIT.Diamonds;
case "Clubs":
return SUIT.Clubs;
case "Spades":
return SUIT.Spades;
case "Hearts":
return SUIT.Hearts;
default:
throw new Error();
}
}
private convertSuitToString(){
switch(this.suit){
case SUIT.Diamonds:
return "Diamonds";
case SUIT.Clubs:
return "Clubs";
case SUIT.Spades:
return "Spades";
case SUIT.Hearts:
return "Hearts";
default:
return "";
}
}
toString(): string {
return String(this.getValue()) + " of " + this.convertSuitToString();
}
}
A new class based on Card class that has a unchanging value, but its own color method
class JokerCard extends Card {
color: string;
constructor(color: string){
super(14);
this.color = color;
}
toString(): string {
return String(this.value) + " " + this.color;
}
}
Instantiating and using both of the classes example
Card is the Abstract class so now we can technically store multiple types of Card objects into a single array
const cards: Card[] = [];
cards.push(new PlayingCard(1, "Diamonds"));
cards.push(new JokerCard("Black"))
Due to shared Logic between the abstract class Card and PlayingCard and JokerCard the compareTo method is available for comparing the playingcard and JokerCard class
console.log(cards[0].compareTo(cards[1]));
// returns false as 1 is not greater then 14
The toString method is also allowed no matter which class object as both were required to implement it.
console.log(cards[0].toString())
// prints "1 of Diamonds"
console.log(cards[1].toString())
// prints "14 Black"
I’ve also implemented this in an online editor to see the working example and edit it goto Card abstractions
Top comments (0)