3

У меня есть этот код, который создает URL-адрес конечной точки, принимая в различных параметров:Как использовать функциональную цепочку с композицией в javascript?

const query = (obj) => { 
 
    let starter = 'query=' 
 
    let queryToString = JSON.stringify(obj) 
 
    return `${starter}${queryToString}` 
 
} 
 

 
const params = (str) => `${str}` 
 

 
const endpoint = (protocol, base, params, query) => { 
 
    if (!params && !query) return `${base}` 
 
    if (!params) return `${base}${query}` 
 
    return `${protocol}${base}?${params}&${query}` 
 
} 
 

 
const baseUrl = 'api.content.io' 
 

 
const protocol = (secure = true) => secure ? 'https://' : 'http://' 
 

 
let result = endpoint(protocol(), baseUrl, params('limit=5&order=desc'), 
 
    query({ 
 
    field: 'title', 
 
    include: 'brands' 
 
    })); 
 

 
console.log(result)

Это создает строку, как:

https: //api.content.io?limit=5&order=desc&query={"field":"title","include":"brands"} 

Можно ли реорганизовать этот код так что условия внутри функции endpoint могут быть удалены, строки правой конкатенации и все это приковано к функциональному вызову, например

Endpoint.chain(protocol(p)).chain(base(b)).chain(params(p)).chain(query(q)).build() 

Как мне начать с этого?

UPDATE: Нижеприведенные решения довольно хороши, но я хотел бы понять, как функциональные программисты используют ADT (Алгебраические типы данных) с Monads для решения этой проблемы. Идея состоит в том, чтобы запустить chain функций, а затем fold, чтобы вернуть значение, которое я хочу

+1

Вы уверены, что _want_ сделать что-то вроде этого? 'base' на самом деле не является параметром' params'. Ни одно из них не подчинено никому другому. Все они равные участники в создании конечной точки. Если вы хотите показать имена компонентов в вызове, идиома JS - это нечто вроде «endpoint ({protocol, base, params, query)), вызываемое с использованием сокращенной записи объекта. –

+0

hmm..you're right. Вы говорите, что я должен достичь функциональной композиции только тогда, когда параметры совпадают? Это может быть неправильное приложение? –

+0

@RayToal, что я хочу сделать, это, по сути, цепочка вызовов функций, так что значение endvalue одной функции передается другому, и необходимость проверки нулевых значений удаляется. –

ответ

2

Вы ищете что-то вроде этого?

Цепочка в основном происходит, когда вы возвращаете полный объект в каждом вызове функции. Здесь каждая функция устанавливает значение своего данного куска и возвращает этот объект для любого контекста, к которому он привязан. Каждая функция сможет обрабатывать полученное значение, а функция значения будет строить url на основе заданных параметров.

var Endpoint = { 
    protocol: protocol, 
    base: base, 
    params: params, 
    query: query, 
    value: value 
} 
function protocol(b) { 
    protocol.value = b && 'https://' || 'http://'; 
    return this; 
} 
function base(s) { 
    base.value = s || ''; 
    return this; 
} 
function params(s) { 
    params.value = s || ''; 
    return this; 
} 
function query(s) { 
    query.value = s || ''; 
    return this; 
} 
function value(s) { 
    return '$protocol$base$params$params$query' 
     .replace('$protocol',protocol.value) 
     .replace('$base',base.value) 
     .replace('$params',params.value) 
     .replace('$query',query.value) 
} 

Endpoint 
    .protocol(true) 
    .base('www.foo.com') 
    .params('one/two/three') 
    .query('?foo=bar') 
    .value() 
// "https://www.foo.comone/two/three$params?foo=bar" 
+0

Это очень близко, но я хотел бы понять, как функциональные программисты используют ADT (Алгебраические типы данных) с Monads для решения этой проблемы Проблема –

+0

Я немного смущен относительно того, как работает этот код. Не могли бы вы рассказать мне, как «base.value = s || «На самом деле работает? Я имею в виду левую сторону. Откуда берется 'base' в' base.value'? –

+0

Это шаблон декоратора, реализованный с помощью цепочки методов. Вы либо блестящие, либо ваша программа блестяще написана. Невероятно, и случайно, Монады прекрасно иллюстрируются в этом примере. Что касается деталей, о которых вы спрашивали, я дам вам подсказку, функции тоже объекты! – Shanimal

2

Вы можете использовать класс для этого, у которого есть методы, позволяющие цепочки (они возвращаются this):

class EndPoint { 
 
    constructor() { 
 
     // defaults 
 
     this.prot = 'https://'; 
 
     this.bas = 'api.content.io'; 
 
     this.qry = ''; 
 
     this.par = ''; 
 
    } 
 
    secure(on = true) { 
 
     this.prot = on ? 'https://' : 'http://'; 
 
     return this; 
 
    } 
 
    base(str) { 
 
     this.bas = str; 
 
     return this; 
 
    } 
 
    query(obj) { 
 
     this.qry = `query=${JSON.stringify(obj)}` 
 
     return this; 
 
    } 
 
    params(str) { 
 
     this.par = str 
 
     return this; 
 
    } 
 
    build() { 
 
     let sep1 = this.qry.length || this.par.length ? '?' : ''; 
 
     let sep2 = this.qry.length && this.par.length ? '&' : ''; 
 
     return `${this.prot}${this.bas}${sep1}${this.par}${sep2}${this.qry}`; 
 
    } 
 
}; 
 

 
let result = new EndPoint().secure(true).base('www.example.com') 
 
    .params('limit=5&order=desc').query({ field: 'title', include: 'brands' }).build(); 
 

 
console.log(result);

1

Хотя приведенные выше ответы будут работать, но если вы хотите, чтобы синтаксис начинался с Endpoint()., затем используйте класс EndpointBuilder и заводскую функцию, как показано ниже.

class EndpointBuilder { 

    constructor() { 
     this.params = []; 
     this.protocol = 'http://'; 
     this.baseUrl = 'api.endpoint.io'; 
    } 

    base(url) { 
     if (url && url.trim().length > 0) 
      this.baseUrl = url; 
     return this; 
    } 

    secure() { 
     this.protocol = 'https://'; 
     return this; 
    } 

    setParam(param, val) { 
     if (param && val) 
      this.params.push({ param, val }); 
     return this 
    } 

    toString() { 

     const url = `${this.protocol}${this.baseUrl}`; 
     if (this.params.length <= 0) 
      return url; 

     let qString = ''; 
     this.params.forEach(p => { 
      qString = `${qString}${qString.length > 0 ? '&' : ''}${p.param}=${JSON.stringify(p.val)}`; 
     }); 

     return `${url}?${qString}`; 
    } 
}; 

// Endpoint Factory 
const Endpoint = function() { 
    return new EndpointBuilder(); 
}; 

Пример использования:

const url1 = Endpoint().setParam('limit', 5).setParam('query', { field: 'title', include: 'brands' }).toString(); 

const url2 = Endpoint().base('another.endpoint.io').setParam('limit', 5).setParam('order', 'desc').setParam('query', { field: 'title', include: 'brands' }).toString(); 

const url3 = Endpoint().base('another.endpoint.io').secure().setParam('limit', 5).setParam('order', 'desc').setParam('query', { field: 'title', include: 'brands' }).toString(); 

console.log(url1); 
// http://api.endpoint.io?limit=5&query={"field":"title","include":"brands"} 

console.log(url2); 
// http://another.endpoint.io?limit=5&order="desc"&query={"field":"title","include":"brands"} 

console.log(url3); 
// https://another.endpoint.io?limit=5&order="desc"&query={"field":"title","include":"brands"} 
Смежные вопросы