JavaScript’te Strateji Tasarım Modelinin Gücü – JSManifest

JavaScript'te Strateji Tasarım Modelinin Gücü – JSManifest

JavaScript, esnekliği ile çok iyi bilinen bir dildir. Muhtemelen JavaScript’in zayıf yönlerinden biri olduğunu söyleyen insanları duymuşsunuzdur, hatta bazıları tam tersini söylüyor. Ben daha çok ikinci tarafta olma eğilimindeyim çünkü bunu yıllar önce pek mümkün görünmeyen harika şeyler yapmak için kendi avantajımıza kullanma eğilimindeyiz.

React, daha sonra icat edilen harika araçlar olarak bunu destekleyen gerçek bir kanıttır. Ayrıca birde şu var Elektron gibi günümüzün gelişen teknolojisine güç veren Visual Studio Kodu ve Figma.

Her JavaScript kitaplığı, günümüzde modern JavaScript ekosisteminde de sıcak bir konu olan bir tür tasarım deseni kullanır. Bu yazıda odaklanacağımız tasarım modellerinden biri Strateji Tasarım Modelidir. JavaScript çok esnek olduğu için, bu yazıda göreceğimiz gibi, Strateji gibi tasarım kalıplarını sağlam kılar.

Bu yazıda Strateji Tasarım Modelini inceleyeceğiz. Bu, bir veya daha fazlasını kapsayan iyi bilinen bir kalıptır. stratejiler (veya algoritmalar) bir görev yapmak. Bu kapsüllenmiş stratejilerin tümü aynı imzaya sahiptir, dolayısıyla bağlam ( arayüz) aynı veya farklı nesneyle ne zaman uğraştıklarını asla bilemez (veya strateji). Bu, her bir stratejinin, uygulamamızın ömrü boyunca programımızın farkına bile varmadan birçok kez değiştirilebileceği anlamına gelir.

Ne tür nesneler söz konusudur?

Strateji modelinde, bu iki nesne her zaman dahil edilir:

  1. Bağlam
  2. strateji

bu Bağlam için her zaman bir referansa veya işaretçiye sahip olmalıdır. kullanılan mevcut strateji. Bu, 200 stratejimiz varsa, diğer 199’un kullanılmasının isteğe bağlı olduğu anlamına gelir. Bunları “etkin değil” olarak düşünebilirsiniz.

bu Bağlam arayüzü de sağlar arayan kişiye. bu arayan bu müşteri. bu arayan işlerini gerçekleştirmek için stratejilerden herhangi birini kullanabilir ve ayrıca mevcut stratejiyi talep üzerine herhangi bir zamanda başka bir strateji ile değiştirebilirler.

Gerçek strateji yürütüldüğünde kullanılacak olan yürütme mantığını kendisi için uygular.

Güçlü

Normal bir işlev uygulamasında, işlev genellikle bir şeyler yapar ve bir değer döndürür. Bir temel olduğunda Strateji Tasarım Modelinde (Bağlam) sınıf ve bir Strateji Stratejiyi çağıran ve sonucu (başka bir deyişle aynı şey) döndüren bir işlev gibidir.

Ama varken iki veya daha fazla strateji, mesele şu ki, strateji arayan tarafından kontrol edilen birçok stratejiden biri olabilir.

Buradaki en büyük fayda, tanımlayabilmemizdir. istediğimiz kadar strateji ve desen olması gerektiği gibi yazıldığı sürece, kodun davranışında tek bir değişiklik ipucu vermeden talep üzerine kullanılmak üzere her biri arasında geçiş yapın.

Bir Stratejinin uygulamaları değişebilir, ancak bağlam tarafından beklendiği gibi aynı imzayı korudukları sürece, kodda gereksiz değişiklikler yaşamaya gerek yoktur.

İşte bu akışı gösteren bir diyagram:

uygulama

İlk uygulamamız getirmeye odaklanacak. bir tanımlayacağız createFetcher döndüren fonksiyon arayüz alıcılar oluşturmak için. Bu alıcılar müşteri tarafından oluşturulabilir ve istedikleri şekilde uygulanabilir. bir url aldıkları sürece, yanıtını alır ve döndürür.

kullanacağız aksiyolar istek kitaplığı, düğümün yerel https modülü ve düğüm getirme Her biri bir strateji olarak uygulanacak kütüphane.

Toplamda 3 stratejimiz olacak:

const axios = require('axios').default
const https = require('https')
const fetch = require('node-fetch')

function createFetcher() {
  const _identifer = Symbol('_createFetcher_')
  let fetchStrategy

  const isFetcher = (fn) => _identifer in fn

  function createFetch(fn) {
    const fetchFn = async function _fetch(url, args) {
      return fn(url, args)
    }
    fetchFn[_identifer] = true
    return fetchFn
  }

  return {
    get fetch() {
      return fetchStrategy
    },
    create(fn) {
      return createFetch(fn)
    },
    use(fetcher) {
      if (!isFetcher(fetcher)) {
        throw new Error(`The fetcher provided is invalid`)
      }
      fetchStrategy = fetcher
      return this
    },
  }
}

const fetcher = createFetcher()

const axiosFetcher = fetcher.create(async (url, args) => {
  try {
    return axios.get(url, args)
  } catch (error) {
    throw error
  }
})

const httpsFetcher = fetcher.create((url, args) => {
  return new Promise((resolve, reject) => {
    const req = https.get(url, args)
    req.addListener('response', resolve)
    req.addListener('error', reject)
  })
})

const nodeFetchFetcher = fetcher.create(async (url, args) => {
  try {
    return fetch(url, args)
  } catch (error) {
    throw error
  }
})

fetcher.use(axiosFetcher)

bizim içinde createFetcher fonksiyon bu satırı oluşturduk: const _identifer = Symbol('_createFetcher_')

Bu satır önemlidir çünkü oluşturulan her stratejinin aslında bir stratejidir aksi takdirde programımız, iletilen herhangi bir nesneyi bir strateji olarak ele alacaktır. Herhangi bir şeyin bir strateji olarak ele alınması olumlu bir avantaj gibi görünebilir, ancak yanlış adım atarsak hata ayıklama deneyimimizi kolayca engelleyebilecek kodumuzu hatalara daha yatkın hale getiren geçerliliğini kaybederiz.

Symbol bize benzersiz bir değişken döndürür tanım. Aynı zamanda bağlamın uygulanması içinde gizlibu yüzden nesnelerin bizim dışımızda yaratılmasının bir yolu yoktur. create işlev bir strateji olarak ele alınacaktır. Kamuya açık olarak yapılan yöntemi kullanmak zorunda kalacaklardı. arayüz tarafından sağlanan bağlam.

Müşteri aradığında use gönderiyor axiosFetcher mevcut strateji olarak kullanılacak ve daha sonra müşteri aracılığıyla başka bir stratejide değiş tokuş yapana kadar bir referans olarak bağlanacaktır. use.

Artık verileri almak için üç stratejimiz var:

const url = 'https://google.com'

fetcher.use(axiosFetcher)

fetcher
  .fetch(url, { headers: { 'Content-Type': 'text/html' } })
  .then((response) => {
    console.log('response using axios', response)
    return fetcher.use(httpsFetcher).fetch(url)
  })
  .then((response) => {
    console.log('response using node https', response)
    return fetcher.use(nodeFetchFetcher).fetch(url)
  })
  .then((response) => {
    console.log('response using node-fetch', response)
  })
  .catch((error) => {
    throw error instanceof Error ? error : new Error(String(error))
  })

Yaşasın! Şimdi bunun kodda nasıl uygulanabileceğini gördük. Ancak gerçek dünyada buna ihtiyacımız olan bir durum düşünebilir miyiz? Aslında bolca düşünebilirsiniz! Ancak, bu kalıp hakkında ilk kez okuyorsanız, o zaman pratikte bir senaryo görmedikçe önceden bir senaryo düşünmenin zor olabileceğini anlıyorum.

Bu gönderide ele aldığımız örnekler, model uygulamasını göstermektedir, ancak bunu okuyan herhangi biri, “Yanıtı almak ve buna bir gün demek için doğrudan bir tane benzeri axios kullanabiliyorken neden üç alıcı stratejisi uygulamakla uğraşasınız?” Diye sorabilir.

Yaklaşan örnekte, Strateji Tasarım Modelinin olduğu bir senaryonun üzerinden geçeceğiz. kesinlikle gerekli.

Farklı veri türlerini işleme

Strateji modelinin en çok parladığı yer, şunun gibi bir şey yaparken farklı veri türlerini işlememiz gerektiğidir. sıralama.

Önceki örneklerde biz gerçekten umursamadı Herhangi bir veri türü hakkında, çünkü sadece biraz yanıt istedik. Ancak, bir şey koleksiyonu aldığımızda ve onları kategorize etmek gibi dar bir görev yapmamız gerektiğinde ne olur? Ya olmaları gerekiyorsa doğru sıralanmış?

Her birinin başka bir veri türünün koleksiyonu olduğu birkaç koleksiyonu sıralamamız gerektiğinde, yalnızca yerel olanı kullanamayız. .sort çünkü her değer “daha az” ve “daha büyük” açısından farklı şekilde ele alınabilir.

Strateji Modelini kullanabilir ve çalışma zamanında kolayca kullanılabilen farklı sıralama algoritmaları tanımlayabiliriz. istek üzerine bunları birbirinin yerine kullan.

Bu koleksiyonları düşünün:

const nums = [2, -13, 0, 42, 1999, 200, 1, 32]
const letters = ['z', 'b', 'm', 'o', 'hello', 'zebra', 'c', '0']
const dates = [
  new Date(2001, 1, 14),
  new Date(2000, 1, 14),
  new Date(1985, 1, 14),
  new Date(2020, 1, 14),
  new Date(2022, 1, 14),
]

const elements = [
  document.getElementById('submitBtn'),
  document.getElementById('submit-form'),
  ...document.querySelectorAll('li'),
]

bir oluşturabiliriz Sort strateji sınıfı ve bir Sorter bağlam sınıfı.

Sınıf olmaları gerekmediğini unutmayın. Uygulamayı biraz çeşitlendirmek için şimdi sınıfları kullanmayı seçiyoruz:

const sorterId = Symbol('_sorter_')

class Sort {
  constructor(name) {
    this[sorterId] = name
  }

  execute(...args) {
    return this.fn(...args)
  }

  use(fn) {
    this.fn = fn
    return this
  }
}

class Sorter {
  sort(...args) {
    return this.sorter.execute.call(this.sorter, ...args)
  }

  use(sorter) {
    if (!(sorterId in sorter)) {
      throw new Error(`Please use Sort as a sorter`)
    }
    this.sorter = sorter
    return this
  }
}

const sorter = new Sorter()

Oldukça düz ileri. Sorter bir referans tutar Sort şu anda oluyor Kullanılmış. Bu, arama sırasında alınacak olan sıralama işlevidir. sort. Her biri Sort örnek bir stratejidir ve use.

bu Sorter stratejiler hakkında hiçbir şey bilmiyor. Tarih sıralayıcı, sayı sıralayıcı vb. olduğunu bilmez. sadece Sort’un yürütme yöntemini çağırır.

Ancak müşteri bilir hepsi hakkında Sort stratejilerin yanı sıra örnekleri de kontrol eder ve kontrol eder. Sorter:

const sorter = new Sorter()

const numberSorter = new Sort('number')
const letterSorter = new Sort('letter')
const dateSorter = new Sort('date')
const domElementSizeSorter = new Sort('dom-element-sizes')

numberSorter.use((item1, item2) => item1 - item2)
letterSorter.use((item1, item2) => item1.localeCompare(item2))
dateSorter.use((item1, item2) => item1.getTime() - item2.getTime())
domElementSizeSorter.use(
  (item1, item2) => item1.scrollHeight - item2.scrollHeight,
)

Bununla birlikte, bunu buna göre ele almak tamamen bize (müşteri) bağlıdır:

function sort(items) {
  const type = typeof items[0]
  sorter.use(
    type === 'number'
      ? numberSorter
      : type === 'string'
      ? letterSorter
      : items[0] instanceof Date
      ? dateSorter
      : items[0] && type === 'object' && 'tagName' in items[0]
      ? domElementSizeSorter
      : Array.prototype.sort.bind(Array),
  )
  return [...items].sort(sorter.sort.bind(sorter))
}

Artık sağlam bir yapıya sahibiz 15 hat fonksiyonu bu sıralayabilir 4 farklı varyasyon koleksiyonların!

console.log('Sorted numbers', sort(nums))
console.log('Sorted letters', sort(letters))
console.log('Sorted dates', sort(dates))

strateji-tasarım-desen-sıralı-koleksiyonlar-sonuç.png

JavaScript’teki Strateji Tasarım Modelinin gücü de buradadır.

JavaScript’in işlevleri değerler olarak ele almasının doğası gereği, bu kod örneği, bu yetenekle kendi avantajına birleşir ve Strateji modeliyle sorunsuz bir şekilde çalışır.

Çözüm

Ve bu yazının sonu burada bitiyor! Umarım bunu faydalı bulmuşsunuzdur ve gelecekte daha faydalı ipuçları için bizi izlemeye devam edin!!

Bir cevap yazın

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