JavaScript’te Zincirleme Modelleri – JSManifest

JavaScript'te Zincirleme Modelleri – JSManifest

Geliştiriciler olarak yazmak bizim sorumluluğumuzdur iyi kod. Bu, hem verimli hem de okunabilir kod yazmak anlamına gelir. Ancak bazen, daha iyi performans göstermek için okunabilirlikten ödün verildiğinde veya tam tersi olduğunda, hangi kodun daha iyi olacağına karar vermemiz gereken engellerle karşılaşırız.

Bu gönderi, JavaScript’teki farklı zincirleme kalıplarını inceleyecek ve umarım bu, zincirleme işlemlerinizi yazarken size mümkün olan her şekilde yardımcı olacaktır.

Yöntem Zincirleme

JavaScript’te yöntem zincirleme, yöntemlerin bir nesneden diğerine ara değişkenler oluşturmadan çağrılmasıdır. Başka bir deyişle, bir tek açıklama programımıza gerçekleştirmesi talimatını verdiğimiz çoklu yöntem çağrıları.

Jquery, DOM API’lerini verimli bir şekilde kapsüllerken komutlarını bir araya getirme konusundaki zarif yeteneği nedeniyle semantiğinden büyük ölçüde yararlanmanın iyi bir örneğidir. Açık ve özlü sözdizimi kullanır:

$(‘#main’).css(‘background’, ‘red’).height(200).css(‘text-align’,’center’).width(500);

Bu, tek bir ifadenin bir örneğidir. Bir satırda bu, bunların hepsini tek bir uygulamada yapar:

  1. Kimliğe sahip bir DOM öğesi için sorgular main
  2. Arka plan rengini şu şekilde değiştirir: red
  3. Yüksekliği şu şekilde değiştirir: 200px
  4. Metin hizalamasını şu şekilde değiştirir: center
  5. Genişliği olarak değiştirir 500px

Bir başka popüler JavaScript kitaplığı, ekspresjs geliştiricilerin kullanması için kolay ve sağlam bir API sağlamak için yöntem zincirini kullanır.

İşte onlardan alınan bir örnek Hata yönetimi sayfa:

var bodyParser = require('body-parser')
var methodOverride = require('method-override')

app.use(
  bodyParser.urlencoded({
    extended: true,
  }),
)
app.use(bodyParser.json())
app.use(methodOverride())
app.use(logErrors)
app.use(clientErrorHandler)
app.use(errorHandler)

Mantık, uygulamada gizlenir ve geri döner this yöntem zincirlemenin devam etmesine izin vermek için:

app.use = function use(fn) {
  var offset = 0
  var path = "https://jsmanifest.com/"

  
  
  if (typeof fn !== 'function') {
    var arg = fn

    while (Array.isArray(arg) && arg.length !== 0) {
      arg = arg[0]
    }

    
    if (typeof arg !== 'function') {
      offset = 1
      path = fn
    }
  }

  var fns = flatten(slice.call(arguments, offset))

  if (fns.length === 0) {
    throw new TypeError('app.use() requires a middleware function')
  }

  
  this.lazyrouter()
  var router = this._router

  fns.forEach(function (fn) {
    
    if (!fn || !fn.handle || !fn.set) {
      return router.use(path, fn)
    }

    debug('.use app under %s', path)
    fn.mountpath = path
    fn.parent = this

    
    router.use(path, function mounted_app(req, res, next) {
      var orig = req.app
      fn.handle(req, res, function (err) {
        setPrototypeOf(req, orig.request)
        setPrototypeOf(res, orig.response)
        next(err)
      })
    })

    
    fn.emit('mount', this)
  }, this)

  return this
}

kod bloğu yazarı api zincirleme yöntemiyle (yine) kodla kod yazmayı kolaylaştırır:

const writer = new CodeBlockWriter()

writer.write('class MyClass extends OtherClass').block(() => {
  writer.writeLine(`@MyDecorator(1, 2)`)
  writer.write(`myMethod(myParam: any)`).block(() => {
    writer.write('return this.post(').quote('myArgument').write(');')
  })
})

Oluşturucu Modeli

Oluşturucu Modeli, doğası gereği, amacına ulaşmak için yöntem zincirini kullanır. Yapıcı tanım gereği bir yaratıcı süreci etkili bir şekilde basitleştirerek adım adım karmaşık nesneler oluşturmanıza olanak tanıyan tasarım deseni.

Yöntem zincirlemenin nasıl tanımlandığına oldukça benziyor değil mi?

bir oluşturacak olsaydık Chicken yöntemlerinin tüm vücut parçalarını oluşturmak olduğu bir sınıf, oluşturucuyu tam bir tavuk oluşturmak için kullanabiliriz:

const initBodyParts = () => ({
  beak: null,
  breast: null,
  claw: null,
  comb: null,
  eye: { left: null, right: null },
  eyeLobes: { left: null, right: null },
  mainTail: null,
  thigh: { left: null, right: null },
  toe: { left: null, right: null },
  wing: { left: null, right: null },
})

class ChickenEye {
  constructor(options) {
    this.options = options
  }

  close() {
    
  }
}

class Chicken {
  constructor(bodyParts) {
    this.bodyParts = bodyParts
  }

  closeEyes() {
    this.bodyParts.eye.left.close()
    this.bodyParts.eye.right.close()
  }
}

class ChickenBuilder {
  constructor() {
    this.bodyParts = initBodyParts()
  }

  addComb(options) {
    this.bodyParts.comb = options
    return this
  }

  addBeak(options) {
    this.bodyParts.beak = options
    return this
  }

  addBreast(options) {
    this.bodyParts.breast = options
    return this
  }

  addToe(options) {
    this.bodyParts.toe[options.side] = options
    return this
  }

  addClaw(options) {
    this.bodyParts.claw = options
    return this
  }

  addEye(options) {
    this.bodyParts.eyes[options.side] = new ChickenEye(options)
    return this
  }

  addEarLobes(options) {
    this.bodyParts.earLobe[options.side] = options
    return this
  }

  addMainTail(options) {
    this.bodyParts.mainTail = options
    return this
  }

  addWing(options) {
    this.bodyParts.wing[options.side] = options
    return this
  }

  addThigh(options) {
    this.bodyParts.thigh[options.side] = options
    return this
  }

  build() {
    const chicken = new Chicken(this.bodyParts)
    this.bodyParts = initBodyParts()
    return chicken
  }
}

const chickenBuilder = new ChickenBuilder()
const chicken = chickenBuilder
  .addBeak({ shape: 'round' })
  .addComb()
  .addEye({ side: 'left' })
  .addEye({ side: 'right' })
  .addThigh({ side: 'left' })
  .addThigh({ side: 'right' })
  .addWing()
  .addMainTail({ length: 2 })
  .addToe({ side: 'left' })
  .addToe({ side: 'right' })
  .build()

Bir oluşturucu kullanmadıysak, kod tekrarlanabilir hale gelebilir ve tüketiciler için değerleri döndürüp döndürmedikleri net değildir, bu da onları bazı belgeler aramaya zorlar:

const chickenBuilder = new ChickenBuilder()
chickenBuilder.addBeak({ shape: 'round' })
chickenBuilder.addComb()
chickenBuilder.addEye({ side: 'left' })
chickenBuilder.addEye({ side: 'right' })
chickenBuilder.addThigh({ side: 'left' })
chickenBuilder.addThigh({ side: 'right' })
chickenBuilder.addWing()
chickenBuilder.addMainTail({ length: 2 })
chickenBuilder.addToe({ side: 'left' })
chickenBuilder.addToe({ side: 'right' })
const chicken = chickenBuilder.build()

Daha önce hiç bir oluşturucu kullanmadıysanız, aşağıdaki örneğe bir göz atın ve bize sağladığı güzel avantajı görüp göremediğinizi görün:

inşaatçı olmadan

let baseUrl = 'https://frogs.com'
let url = `${baseUrl}`

const pathname = `/api/v1`
const page = 2
const filter = 'date_added'
url = url + pathname + page + filter

inşaatçı ile

let baseUrl = 'https://frogs.com'
let url = `${baseUrl}`

const builder = new UrlBuilder(baseUrl)
url = builder.page(2).filter('date_added').pathname('/api/v1').build()

Oluşturucuyu kullanarak birden çok kod satırı kaydettik ancak uygulama ayrıntılarını da tüketiciden gizledik. Burada önemli olan uygulamayı gizlemek.

Bu, oluşturucunun yukarıdaki snippet’teki uygulamasıdır (evet, uzun olduğunu biliyorum, ancak dinlemeye devam edin):

class UrlBuilder {
  #filter = ''
  #page = ''

  constructor(baseUrl = '') {
    this.baseUrl = baseUrl
  }

  pathname(value) {
    this.pathname = value
    return this
  }

  page(value) {
    this.#page = value
    return this
  }

  filter(value) {
    this.#filter = value
    return this
  }

  build() {
    const append = (str = '', key = '', value) => {
      if (!str.includes('?')) str += '?'
      str += `&${key}=${value}`
      return str
    }

    let url = `${this.baseUrl}/api/v1/${this.pathname}?`

    this.#page && (url = append(url, 'page', this.#page))
    this.#filter && (url = append(url, 'filter', this.#filter))

    return url
  }
}

Bundan çıkarılacak şey, geliştiriciler olarak işimizin verimli ve okunabilir kod.

Evet, oluşturucusuz örneğimiz teknik olarak daha kısaydı, ancak yeniden kullanılabilir mi? Kodu elimizden geldiğince kolay okuyabilen kişilerle çalışabilecek miyiz (biz yazdık).

İkisinin de cevabı hayır. Kodumuzu paylaştığımızda (hatta Biz kodumuzu yeniden kullanmayı deneyin) öncekinin aksine oluşturucuyu kolayca alıp yeniden kullanabiliriz. Bunun nedeni, önceki kodu yeniden kullanmayı denersek, kodu kopyalayıp yapıştırmamız gerektiğidir.

Kopyalayıp yapıştırdığımızda yinelenen kod üretiriz. Kod kopyalandığında, api’mize güncellemeler yazmamız gerektiğinde değişiklikleri iki katına çıkarmamız gerekir. Gereksiz yere değişiklikleri ikiye veya üçe katlamamız gerektiğinde, kodumuz şöyle olur: sürdürülemez. Oluşturucu kalıbı, tüm bu sorunları tek bir hamlede çözen basit ama güçlü bir kalıptır!

Sorumluluk Zinciri Modeli

Sorumluluk Zinciri (COR), bazı isteklerin birden çok nesne tarafından gönderilmesine, alınmasına ve işlenmesine izin veren bir kalıptır. Bu nesneler (sadece işlevler), önceki veya sonraki isteğin uygulama ayrıntılarına bağlı değildir ve yürütmeyi çalıştırdığında ne yapılacağına karar verebilir. Ayrıca tüm zinciri iptal edebilir veya isteğin zincirdeki bir sonraki nesneye (veya işleve) devam etmesine izin verebilirler.

DOM’da kullanılan desene bir örnek:

<div id="root" onclick="onBtnContainerClick()">
  <button>Say hello</button>
</div>

İşlevler birbiri ardına kontrol edilebilir ve öngörülebilir bir şekilde zincirlenebildiği sürece, bu modeli uygulamanın gerçekten “resmi” veya doğru bir yolu yoktur.

ExpressJS ayrıca bu kalıbı yönlendiricilerinde de kullanır. ara katman yazılımı bir işleyiciden diğerine işleyiciler:

app.get(
  '/user/:id',
  function (req, res, next) {
    
    if (req.params.id === '0') next('route')
    
    else next()
  },
  function (req, res, next) {
    
    res.send('regular')
  },
)

Ancak, işleyicilerin birbirleriyle tam olarak nasıl bağlantılı olduğunu merak ediyorsanız next() çağrılar, tam olarak nasıl çalışır bağlantılı liste veri yapısı çalışır.

Bu kalıbı pratikte uygulamanın doğru bir yolu yoktur, ancak işleyicileri birbirine bağlayan bir örnek:

class Fighter {
  constructor() {
    this.hp = 100
    this.next = null
  }

  fight(target) {
    target.hp -= 25
    this.next && this.next.fight(target)
  }
}

class FistFighters {
  constructor() {
    this.fighters = []
  }

  addFighter(fighter) {
    if (this.fighters.length) {
      this.fighters[this.fighters.length - 1].next = fighter
    }
    this.fighters.push(fighter)
  }

  fight(target) {
    this.fighters[0].fight(target)
  }
}

const mob = new FistFighters()

const joe = new Fighter()
const michael = new Fighter()
const jacob = new Fighter()
const mojo = new Fighter()

mob.addFighter(joe)
mob.addFighter(michael)
mob.addFighter(jacob)
mob.addFighter(mojo)

const jim = new Fighter()

mob.fight(jim)

console.log(jim) 

javascript'te uygulamalı örnek sorumluluk zinciri

Opsiyonel Zincirleme

Yuvalanmış nesnelere erişmek tehlikeli bir işlemdir, çünkü kodumuz değerler boş olduğunda veya yalnızca prototipi nesne prototipinden miras alınmadığında işlemezse, bir TypeError hangi programımızı çökertebilir.

İşte demek istediğim:

const buckets = {
  red: {
    name: 'RedBucket',
    items: [1, 2, 10],
  },
}

const redBucketItems = buckets.red.items[1]

erişmeye çalışırsak buckets.red.items[1] ne zaman items yanlışlıkla nesne olmayan bir veri türüne ayarlanmış (bir null değer), şuna benzeyen hoş olmayan bir şey elde ederiz:

Uncaught TypeError: Cannot read properties of null (reading '1')

JavaScript, isteğe bağlı zincirleme olarak adlandırılan, bağlı nesneler aracılığıyla değerlere erişmeyi basitleştiren ve bir noktada hale gelebilecek bu işlemi daha kolay hale getirmenin bir yolunu sağlar. null veya nesnelerden başka bir şey:

buckets.red.items[1]



buckets?.red?.items?.[1] 

Söz Zincirleme

function fetchFrogs() {
  return new Promise((resolve, reject) => {
    return fetch('https://frogs.com/api/v1')
  })
}

Sözleri birlikte zincirleyerek, işlevlerinizin her birinin (tam anlamıyla) verilerini zamanında alma sözü verileceği zaman uyumsuz kod yazabilirsiniz:

function callMeWhenYouGetMyFrogs(frogs) {
  window.alert(
    `Here is a list of frogs fetched: ${JSON.stringify(frogs, null, 2)}`,
  )
}

fetchFrogs()
  .then((response) => response.json())
  .then((data) => data.frogs)
  .then((frogs) => callMeWhenYouGetMyFrogs(frogs))
  .catch(console.error)

Çözüm

Ve bu yazının sonu burada bitiyor! Umarım bunu değerli bulmuşsunuzdur ve gelecekte daha fazlasını ararsınız!

Bir cevap yazın

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