Isaac
Isaac

Reputation: 12874

Implementation of conditional logic via object properties

const abc = ({ data }) => {
  console.log('abc'+data);
}

const bcd = ({ data }) => {
  console.log('bcd'+data);
}

Given the above two function, depending on data, invoke the relevant function.

Method A:

if (data === 'funA') {
  this.abc({ data });
} else if (data === 'funB') {
  this.bcd({ data });
}

Method B:

const obj = {
  funA: () => this.abc({ data: 'age' }),
  funB: () => this.bcd({ data: 'name' })
}

obj[data] && obj[data]();

//Or provide a default handling function as below
//(obj[data] && obj[data]()) || someDefaultHandlingMethod()

Recently came across some Medium articles, recommending Method B over Method A, to avoid the long if statement. I'm wondering if MethodB actually create redundant object when we have () => abc({ data: 'age' })? Or there is a better way in achieving it?

Note:

The above is only pseudocode to share more of the idea of how can we invoke functions by using obj properties and replacing if-else

Upvotes: 4

Views: 100

Answers (2)

VLAZ
VLAZ

Reputation: 29004

You can take a page from Object Oriented approaches and perhaps use polymorphism

//define two types
function Foo() {}
function Bar() {}

//define a common interface for interacting with them
Foo.prototype.callMe = data => console.log( "Hi, I'm Foo, and your data is: " + data);
Bar.prototype.callMe = data => console.log( data + "\nthis was your data and this has been Bar.");

//instantiate one of each
let collection = [
  new Foo(),
  new Bar()
]

for (let obj of collection) {
  //call the common interface - the implementation depends on the object that uses it
  obj.callMe("lorem ipsum");
}

This is a polymorphic way of deciding what happens. However, it's underlying single dispatch - that is, choosing which method to invoke depends on a single object. Other languages have multiple dispatch where you can vary the method invoked based on multiple criteria. OO has some solutions to this like the Visitor Pattern which is a form of double dispatch.

function SingleObjectType() {
  this.foo = data => console.log("calling foo() method. Data is: " + data);
  
  this.bar = data => console.log(data + " was passed to bar() method");
  
  //entry point for a visitor
  this.accept = visitor => visitor(this); 
};

//set the common data
let data = "lorem ipsum";

//create visitors
function fooVisitor(obj) {
  obj.foo(data);
}

function barVisitor(obj) {
  obj.bar(data);
}

const singleObject = new SingleObjectType();

singleObject.accept(fooVisitor);
singleObject.accept(barVisitor);

This is a light-weight implementation of the Visitor pattern where instead of an entire object, you pass a single function as a callback. It is more idiomatic for JavaScript and it's less to set up. An even more functional approach would be to have higher order function producing the visitor callbacks which means you can more easily vary the data passed in (right now, it depends on the data being available at the visitor function definition):

function SingleObjectType() {
  this.foo = data => console.log("calling foo() method. Data is: " + data);
  
  this.bar = data => console.log(data + " was passed to bar() method");
  
  //entry point for a visitor
  this.accept = visitor => visitor(this); 
};

//set the common data
let commonData = "lorem ipsum";

//more OO terminology: visitor creators
//more FP terminology: curried functions
function fooVisitor(data) {
  return function(obj) {
    obj.foo(data);
  }
}

function barVisitor(data) {
  return function(obj) {
    obj.bar(data);
  }
}

const singleObject = new SingleObjectType();

singleObject.accept(fooVisitor(commonData));
singleObject.accept(barVisitor(commonData));

For completeness' sake, a more OO approach would be to turn the visitors into actual classes:

function SingleObjectType() {
  this.foo = data => console.log("calling foo() method. Data is: " + data);
  
  this.bar = data => console.log(data + " was passed to bar() method");
  
  //entry point for a visitor
  this.accept = visitor => visitor.visit(this); 
};

//set the common data
let data = "lorem ipsum";

//visitor classes
function FooVisitor(data) {
  this.visit = obj => obj.foo(data);
}

function BarVisitor(obj) {
  this.visit = obj => obj.bar(data);
}

const singleObject = new SingleObjectType();

singleObject.accept(new FooVisitor(data));
singleObject.accept(new BarVisitor(data));

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386570

Why not just assing the function for the properties? This requires an accessor for getting the right function and a call with data as well. The logic remains in the original functions.

const obj = {
    funA: this.abc,
    funB: this.bcd
}

obj[data](data);

Upvotes: 3

Related Questions