Who is responsible for creating part of main class? - javascript

There is a major class Panorama with parts as composition PanoramaViewOver.
class Panorama {
panoramaViewOver: PanoramaViewOver | null;
}
Who is responsible to create a class PanoramaViewOver if needed?
I can create wrapper class that creates a new Panorama() and creates a PanoramaViewOver():
class Creator {
panorama: Panorama;
createPanorama() {
this.panorama = new Panorama();
}
createPanoramaViewOver() {
const view = new PanoramaViewOver();
if(this.panorama) this.panorama.view = view;
}
}
Another way is create a public method createPanoramaViewOver() inside main class Panorama().
What is a better way?

Instead of createPanorama() and createPanoramaViewOver() I would use create() which implicitly creates a Panorama instance and createWithPanorama(Panorama panorama) which takes an already initialized Panorama object.
Both methods would need to be static resembling a factory method pattern.
This way you can set panorama.view outside of the Creator class.
It would look like this:
class Creator {
private _panorama: Panorama | null;
constructor(panorama : Panorama | null) {
this._panorama = panorama;
}
static create() {
return new Creator(new Panorama());
}
static createWithPanorama(panorama : Panorama | null) {
return new Creator(panorama);
}
}
You would use it like this:
let creator = Creator.create();
let creatorWithoutPanorama = Creator.createWithPanorama(null);
let p = new Panorama();
p.view = /* something */;
let creatorWithPanoramaViewOver = Creator.createWithPanorama(p);
It's not idiomatic to initialize each member of a class with different methods.
You usually do that in one place only, most of the time in the constructor.

Related

Calling constructor of parent class in static method of child class

I got a problem with calling super inside of the child static method.
const _todos = [];
class Todo {
constructor(title) {
this.title = title;
this.id = Math.round(Math.random() * 100);
_todos.push({title, id: this.id});
console.log(`Todo ID: ${this.id}. DONT FORGET IT!`);
}
static TodoerVersion = '1.8';
static removeTodo(id) {
const todoIndex = _todos.findIndex(t => t.id == id);
_todos.splice(todoIndex, 1);
}
}
class TodoV2 extends Todo {
static addTodo(title) {
super(title);
}
static addDescription(todo, description) {
todo.description = description;
}
static TodoerVersion = '2.0';
};
new TodoV2("play guitar");
Why does it not work?
But if i call super in normal method, it would works just fine.
super is only callable within the constructor function of a class.
Two answers for you:
The question you actually asked.
What I think you should do instead. :-)
Answering the question you asked:
JavaScript is very special in this regard: this has meaning in static methods (as long as you call them correctly, e.g. TodoV2.addTodo("title")): it's the constructor function you called the static method on. And a subclass constructor function inherits from its superclass constructor function (yes, really). So you can access the constructor function for the parent class using Object.getPrototypeOf(this) in the static method:
// This is something unusual that JavaScript actually does support
static addTodo(title) {
const ctor = Object.getPrototypeOf(this);
return new ctor(title);
}
To handle the case where the user may have called addTodo in a way that doesn't set this, you might do something like this to default to TodoV2 if this is undefined:
static addTodo(title) {
const ctor = Object.getPrototypeOf(this ?? TodoV2);
return new ctor(title);
}
What I think you should do instead
You shouldn't be using a static method for this. Instead, define a constructor for TodoV2:
class TodoV2 extends Todo {
constructor(title, description) {
super(title);
this.description = description ?? "";
}
static addTodo(title) { // Why no `description`?
return new this(title); // Or `new TodoV2(title)`
}
}
You might also look into the Symbol.species pattern if you want subclasses to create instances of superclasses.

How to simulate polymorphism in typescript?

I am trying to simulate the OOP concept in typescript. I am from a Java background. The problem I am facing is, I am trying to create a Board game. I have Celltypes like Hotel,Jail,Treasure and so on. They all have common attribute called amount of type number. So I created an interface called CellType and implemented it to the individual classes mentioned above(Hotel,Treasure so on)
There is a class called Board where I want to initialise and declare an array of predefined celltypes. Something like this :
var cell[:CellType]; //declaration
Hotel = new Hotel(200);
cell = [Hotel1,Treasure1,Jail1,Hotel2..] //initialization
In java we can do :
interface CellType{ public integer amount};
class Hotel implements CellType;
class Jail implements Celltype;
// in main class
ArrayList<CellType> cellTypes = new ArrayList<CellType>;
Hotel Taj = new Hotel();
cellTypes.add(Taj);
Jail jail = new Jail();
cellTypes.add(jail);
So, how can I declare an array of multiple child classes that are inheriting the same superclass the way we do it in Java?
Interface/classes:
interface CellType { amount: number };
class Hotel implements CellType {
// This concise syntax creates the public property
// and assigns to it the value passed into the
// constructor
constructor(public amount: number) { }
}
class Jail implements CellType {
constructor(public amount: number) { }
}
Usage:
let cellTypes: Array<CellType> = []; // Or let cellTypes: CellType[] = [];
let taj = new Hotel(100);
cellTypes.push(taj);
let jail = new Jail(200);
cellTypes.push(jail);
You can be more concise like this:
let cellTypes: Array<CellType> = [new Hotel(100), new Jail(200)];

Class Inheritance for members

I have an Octree class. A key feature of an Octree is that it can create its own children.
class Octree {
...
createChildren(){
...
/* for each of the 8 new children*/
this.children.push(new Octree(/*someargs*/))
...
}
}
Now I want to inherit off of the Octree class, however, I also want the children to become the inherited class. For example class LODWorldTree extends Octree, to additionally contain some renderer data for a game. However, if I call LODWorldTree.createChildren(), then LODWorldTree.children will be an array of Octrees instead of LODWorldTrees.
What is the best way to fix this problem? While writing this it occured to I could store Octree.myClass = /*some inherited class*/, and manually set this variable for all classes that inherit from Octree. Is there a better way to do something like this? Maybe with this.prototype?
You can utilize the fact that each object has a reference to it's own constructor via the prototype:
class A {
constructor() {
this.val = 1;
this.children = [];
this.typeName = `I'm A`;
}
addSelfChild() {
this.children.push(new this.constructor(this.val + 1));
}
}
let a = new A(1);
a.addSelfChild();
a.addSelfChild();
console.dir(a);
class B extends A {
constructor(val) {
super(val);
this.typeName = `I'm B`;
}
}
let b = new B(1);
b.addSelfChild();
b.addSelfChild();
console.dir(b);
Try to use constructor attribute:
this.children.push(new this.constructor(/*someargs*/));
this.constructor is the reference for constructor for current object, so invoking it will produce new instance of the same class

Re-initialize a class instance without creating a new instance?

I'm wondering if there is a standard way to re-initialize, or re-construct a class instance without creating a new instance all together.
Let's say I have a TestClass instance:
class TestClass {
constructor() {
this.x=0;
this.y=50;
this.z=200;
}
}
var testClassInstance=new TestClass();
And basically, overtime I tweak some of it's values.
testClassInstance.x+=250;
testClassInstance.y-=20;
Then later on I want to reset all of its values to whatever was defined when the instance was created. I'm wondering if there is a way to then basically reinitialize it, without creating an entirely new instance?
Is something like
testClassInstance.constructor()
safe and reliable?
class TestClass {
constructor() {
this.reset();
}
reset(){
this.x=0;
this.y=50;
this.z=200;
}
}
const myTestClass = new TestClass();
myTestClass.x = 5;
console.log(myTestClass.x); // 5
myTestClass.reset();
console.log(myTestClass.x); // 0
This answer was generated because of the first version of this question.
Your class is never modified. The class is an implementation, what you modify are the instances created using that implementation.
Look this code snippet:
class TestClass {
constructor() {
this.x=0;
this.y=50;
this.z=200;
}
}
var testClassInstance=new TestClass();
testClassInstance.x+=250;
testClassInstance.y-=20;
console.log(testClassInstance.x);
console.log(testClassInstance.y);
var anotherTestClassInstance=new TestClass();
console.log(anotherTestClassInstance.x);
console.log(anotherTestClassInstance.y);
See? the new object has the initial values declared in TestClass's constructor.

Create class with arguments or call its methods separately

How JS code should be structered when instantiating new classes inside controller class Main.
Solutions:
A: pass arguments while creating new class - new Options(args) - and let Options's constructor call its own methods.
B: create new class and call the classes' methods on the object.
Later I'd use properties from Options in another classes.
// A
class Main {
constructor(options) {
this.options = new Options(options);
{ firstProperty, secondProperty } = this.options;
this.another = new Another(firstProperty, secondProperty);
}
}
// B
class Main {
constructor(options) {
this.options = new Options();
const firstProperty = this.options.methodA(options);
const secondProperty = this.options.methodB(options);
this.another = new Another();
const anotherPropety = this.another.methodA(firstProperty);
(...)
}
}
For the purposes of decoupling I would suggest a third option.
//main.js
class Main {
constructor(options) {
this.options = options;
// all instances of class would have:
this.options.foo = 'bar'
}
method() {
return `${this.options.foo} - ${this.options.setup}`
}
}
// example.js
const options = new Options({setup: 'options'});
const example = new Main(options);
console.log(example.method());
This lets your inject your dependencies into a given class, which makes writing tests for your code far simpler. It also gives you the benefit of (as long as you maintain a common interface) swapping out Options for NewAwesomeOptions at some later point without having to find everywhere you might have hard coded it into a class.

Categories