Лучший способ вывода строк на основе условной логики - PullRequest
1 голос
/ 03 апреля 2020

Я ищу мнения о том, как лучше всего достичь следующего. Его цель - от бэкэнда получить набор пользовательских данных, а затем на основе этих данных сконфигурировать объект текстов, который будет использоваться во внешнем интерфейсе. Я хочу, чтобы интерфейс был тупым и просто знал, что он должен отображать text.string. Backend будет содержать логику c определения того, что показано.

Я начал с объекта, подобного этому:

const someData = {
    hasBirthday: true,
    subscriptionFee: 100
}

const text = {
    welcome: (someData.hasBirthday) ? 'Happy birthday and welcome' : 'Welcome',
    cost: (someData.subscriptionFee > 0) ? `Your subscription fee is ${someData.subscriptionFee}` : `Enjoy your free stuff`
}

Довольно просто, однако затем были введены некоторые дополнительные логи c. который требовал вложенных тернарных операторов, вот так:

const someData = {
    hasBirthday: true,
    subscriptionFee: 100
}

const text = {
    welcome: (someData.hasBirthday) 
      ? 'Happy birthday and welcome' 
      : (someData.subscriptionFee > 0) 
        ? 'Welcome' 
        : 'Welcome, some custom message about your account being free',
    cost: (someData.subscriptionFee > 0) 
      ? `Your subscription fee is ${someData.subscriptionFee}` 
      : (someData.hasBirthday)
        ? `Enjoy your free stuff, you'll get a present on your birthday as well`
        : 'You only get free stuff, we do not know when your birthday is'
}

Заметьте, что это лишь подмножество примера, конечно, я мог бы сделать if else утверждений. Текст в значительной степени не имеет значения, но он служит цель в том, что она существенно отличается для каждой комбинации сравнений данных.

Теперь это довольно быстро запуталось, поэтому мне было интересно, если у кого-нибудь были мысли по поводу следующего предложения, чтобы сделать вещи немного чище:

const texts = {
    welcome: [
        {
            comparator: (data) => data.hasBirthday,
            value: () => 'Happy birthday and welcome' 
        },
        {
            comparator: (data) => !data.hasBirthday && someData.subscriptionFee > 0,
            value: () => 'Welcome' 
        },
        {
            comparator: (data) => !data.hasBirthday && someData.subscriptionFee <= 0,
            value: () => 'Welcome, some custom message about your account being free'
        },       
        {
            comparator: (data) => true,
            value: () => 'This could act like an else conditional'
        },
    ],
    cost: [
        {
            comparator: (data) => data.subscriptionFee > 0,
            value: (data) => `Your subscription fee is ${data.subscriptionFee}` 
        },
        {
            comparator: (data) => data.subscriptionFee <= 0 && someData.hasBirthday,
            value: () => `Enjoy your free stuff, you'll get a present on your birthday as well` 
        },
        {
            comparator: (data) => data.subscriptionFee <= 0 && !someData.hasBirthday,
            value: () => 'You only get free stuff, we do not know when your birthday is'
        },
    ]
}

// using lodash
_.mapValues(texts, (text) => {
    const matched = _.find(text, (t) => t.comparator(someData));
    return (_.isFunction(_.get(matched, 'value'))) ? matched.value(someData) : null;
}),

Итак, теперь каждое значение имеет функцию компаратора, которая может иметь различные логики c, чтобы определить, должно ли оно отображаться. _.find будет искать рекурсивно по порядку, поэтому мы знаем, что приоритет отдается первому-последнему.

В любом случае, ищите критику в отношении вышеизложенного, если у кого-то есть какие-либо идеи. Спасибо.

Обновлено: Реализация шаблона Builder

Я не собираю другой экземпляр класса, просто возвращаю объект, но все та же идея:

class Builder {
  constructor(data) {
    this._data = data;
  }

  setWelcome() { 
    if (this._data.hasBirthday) {
      this.welcome = 'Happy birthday and welcome'
    } else if (this._data.subscriptionFee > 0) {
      this.welcome = 'Welcome'
    } else {
      this.welcome = 'Welcome, some custom message about your account being free'
    }
    return this;
  }

  setCost () {
    if (this._data.subscriptionFee > 0) {
      this.cost = `Your subscription fee is ${this._data.subscriptionFee}`
    } else if (this._data.hasBirthday) {
      this.cost = `Enjoy your free stuff, you'll get a present on your birthday as well`
    } else {
      this.cost = 'You only get free stuff, we do not know when your birthday is'
    }
    return this;
  }

  create() {
    return _.omit(this
      .setWelcome()
      .setCost(), '_data')
  }
}

const object = new Builder({
  hasBirthday: true,
  subscriptionFee: 100
}).create();

1 Ответ

0 голосов
/ 04 апреля 2020

Afaik вы ищете (иерархический) заказ в применении условий. Я бы использовал для этого массивы, чтобы вы могли создавать свою собственную иерархию, применять условия и впоследствии возвращать первый найденный текст на основе условий, что-то вроде данного фрагмента. Я полагаю, что здесь сложно обойти сложность, потому что дело не в коде, а в количестве условий и порядке, который вы хотите применить.

Но вполне возможно, что я неправильно понял, что вы имеете в виду.

const someObjects = [
  {hasBirthday: true, subscriptionFee: 155, color: `brown`},
  {hasBirthday: false, subscriptionFee: 0, color: `orange`},
  {hasBirthday: false, subscriptionFee: 25, color: `green`, some: 'Hi there'},
]
someObjects.forEach(v => console.dir(getTexts(v)));

function getTexts(data) {
  const texts = {
    welcome: [
      [data => `${data.some}`, 
       data => data.some && data.some.startsWith("Hi")],
      ['Welcome', 
       data => !data.hasBirthday && data.subscriptionFee > 0],
      ['Happy birthday and welcome', 
       data => data.hasBirthday],
      ['Welcome, some custom message about your account being free', 
       data => !data.hasBirthday && data.subscriptionFee <= 0]
    ],
    cost: [
      [data => `Your subscription fee is $${data.subscriptionFee.toFixed(2)}`, 
       data => data.subscriptionFee > 0],
      [`Enjoy your free stuff, you'll get a present on your birthday as well`, 
       data => data.subscriptionFee <= 0 && data.hasBirthday],
      ['You only get free stuff, we do not know when your birthday is', 
       data => data.subscriptionFee <= 0 && !data.hasBirthday],
      ['Today, everything is free!', 
       data => !data.subscriptionFee]
    ],
    color: [
      [data => `Your color is ${data.color} (available on even dates)`, 
       data => [`orange`, `purple`].find( v => v === data.color) && 
        new Date().getDate() %2 !== 0],
      [data => `Your color ${data.color} is not available on odd dates`,
       data => [`orange`, `purple`].find( v => v === data.color) && 
        new Date().getDate() %2 === 0],
      [data => `Your color is ${data.color}`, 
       data => [`yellow`, `red`, `brown`, `blue`].find( v => v === data.color)],
      [data => `The color [${data.color || `no color given`}] is not available, sorry`, 
       data => !([`yellow`, `red`, `brown`, `blue`].find( v => v === data.color))],
    ]
  };
  const getStr = (str, data) => str instanceof Function ? str(data) : str;
  const txtReducer = txts => txts.reduce( (acc, [str, comparison]) => 
      comparison(data) && [...acc, getStr(str, data)] || acc, []);
  const textsReduced = Object.entries(texts).reduce( (acc, [key, value]) => 
      ({...acc, [key]: txtReducer(value)}), {});
  
  return Object.entries(textsReduced)
    .reduce( (acc, [key, texts]) => ({...acc, [key]: texts[0] }), {} );
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...