Need help in OOP

class Shape {
radius: number;
length: number;
breadth: number;
base: number;
height: number;

constructor(radius: number) {
this.radius = radius;
this.length = 0;
this.breadth = 0;
this.base = 0;
this.height = 0;
}
}

class Circle extends Shape {
constructor(radius: number) {
super(radius);
}

getArea(): number {
return Math.PI * this.radius * this.radius;
}
}

class Rectangle extends Shape {
constructor(length: number, breadth: number) {
super(0);
this.length = length;
this.breadth = breadth;
}

getArea(): number {
return this.length * this.breadth;
}
}

class Triangle extends Shape {
constructor(base: number, height: number) {
super(0);
this.base = base;
this.height = height;
}

getArea(): number {
return (this.base * this.height) / 2;
}
}

const circle = new Circle(10);
const radius = new Rectangle(3, 4);
const triangle = new Triangle(5, 6);

console.log(circle.getArea());
console.log(radius.getArea());
console.log(triangle.getArea());
class Shape {
radius: number;
length: number;
breadth: number;
base: number;
height: number;

constructor(radius: number) {
this.radius = radius;
this.length = 0;
this.breadth = 0;
this.base = 0;
this.height = 0;
}
}

class Circle extends Shape {
constructor(radius: number) {
super(radius);
}

getArea(): number {
return Math.PI * this.radius * this.radius;
}
}

class Rectangle extends Shape {
constructor(length: number, breadth: number) {
super(0);
this.length = length;
this.breadth = breadth;
}

getArea(): number {
return this.length * this.breadth;
}
}

class Triangle extends Shape {
constructor(base: number, height: number) {
super(0);
this.base = base;
this.height = height;
}

getArea(): number {
return (this.base * this.height) / 2;
}
}

const circle = new Circle(10);
const radius = new Rectangle(3, 4);
const triangle = new Triangle(5, 6);

console.log(circle.getArea());
console.log(radius.getArea());
console.log(triangle.getArea());
is there a better way to do this using OOP? this feels a bit inconsistent to me but im inexperienced in OOP and don't know if there's a better way to do this. would love some help!
42 Replies
Brendonovich
Brendonovich3y ago
I think a more OOP style approach would be for Shape to have abstract function getArea(): number; and the extending classes would define the properties themselves - it doesn’t really make sense for a rectangle to have a radius Imo my library Sceneify is a decent demonstration of OOP with abstract classes and inheritance
nexxel
nexxelOP3y ago
interesting how would that look like in code? i've never seen an abstract function
Brendonovich
Brendonovich3y ago
It’s like putting a function on a trait in rust You do everything except add the curly braces and body of the function Funnily enough, this example uses the abstract class like a trait
nexxel
nexxelOP3y ago
hmm, it says abstract methods can only appear in abstract classes need to learn this lol
Brendonovich
Brendonovich3y ago
Ah yeah you’ll need to make the class abstract too An abstract class can’t be instantiated, but classes that inherit it can
nexxel
nexxelOP3y ago
abstract class Shape {
radius: number;
length: number;
breadth: number;
base: number;
height: number;

constructor(radius: number) {
this.radius = radius;
this.length = 0;
this.breadth = 0;
this.base = 0;
this.height = 0;
}

abstract getArea(): number;
}

class Circle extends Shape {
constructor(radius: number) {
super(radius);
}

getArea(): number {
return Math.PI * this.radius * this.radius;
}
}

class Rectangle extends Shape {
constructor(length: number, breadth: number) {
super(0);
this.length = length;
this.breadth = breadth;
}

getArea(): number {
return this.length * this.breadth;
}
}

class Triangle extends Shape {
constructor(base: number, height: number) {
super(0);
this.base = base;
this.height = height;
}

getArea(): number {
return (this.base * this.height) / 2;
}
}

const circle = new Circle(10);
const radius = new Rectangle(3, 4);
const triangle = new Triangle(5, 6);

console.log(circle.getArea());
console.log(radius.getArea());
console.log(triangle.getArea());
abstract class Shape {
radius: number;
length: number;
breadth: number;
base: number;
height: number;

constructor(radius: number) {
this.radius = radius;
this.length = 0;
this.breadth = 0;
this.base = 0;
this.height = 0;
}

abstract getArea(): number;
}

class Circle extends Shape {
constructor(radius: number) {
super(radius);
}

getArea(): number {
return Math.PI * this.radius * this.radius;
}
}

class Rectangle extends Shape {
constructor(length: number, breadth: number) {
super(0);
this.length = length;
this.breadth = breadth;
}

getArea(): number {
return this.length * this.breadth;
}
}

class Triangle extends Shape {
constructor(base: number, height: number) {
super(0);
this.base = base;
this.height = height;
}

getArea(): number {
return (this.base * this.height) / 2;
}
}

const circle = new Circle(10);
const radius = new Rectangle(3, 4);
const triangle = new Triangle(5, 6);

console.log(circle.getArea());
console.log(radius.getArea());
console.log(triangle.getArea());
like this?
Brendonovich
Brendonovich3y ago
Ye The reason you’d do it this way is that now you can make a function accept Shape and know that the getArea function will exist
nexxel
nexxelOP3y ago
ohh that makes sense now
Brendonovich
Brendonovich3y ago
If you move all the properties from Shape to the extending classes, you could turn it into an interface
nexxel
nexxelOP3y ago
yeah im trying to code up both approaches to this one using adts and one using oop
// adts
interface Circle {
type: "circle";
radius: number;
}

interface Rectangle {
type: "rectangle";
length: number;
breadth: number;
}

interface Triangle {
type: "triangle";
base: number;
height: number;
}

type Shape = Circle | Rectangle | Triangle;

const getArea = (shape: Shape) => {
switch (shape.type) {
case "circle":
return Math.PI * shape.radius * shape.radius;
case "rectangle":
return shape.length * shape.breadth;
case "triangle":
return (shape.base * shape.height) / 2;
}
};

const c: Circle = { type: "circle", radius: 10 };
const r: Rectangle = { type: "rectangle", length: 3, breadth: 4 };
const t: Triangle = { type: "triangle", base: 5, height: 6 };

console.log(getArea(c));
console.log(getArea(r));
console.log(getArea(t));
// adts
interface Circle {
type: "circle";
radius: number;
}

interface Rectangle {
type: "rectangle";
length: number;
breadth: number;
}

interface Triangle {
type: "triangle";
base: number;
height: number;
}

type Shape = Circle | Rectangle | Triangle;

const getArea = (shape: Shape) => {
switch (shape.type) {
case "circle":
return Math.PI * shape.radius * shape.radius;
case "rectangle":
return shape.length * shape.breadth;
case "triangle":
return (shape.base * shape.height) / 2;
}
};

const c: Circle = { type: "circle", radius: 10 };
const r: Rectangle = { type: "rectangle", length: 3, breadth: 4 };
const t: Triangle = { type: "triangle", base: 5, height: 6 };

console.log(getArea(c));
console.log(getArea(r));
console.log(getArea(t));
Brendonovich
Brendonovich3y ago
huh Would never have thought to do that example in that way but yeah it works
nexxel
nexxelOP3y ago
what were you thinking
Brendonovich
Brendonovich3y ago
Problem is that it couples Shape to the three specific shapes
nexxel
nexxelOP3y ago
yeah you're gonna have to add other shapes manually
Brendonovich
Brendonovich3y ago
And if this was a library there’d be no way to add more shapes You also lose being able to rely on object prototypes and instanceof
nexxel
nexxelOP3y ago
what do you think is a better approach then?
Brendonovich
Brendonovich3y ago
I think having shape as an abstract class is better for something like this I’m generally against OOP but in some cases it just makes sense For TS that is, Rust is a different story just bc of how the lang works
nexxel
nexxelOP3y ago
here's ocaml
type shape =
| Circle of float
| Rectangle of float * float
| Triangle of float * float

let area s = match s with
| Circle r -> 3.14 *. r *. r
| Rectangle (l, b) -> l *. b
| Triangle (b, h) -> b *. h /. 2.0;;
type shape =
| Circle of float
| Rectangle of float * float
| Triangle of float * float

let area s = match s with
| Circle r -> 3.14 *. r *. r
| Rectangle (l, b) -> l *. b
| Triangle (b, h) -> b *. h /. 2.0;;
so satisfying lol
Brendonovich
Brendonovich3y ago
Is very nice haha But again you can’t extend shape once you’ve defined it
nexxel
nexxelOP3y ago
yeah well thats just the fp approach i guess tradeoffs
Brendonovich
Brendonovich3y ago
Hmm I don’t think that’s an inherently fp thing Surely in fp u can define types that stuff adheres to like an interface
nexxel
nexxelOP3y ago
sure, i meant ADTs i think this example isn't great lol prolly should think of something else
Brendonovich
Brendonovich3y ago
Ah ok for ADTs you’re right
Brendonovich
Brendonovich3y ago
Take a look at this abstract class: it’s useless on its own, but Scene and Input both add functionality on top https://github.com/Brendonovich/sceneify/blob/main/packages/core/src/Source.ts
GitHub
sceneify/Source.ts at main · Brendonovich/sceneify
The simplest way to control OBS from JavaScript. Contribute to Brendonovich/sceneify development by creating an account on GitHub.
nexxel
nexxelOP3y ago
thats really helpful thanks
Brendonovich
Brendonovich3y ago
In addition to that, some functions accept either Scenes or Inputs since they’re just Sources, but I’m still able to check the specific type using instanceof (line 207): https://github.com/Brendonovich/sceneify/blob/main/packages/core/src/Scene.ts
GitHub
sceneify/Scene.ts at main · Brendonovich/sceneify
The simplest way to control OBS from JavaScript. Contribute to Brendonovich/sceneify development by creating an account on GitHub.
nexxel
nexxelOP3y ago
i get why you'd use abstract now
Brendonovich
Brendonovich3y ago
It just so happens that modelling stuff from OBS in an OOP fashion works really well
nexxel
nexxelOP3y ago
yeah thats cool oop is a good abstraction for most ui related things imo guis for example
Brendonovich
Brendonovich3y ago
Do u prefer retained or immediate mode guis?
nexxel
nexxelOP3y ago
idk what that is 😄
Brendonovich
Brendonovich3y ago
Immediate: react, imgui. Each render discards the previous ui node tree and produces a new one Retained: Qt, wxwidgets. UI elements are distinct object in memory that hold state and are traversed each render
nexxel
nexxelOP3y ago
ohhh idk what i prefer cause i've never built guis
Brendonovich
Brendonovich3y ago
U write react 💀
nexxel
nexxelOP3y ago
but i'd probably use immediate? i mean lets say you're building a game is the immediate mode really suited for that
Brendonovich
Brendonovich3y ago
It can be Some games use stuff like imgui for their ui Though if u do ui through unity or unreal it’s probs retained
nexxel
nexxelOP3y ago
that makes sense everyday you learn something new haha
Brendonovich
Brendonovich3y ago
Have u had your game developer phase yet
nexxel
nexxelOP3y ago
not yet
Brendonovich
Brendonovich3y ago
Damn
nexxel
nexxelOP3y ago
i've only finished 1 year of coding this month lol very beginner
Brendonovich
Brendonovich3y ago
Oh bruh u killing it
Want results from more Discord servers?
Add your server