Home > OS >  Implementing kind of a delegation pattern in Javascript
Implementing kind of a delegation pattern in Javascript

Time:11-14

I have two classes, A and B. What I am trying to do is to pass data from A to B after receiving a message from sockets.

This is simplified look of how classes are defined:

class A:

export default class A {
  client;
  callbacks;
 
  constructor() {
    this.callbacks = {
      open: () => this.client.logger.debug('open'),
      close: () => this.client.logger.debug('closed'),
      message: (data) => {this.client.logger.log(data)}, //I want to pass this data object to class B
    };
    
    this.client = new Spot(constants.apiKey, constants.apiSecret, {
      baseURL: constants.baseURL,
      wsURL: constants.wsURL,
    });

    this.client.userData(listenKey, this.callbacks);

  }
}

I already have a property of A in class definition of B:

export default class B {
  account;

  constructor() {
    this.account = new A();

    
  }
}

What would be a correct/standard way to connect these two so I get a 'data' object from class A every time the socket message callback from class A is triggered?

I am a bit new with JS, but on iOS we would use a delegation pattern, with a protocol, that says:

  1. class A will have a delegate property.
  2. A delegate (class B) must implement a protocol (in this case it would be a requirement to implement method called didReceiveMessage(data).
  3. After that, when a message is received in class A, we would just do(in socket message callback shown above) something like this.delegate.didReceiveMessage(data).

Protocol usage here is not important generally, but it is a plus, cause from A class, we can only access didReceiveData(data) method trough a delegate property, and nothing else (other properties / methods of class B are not visible). At least that is how it works in Swift/Obj-C. I just mentioned it, cause I am curious is this how it is done in JS too.

I guess there is some similar mechanism in Javascript, or some more standard/better way to achieve this kind of data sending between objects?

CodePudding user response:

on iOS we would use a delegation pattern, with a protocol

You can do it exactly as you described:

export default class A {
  client;
  delegate;
 
  constructor(delegate) {
    this.delegate = delegate;
    
    this.client = new Spot(constants.apiKey, constants.apiSecret, {
      baseURL: constants.baseURL,
      wsURL: constants.wsURL,
    });

    const callbacks = {
      open: () => this.client.logger.debug('open'),
      close: () => this.client.logger.debug('closed'),
      message: (data) => this.delegate.didReceiveMessage(data),
    };
    this.client.userData(listenKey, callbacks);
  }
}

export default class B {
  account;

  constructor() {
    this.account = new A(this);
  }
  
  didReceiveMessage(data) {
    console.log(data); // or whatever
  }
}

There is no interface (protocol) declaration that would tell A which properties and methods it may access on the passed delegate, but the contract exists of course. You should document it in prose. (Or use TypeScript).

Notice also how your class A interacts with the Spot client, it uses very much the same pattern of passing an object with event handler methods.

A simpler pattern in JavaScript, if you just need a single method in your protocol, is to pass a callable function only:

export default class A {
  client;
 
  constructor(onMessage) {
    this.client = new Spot(constants.apiKey, constants.apiSecret, {
      baseURL: constants.baseURL,
      wsURL: constants.wsURL,
    });
    this.client.userData(listenKey, {
      open: () => this.client.logger.debug('open'),
      close: () => this.client.logger.debug('closed'),
      message: onMessage,
    });
  }
}

export default class B {
  account;

  constructor() {
    this.account = new A(this.didReceiveMessage.bind(this));
    // or inline:
    this.account = new A(data => {
      console.log(data); // or whatever
    });
  }
  
  didReceiveMessage(data) {
    console.log(data); // or whatever
  }
}

CodePudding user response:

I am not an expert on NodeJs, but you can use something like an emitter plugin.

In javascript, it would look like this:

function A() {
    Emitter(this);
    
    this.action = function() {
        console.log("something happened");
        
        this.emit("action", { prop: "value" });
    };
}

function B(a_instance) {
    // subscribe to "action" event
    a.on("action", function(data) {
        console.log(data.prop); // "value"
    });
};

var myA = new A();
var myB = new B(myA);

myA.action();
  • Related