泛化具有不同参数的相似函数

Generalize similar function with different parameters

想象一下应用程序中的两种内容:“歌曲”和“电影”。

我有两个非常相似的辅助方法:

/**
 * @protected <-- No need to validate arguments, as the method is "protected".
 * ...
 */
function fillRemainingSongsList(currentSongs, currentUserId) {
  const remainingSongs = MAX_LIST_SIZE - currentSongs.length; 
  const shouldFetchTrending = Math.random() < FETCH_TRENDING_PROBABILITY;
  const promises = [
    api.songs.getRandomSongs(), 
    shouldFetchTrending ? api.songs.getTrendingSongs() : undefined
  ];

  const [
    randomSongs = [],
    trendingSongs = [],
  ] = await Promise.all(promises);

  return [...currentSongs, ...randomSongs, ...trendingSongs];
}

/**
 * @protected
 * ...
 */
async function fillRemainingFilmsList(currentFilms, category) {
  const remainingFilms = MAX_LIST_SIZE - currentFilms.length; 
  const shouldFetchTrending = Math.random() < FETCH_TRENDING_PROBABILITY;
  const promises = [
    api.films.getRandomFilms(category), 
    shouldFetchTrending ? api.films.getTrendingFilms(category) : undefined
  ];

  const [
    randomFilms = [],
    trendingFilms = [],
  ] = await Promise.all(promises);

  return [...currentFilms, ...randomFilms, ...trendingFilms];
}

如您所见,两个函数中都存在代码重复。我该怎么做才能更多地概括它们?任何设计模式?

我正在处理的问题是两种方法都调用不同的 api 方法,并且具有不同的参数...但是,另一方面,我试图在逻辑上不重复我自己。

您可以将随机函数和趋势函数传入公共重构函数。为了迎合不同趋势函数行为方式的差异(一个不需要参数,一个需要类别),您可以将它们包装在函数中:

async function fillRemaining(currentItems, getRandomFn, getTrendingFn) {
  const remainingItems = MAX_LIST_SIZE - currentItems.length; // this is never used?
  const shouldFetchTrending = Math.random() < FETCH_TRENDING_PROBABILITY;
  const promises = [
    getRandomFn(), 
    shouldFetchTrending ? getTrendingFn() : undefined
  ];

  const [
    randomItems = [],
    trendingItems = [],
  ] = await Promise.all(promises);

  return [...currentItems, ...randomItems, ...trendingItems];
}

现在两个函数都可以调用重构后的函数了:

function fillRemainingSongsList(currentSongs, currentUserId) {

  function getRandom () {
    return api.songs.getRandomSongs();
  }

  function getTrending () {
    return api.songs.getTrendingSongs();
  }

  return fillRemaining(currentSongs, getRandom, getTrending);
}

为了清楚起见,上面的函数是用命名函数编写的。或者你可以使用匿名函数:

async function fillRemainingFilmsList(currentFilms, category) {

  return fillRemaining(
    currentFilms,
    () => api.films.getRandomFilms(category),
    () => api.films.getTrendingFilms(category)
  );
}

两种样式的作用完全相同。