JavaScript’te API Yöntemleri Tasarlama – JSManifest

JavaScript'te API Yöntemleri Tasarlama – JSManifest

JavaScript’te API yöntemleri tasarlamak, öğrenmek için çok yararlı bir beceridir ve programlamaya farklı bir perspektiften bakmanıza olanak tanır. Bir bakış açısı, kullanıcılarınız için bir proje oluşturmak yerine, geliştiricilerin kullanması için bir proje oluşturuyorsunuz. Daha önce bir kitaplık veya SDK oluşturmadıysanız, bu makale yöntem tasarımına nereden ve nasıl başlayacağınızı anlamanıza yardımcı olabilir.

JavaScript, geliştiriciler olarak sağlam, çok kullanımlı API’ler oluşturmak için yararlanabileceğimiz, gevşek yazılmış bir dildir.

Bu makale, JavaScript’te bir API için yöntemler tasarlarken akılda tutulması gereken birkaç kuralı gözden geçirecektir.

1. Adlandırılmış Parametreler

İlk bahsedeceğimiz parametre adlandırılmış parametrelerdir. ES6 tanıtılmadan önceki eski günlerde, işlevleri bildirmenin tek yolu şuydu: fonksiyon bildirimleri kullanmak function sözdizimi. Yürütme akışlarıyla nereye gideceğinizi belirlemek için, bir işlev aritesi (işlevin beklediği argüman sayısı) alır, onu bir diziye dönüştürür ve argümanların nasıl göründüğüne bağlı olarak mantığın geri kalanını uygularsınız.

Bu örnekte, animal, optionsve callback fonksiyonun parametreleridir ve arity üç olacaktır. İşlev, yeni bir hesap oluşturmak için tasarlanmıştır ve arayan tarafından sağlanmadıysa her hesabın bazı varsayılan ayarları olacaktır:

function createAccount(
  username = '',
  password = '',
  nickname = '',
  email = '',
  gender = 'Male',
  bio = '',
  subscription = 'Basic',
  callback,
) {
  if (!username || !password || !email) {
    throw new Error(
      'You are missing one or all of the following fields: "username", "password", "email"',
    )
  }
  return api
    .createAccount({
      username,
      password,
      nickname,
      email,
      gender,
      bio,
      subscription,
    })
    .then((result) => {
      if (callback) callback(null, result)
    })
    .catch((error) => {
      console.error(error)
      if (callback) callback(error)
    })
}

createAccount(
  'lucas',
  'applebee123x123',
  '',
  'applebee1233@gmail.com',
  '',
  'My bio',
  'Basic',
  function cb(err, data) {
    if (err) {
      console.error(err)
    }
    
  },
)

Bununla ilgili sorun, arayanın, bir veya daha fazla parametre gerekmese bile düzgün çalışması için işleve parametre olarak iletilecek argümanların tam sırasını bilmesi gerektiğidir. Gereksinimleri sırayla ezberlemek zor olabilirken, dikkatli olmazsanız siparişi karıştırmak çok kolay olabilir. Ayrıca, fonksiyonun düzgün çalışması için gerekli değilse, bir parametreyi gerekli kılmak pek mantıklı değildir.

Gelecekte bakımı da zor olacak çünkü sizin veya patronunuzun bunlardan kurtulması gerektiğinde username ve yap email bunun yerine yeni kullanıcı adı olarak, mantığı değiştirmeniz gerekir.

Daha iyi bir uygulama, yalnızca bir nesneyi kullanmaktır:

function createAccount({
  username = '',
  password = '',
  nickname = '',
  email = '',
  gender = 'Male',
  bio = '',
  subscription = 'Basic',
  callback,
}) {
  if (!username || !password || !email) {
    throw new Error(
      'You are missing one or all of the following fields: "username", "password", "email"',
    )
  }
  return api
    .createAccount({
      username,
      password,
      nickname,
      email,
      gender,
      bio,
      subscription,
    })
    .then((result) => {
      if (callback) callback(null, result)
    })
    .catch((error) => {
      console.error(error)
      if (callback) callback(error)
    })
}

Yalnızca kullanıcı adını koddan kaldırmanız gerektiğinden, okunabilirlikten ve sürdürülebilirlik üzerinde daha fazla kontrolden yararlanıyoruz:

function createAccount({
  password = '',
  nickname = '',
  email = '',
  gender = 'Male',
  bio = '',
  subscription = 'Basic',
  callback,
}) {
  if (!password || !email) {
    throw new Error(
      'You are missing one or all of the following fields: "email", "password"',
    )
  }
  return api
    .createAccount({
      password,
      nickname,
      email,
      gender,
      bio,
      subscription,
    })
    .then((result) => {
      if (callback) callback(null, result)
    })
    .catch((error) => {
      console.error(error)
      if (callback) callback(error)
    })
}

Aramayı yapmak da daha özlü ve okunabilir hale gelir:

createAccount({
  password: 'applebee123x123',
  email: 'applebee1233@gmail.com',
  bio: 'My bio',
  callback: function cb(err, data) {
    if (err) {
      console.error(err)
    }
    
  },
})

2. Yöntem Zincirleme / Akıcı API

API yazmanın en sevdiğim yolu, akıcı API yöntem zincirleme ile.

Yöntem zincirleme, basitçe birden çok çağrıyı birbiri ardına zincirleme işlemidir. Genel fikir, okunabilir ve akıcı bir kod elde etmek, böylece daha hızlı anlaşılmasını sağlamaktır. Bu yöntemler genellikle fiillerdir (döndürme gibi)

Örneğin:

getPhoto('../nemo_the_fish.jpg')
  .applyFilter('grayscale', '100%')
  .rotate(100)
  .scale(1.5)

Bu şu anlama gelir: “resmi al nemo_the_fish.jpg ve gri tonlama filtresini %100 değerinde uygulayın, görüntüyü 100 derece döndürün ve ölçeği 1,5 kat daha artırın.”

Bu uygulamanın iyi bir yanı, kendi akıcı API arayüzünüzü yazmaya başlamanın çok hızlı olmasıdır. Zincirlenebilmesi için referansı yöntem çağrılarınızın içindeki bağlama döndürmeniz yeterlidir:

const createWarrior = function createWarrior(name) {
  let hp = 100
  let battleCryInterval = 0

  return {
    bash: function(target) {
      target -= 10
      return this
    },
    
    battleCry: function battleCry() {
      hp += 60
      battleCryInterval = setInterval(() => {
        hp -= 1
      }, 1000)
      setTimeout(() => {
        if (battleCryInterval) {
          clearInterval(battleCryInterval)
        }
      }, 60000)
      return this
    },
    getHp: function getHp() {
      return hp
    },
  }
}

const warrior = createWarrior('chris')
const otherWarrior = createWarrior('bob')

warrior
  .battleCry()
  .bash(otherWarrior)
  .bash(otherWarrior)
  .bash(otherWarrior)
  .bash(otherWarrior)
  .bash(otherWarrior)

const otherWarriorsHp = otherWarrior.getHp()

console.log(otherWarriorsHp) 

Akıcı bir API’nin en büyük örneklerinden biri jQueryve kitaplığın akıcılığı sayesinde onu tartışmasız hem öğrenmesi hem de kullanması en kolay JavaScript kitaplıklarından biri yapar:

$(window).resize(function() {
  $('#logbox').append('<div>The window resized</div>')
})

Bununla birlikte, akıcı API zincirleme yönteminin birkaç dezavantajı vardır.

En büyük dezavantajı, bir zincirin ortasında bir kesme noktası belirlemenin zor olabilmesi ve hataların hata ayıklamasını zorlaştırmasıdır. Ayrıca, çok fazla prosedürel kodu teşvik eder. Zincirdeki herhangi bir adıma günlükçüler ekleyerek ve sonraki yöntemleri çağırmak için bunları kullanarak hata ayıklama sorununu aşmanın yolları vardır.

Diğer bir dezavantaj, sıkı nokta gösterimi erişimiyle uzun cümleler yazma eylemine kapılmanızdır. Bu, işleri basit tutmanın önüne geçebilir, bu yüzden işleri basit tutmayı unutmayın.

3. Fonksiyon Polimorfizmi

terimini duymuş olabilirsiniz polimorfizm genellikle bir şeyin bağlama göre farklı davrandığı diğer dillerde.

Aynı kavram JavaScript’teki fonksiyon polimorfizmi için de geçerlidir. Bunlar, iletilen argümanlara göre hareket eden işlevlerdir (ki bu bizim bağlam).

API’ler, üzerinde daha fazla kontrole sahip olmak için genellikle bir diziye veya dizi benzeri yapıya argümanlar toplar. Onları bir dizi yapısında bulundurmak, onları aynı kapsamdaki diğer işlevlere geçirmek ve bunun tersi gibi şeyler yapmalarına izin verir.

Ok işlevleri tanıtılmadan önce, polimorfik işlevler içinde argümanları toplamak için yaygın uygulama, dizi benzerini kullanmaktı. arguments nesne. Bazen, onları bir diziye atadıktan sonra argümanlarla daha fazla şey yapmanız gereken durumlarda olabilirsiniz. Argümanlar dizi benzeri bir nesne olsa da, temel dizi işlevleri eksik olduğundan gerçek bir dizi gibi işlev görmez ve bu çok sınırlayıcıdır.

Geliştiricilerin bunu aşmanın yolu, kullanarak ayrı, sığ bir kopya oluşturmaktır. Array.prototype.slice.call(). buna denir method delegation. Sırayla, sen temsilci dilim() çağrısı Array.prototype nesne.

Bunun bir örneği şöyle görünecektir:

const args = Array.prototype.slice.call(arguments, 0)

Bu, 0 dizininden başlayan öğeleri kopyalar ve sonraki her şeyi döndürür.

Argümanlar gibi gerçek yöntemleri yok .push veya .shiftbu yüzden onu bir diziye dönüştürüyoruz Array.prototype.slice böylece tüm dizi yöntemlerine erişebiliriz.

ES6’da, aşağıda gösterildiği gibi spread operatörünü kullanarak onu kolayca bir diziye dönüştürebiliriz:

const someFunction = function(...args) {
  console.log(args)
  console.log(args.shift())
}

someFunction(1, 'hello', 'bob')


Argümanlarınızı bir dizi veya dizi benzeri bir yapıya sahip olduğunuzda, argümanların nasıl göründüğüne bağlı olarak yürütme mantığı ile nereye gideceğinizi belirleyebilirsiniz. Bu, çok fazla kod yazmadan çok amaçlı kullanım için çok esnek hale getirir.

Yayılmadan:

const applyFilter = function(filter, value, options) => {
  const args = [].slice.call(arguments, 0)
  console.log(args.length) 
}

applyFilter('grayscale', '100%')

Yayılma ile:

const applyFilter = (...args) => {
  console.log(args.length) 
}

applyFilter('grayscale', '100%')

Bunu akılda tutarak, şimdi bu argümanlardan yürütmenin nasıl ele alınacağını belirleyebiliriz:

const applyFilterToImage = (image) => {
  return function applyFilter(...args) => {
  

    let options
    let filters = {}
    let callback

    const arg1 = args[0]
    
    if (args.length === 1) {
      if (arg1 && typeof arg1 === 'object') {
        filters = { ...arg1 }
        
        const arg2 = args[1]
        if (arg2 && typeof arg2 === 'function') {
          callback = arg2
        }
      } else {
        throw new Error(
          'You must supply an object if you are only providing the first argument',
        )
      }
    } else {
      if (args.length > 2) {
        
        if (typeof args[3] === 'object') {
          options = args[3]
        }
          
        else if (typeof args[3] === 'function') {
          callback = args[3]
        }
      }
      
      if (typeof arg1 === 'string') {
        const filter = arg1
        const value = args[1]
        filters[filter] = value 
      } else {
        if (callback) {
          callback(new Error('Filter is not a string'))
        }
      }
    }
      const newImg = api.filterImage(filters, options)
      if (callback) {
        return callback(null, newImg)
      }
  }
}

const img = '../bob_the_builder.jpg'
const applyFilter = applyFilterToImage(img)
const callback = (newImg) => {
  console.log(newImg)
}

applyFilter({
  grayscale: '100%',
  rotate: 100,
  scale: 1.5,
}, callback)

Basit işlev, geliştiricinin onu çeşitli şekillerde kullanmasına olanak tanır:

  1. Birinci ve ikinci bağımsız değişken olarak bir dize ve bir değer sağlayarak tek bir filtre uygulayın.
  2. Veya ilk argüman olarak bir anahtar/değer çiftleri nesnesi sağlayarak birden çok filtre uygulayın.
  3. Veya ilk argüman olarak bir filtre anahtarı/değeri nesnesi sağlayarak tek bir filtre uygulayın.
  4. İsteğe bağlı olarak, yeni filtrelenmiş görüntüyü ikinci veya üçüncü argüman olarak bir geri arama yoluyla geri alın (konum, filtreler için ne ilettiklerine bağlıdır.)

Çözüm

Bu, bu makalenin sonunu tamamlıyor. Gelecekte benden daha fazla gönderi bekleyin!

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.