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

JavaScript’te, dilde yerleşik bir otomatik çöp toplama mekanizmasına sahip olduğumuz için şanslıyız. Var bazı hafızayı kendi başımıza yönetmenin gerekli olduğu durumlar. Bu, müşterilerin yararlanabileceği nesnelerle ortak noktaları paylaşmayı amaçladığı için Flyweight Tasarım Modelinin kullanışlı olabileceği yerdir. Bu, ölçeklenebilir uygulamalar yazmanın verimli bir yoludur, çünkü kullanıcıların mümkün olan en az miktarda bellek kullanmasına izin vermemize yardımcı olur.

Bu gönderide, JavaScript’teki Flyweight Tasarım Modelinin gücünü gözden geçireceğiz ve bellek açısından daha verimli uygulamalar oluşturmak için ondan yararlanacağız. Ortaya çıkan sorunları gözden geçireceğiz ve sinek ağırlığı modelinin hepsini nasıl ortadan kaldırdığını göstereceğiz.

Daha önce bir JavaScript kitaplığı kullandıysanız, bir JavaScript kitaplığı, çerçeve veya hatta DOM aracılığıyla olsun, size verilen bir sineklik modelinin bazı varyasyonları üzerinde doğrudan çalışmış olmanız için iyi bir şans vardır.

Nesneleri DOM öğeleri olarak temsil eden bu nesne dizisine bir göz atalım:

const elems = [
  {
    tagName: 'div',
    style: { width: '28.5px', height: '20px' },
    children: [
      { tagName: 'input', style: { width: '28.5px', height: '20px' } },
      { tagName: 'input', style: { width: '28.5px', height: '20px' } },
      { tagName: 'select', style: { width: '28.5px', height: '20px' } },
      { tagName: 'input', style: { width: '28.5px', height: '20px' } },
    ],
  },
]

eğer bakarsanız children dizi, üç nesne olduğuna dikkat edin. yapısal olarak özdeş:

flyweight-elems-üç-özdeş-yapısal-eşdeğer-objects.png

Bu zaten bir sorun çünkü bu uygulamaya devam edersek ve projemiz büyürse programımız performansta büyük bir darbe alacak çünkü hepsi yapısal olarak eşdeğer olmalarına rağmen bellekte üç ayrı nesne oluşturacak. 1000 tane olduğunu düşünün.

Sinek ağırlık tasarım modelinin gerçek örneklerini gözden geçirdiğimizde, temel olarak sinek ağırlığın perde arkasında yapmayı amaçladığı şey budur:

const inputElement = {
  tagName: 'input',
  style: { width: '28.5px', height: '20px' },
}

const elems = [
  {
    tagName: 'div',
    style: { width: '28.5px', height: '20px' },
    children: [
      inputElement,
      inputElement,
      { tagName: 'select', style: { width: '28.5px', height: '20px' } },
      inputElement,
    ],
  },
]

Nasıl olduğunu fark et inputElement defalarca bahsedilir.

Farklı nesne yapılarındaki örneklerin üzerinden geçeceğiz (örneğin sınıflar gibi) ama sonuçta bu kavram her zaman uygulanır.


Sinek ağırlığı modelini düşünmenin iyi bir yolu “paylaşılan şeyler” dir. Bir önceki örneğimizde paylaşılan en inputElement nesne üç kere. Bellek kullanımını en az üç kez en aza indirdik.

Sık karşılaşabileceğiniz yaygın bir uygulama, bir tür almak bellekteki bazı önbellekteki nesneleri alma yöntemi:

class Coin {
  constructor(value) {
    this.value = value
  }
}

class CoinCollage {
  coins = new Map()

  create(value) {
    let coin = this.coins.get(value)
    if (!coin) {
      coin = new Coin(value)
      this.coins.set(value, coin)
    }
    return coin
  }
}

const coins = new CoinCollage()

const dime = coins.create(0.1)
const quarter = coins.create(0.25)
const dollar = coins.create(1.0)

console.log(coins)

Bu, kullanıcının hafızasını koruduğu için yeniden oluşturulması gerekmeyen önceden oluşturulmuş nesneleri yeniden kullanmak ve paylaşmak için harika bir tekniktir.

Gibi birçok kütüphanede kullanılır ts-morf genellikle bu yöntemlerin önüne şunun gibi bir şey koyar "getOrCreate<the rest of the variable's name>"

İçsel Durum

Flyweight uygulamaları genellikle, ona bellek açısından verimli kararlar vermek için kullanabileceğimiz bir içsel durum eklediğimizde kullanışlı hale gelir.

Daha önceki örneklerimizde CoinCollege sınıf, içsel durumumuzu burada tespit edebiliriz:

flyweight-tasarım-kalıp-iç-durum

İlk önce daha önce yaratılmış olanı yakalamaya çalıştık. Coin ile örnek value için sordu. Uygulamamız daha önce oluşturduysa, gereksiz yeniden oluşturmayı önleyebilir ve sadece öncekine geri dönebiliriz. Coin depolamıştık.

Bu, kütüphane yazarlarının en sık aradığı bir faydadır.

dışsal durum

Sinek ağırlığı modelinin bir diğer önemli rolü de dışsal durumdur. Bunlar var olan devletler sinek ağırlığının dışında uygulama ama çalışmak istiyorum ile birlikte sinek ağırlığı. Yaygın bir kullanım durumu, geri arama işlevleri tarafından gözlemlenen durumlardır:

class CoinCollage {
  #onCreate = undefined
  coins = new Map()

  create(value) {
    let coin = this.coins.get(value)
    if (!coin) {
      coin = new Coin(value)
      this.#onCreate?.(coin)
      this.coins.set(value, coin)
    }
    return coin
  }

  set onCreate(fn) {
    this.#onCreate = fn
  }
}

const coins = new CoinCollage()

const createdCoinValues = []

coins.onCreate = function onCreate(coin) {
  createdCoinValues.push(coin)

  console.log(
    `Created new coin of value ${coin.value}. The total number of coins is now ${createdCoinValues.length}`,
  )

  if (createdCoinValues.length >= 3) {
    console.warn(`You are 2 coins away from the maximum coins allowed`)
  }
}

const dime = coins.create(0.1)
const quarter = coins.create(0.25)
const dollar = coins.create(1.0)

flyweight-design-pattern-extrinsic-state.png

Bu yetenek yerinde olduğunda, daha fazla yaratımları durdurabiliriz. Coin iş mantığımız sadece ilk 5 jeton için geçerliyse. Bu, içsel durumumuz için harika bir arkadaştır!

Prototip Kalıtım

JavaScript geliştirme kariyerimin ilk aşamalarında kavramam zor olan bir şey, karar prototip kalıtım ve fabrika işlevleri arasında yapma. Çok uzun süredir üzerimde duran bir gizem, işlevlerin neden bu şekilde oluşturulduğunu sık sık gördüğümü bulmaktı:

function Calculator() {
  this.value = 0
}

Calculator.prototype.add = function add(num1, num2) {
  return num1 + num2
}

Calculator.prototype.subtract = function subtract(num1, num2) {
  return num2 - num1
}

Nesnelerin bu şekilde yaratılmasının aksine:

function makeCalculator() {
  let value = 0

  return {
    add(num1, num2) {
      return num1 + num2
    },
    subtract(num1, num2) {
      return num2 - num1
    },
  }
}

İlk örnekte, yeni örneklemelerimiz Calculator class, prototipinde tanımlanan aynı özellikleri/yöntemleri devralır ve yeniden kullanır. Bu şu anlama gelir:

flyweight-design-pattern-diagram-javascript.png

İkinci örnekte, yeni örneklemelerimiz makeCalculator fabrika vasiyeti olumsuzluk devral ve yeniden kullan add ve subtract ancak bunun yerine tamamen yeni bir add ve subtract şekil ve kod boyutu bakımından aynı olsalar bile işlev görürler.

flyweight-pattern-factor-function-version-in-javascript.png

İkisinin de genel olarak artıları ve eksileri var. Ancak sinek ağırlığı bağlamında prototip kalıtım önerilir. Diğer her şey için, her zaman fabrika işleviyle giderim ama bu, bu yazının kapsamı dışındadır.

Gerçek Dünya Kodu Örneği

bu süper ajan kütüphane, pratikte prototip kalıtımı kullanarak sineklik tasarım modelini sergiliyor. Rica etmek sınıf. Modern programlar, veri alma gibi görevleri yapmak için sık sık istekte bulunur. Mesele şu ki, bu istekler sonraki istekler için yeniden kullanılamaz. Bazılarının yeni örneklerini başlatmaları gerekiyor Request nesne. Programlar birlikte çalıştığında Request nesnelerde, mevcut duruma dayanmayan yöntemlerinin ve özelliklerinin kopyalarını yapmak gerçekten gereksizdir.

Bunun bir örneği, sorgulama yöntemi üzerinde Request nesne. Uygulama ayrıntıları neredeyse hiç değişmez, bu nedenle kopya oluşturmanın ve yeni nesnelere taşımanın bir anlamı yoktur.

Çö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.