JavaScript’teki 4 Tehlikeli Problem Oluşturucu Tasarım Modeliyle Kolayca Çözüldü – JSManifest

JavaScript'teki 4 Tehlikeli Problem Oluşturucu Tasarım Modeliyle Kolayca Çözüldü – JSManifest

JavaScript’te uygulamalar geliştirirken bazen karmaşık nesneler oluşturmakta zorlanabilirsiniz. Kodunuzda bu belirli noktaya ulaştığında, uygulamanız büyüdükçe çok daha karmaşık hale gelebileceğinden daha önemli hale gelir.

karmaşık“Birkaç biçimde olabilir. Biri, belirli nesnelerin farklı varyasyonlarını oluşturmaya çalışırken kodunuz tekrarlanıyor olabilir. Bir diğeri, bu nesnelerin varyasyonlarını oluşturmaya çalışmanın oldukça uzun olabileceği olabilir, çünkü mantığı bir yerde dev bir blokta yapmak zorunda kalmak, örneğin kurucu bir blok sınıf.

Bu makale bu sorunları gözden geçirecek ve JavaScript’teki Oluşturucu Tasarım Modelinin bu sorunları nasıl daha az sorun haline getireceğini gösterecektir.

Peki, Oluşturucu kalıbının yapabileceği sorunlar nelerdir? kolayca çözmek?

Önce bir örneğe bakalım olmadan oluşturucu deseni ve ardından bir örnek ile birlikte oluşturucu kalıbı, böylece devam ederken aklında görsel bir kod örneği olan tek kişi ben değilim:

Aşağıdaki kod örneklerinde, bir Frog sınıf. Bunu yapmak için öyleymiş gibi davranacağız. Frog sınıfın vahşi doğada sorunsuz bir şekilde yaşayabilmeleri ve dışarı çıkabilmeleri için, iki göz, dört bacak, bir koku, bir dil ve bir kalp. Şimdi açıkçası gerçek dünyada çok daha fazlası var ve bir koku yaşayabilmek, ancak her şey hakkında tamamen gerçeğe uygun olmaktansa onu hem basit hem de ilginç tutacağız. Başka bir gönderide başka bir zamanda gerçeklerimizi %100 doğru hale getirebiliriz 🙂

Oluşturucu deseni olmadan

class Frog {
  constructor(name, gender, eyes, legs, scent, tongue, heart, weight, height) {
    this.name = name
    this.gender = gender
    this.eyes = eyes
    this.legs = legs
    this.scent = scent
    this.tongue = tongue
    this.heart = heart
    if (weight) {
      this.weight = weight
    }
    if (height) {
      this.height = height
    }
  }
}

Oluşturucu deseni ile

class FrogBuilder {
  constructor(name, gender) {
    this.name = name
    this.gender = gender
  }

  setEyes(eyes) {
    this.eyes = eyes
    return this
  }

  setLegs(legs) {
    this.legs = legs
    return this
  }

  setScent(scent) {
    this.scent = scent
    return this
  }

  setTongue(tongue) {
    this.tongue = tongue
    return this
  }

  setHeart(heart) {
    this.heart = heart
    return this
  }

  setWeight(weight) {
    this.weight = weight
    return this
  }

  setHeight(height) {
    this.height = height
    return this
  }
}

Şimdi bu biraz abartılı görünüyor çünkü oluşturucu desen örneği kodda daha büyük. Ancak, potansiyel bir kurbağa uygulamasının geliştirilmesi sırasında meydana gelebilecek tüm durumları daha derine inerseniz, bu iki örneğe bakarak, oluşturucu deseninin uygulandığı kod örneğinin, basitliği, sürdürülebilirliği ve sürdürülebilirliği teşvik etmede yavaş yavaş yükseleceğini göreceksiniz. sağlam işlevsellik uygulamak için daha fazla fırsat açmak.

Oluşturucu Tasarım Modelinin JavaScript’te kolayca çözebileceği 4 sorun:

1. Kod dağınıklığı ve karışıklık

Büyük boyutlu fonksiyon bloklarını geliştirmenin dikkatsizliğinden kaynaklanan hataların ve kazaların meydana gelmesi nadir değildir. Ayrıca, var olduğunda çok fazla şey oluyor tek bir blokta, kafa karıştırmak kolay.

Peki, fonksiyon bloklarında “çok fazla şey oluyor” olduğunda ne tür bir duruma girersiniz? kurucu?

Uygulanan ilk kod örneğimize geri dönersek olmadan oluşturucu deseni, bir örneğe uygulamadan önce iletilen argümanları kabul etmek için bazı ek mantık eklememiz gerektiğini varsayalım:

class Frog {
  constructor(name, gender, eyes, legs, scent, tongue, heart, weight, height) {
    if (!Array.isArray(legs)) {
      throw new Error('Parameter "legs" is not an array')
    }
    
    this.name = name.charAt(0).toUpperCase() + name.slice(1)
    this.gender = gender
    
    
    
    
    
    this.eyes = Array.isArray(eyes) ? { left: eye[0], right: eye[1] } : eyes
    this.legs = legs
    this.scent = scent
    
    
    const isOld = 'tongueWidth' in tongue
    if (isOld) {
      const newTongue = { ...tongue }
      delete newTongue['tongueWidth']
      newTongue.width = tongue.width
      this.tongue = newTongue
    } else {
      this.tongue = newTongue
    }
    this.heart = heart
    if (typeof weight !== 'undefined') {
      this.weight = weight
    }
    if (typeof height !== 'undefined') {
      this.height = height
    }
  }
}

const larry = new Frog(
  'larry',
  'male',
  [{ volume: 1.1 }, { volume: 1.12 }],
  [{ size: 'small' }, { size: 'small' }, { size: 'small' }, { size: 'small' }],
  'sweaty socks',
  { tongueWidth: 18, color: 'dark red', type: 'round' },
  { rate: 22 },
  6,
  3.5,
)

Yapıcımız biraz uzun ve bazı durumlarda çok fazla mantığa bile gerek yokmuş gibi görünüyor. Farklı parametreleri işleme mantığı ile karmaşıktır. Bu, özellikle bunun kaynak koduna uzun süredir bakmadıysak kafa karıştırıcı olabilir.

Bir kurbağa uygulaması geliştirirken ve bir örneğini başlatmak istediğimizde Frog, dezavantajı, fonksiyon imzasını takip etme açısından her parametreyi %100’e yakın mükemmel hale getirdiğimizden veya inşaat aşamasında bir şeyler atacağından emin olmamız gerektiğidir. türünü iki kez kontrol etmemiz gerekirse eyes bir noktada, aradığımız koda ulaşmak için kod karmaşasını taramamız gerekecekti. Sonunda aradığınız satırları bulsaydınız, ancak daha sonra referans veren ve onu etkileyen başka bir kod satırı olduğunu fark ederseniz kafanız karışır mıydı? aynı parametre sadece 50 satır yukarıda? Şimdi ne olacağını anlayabilmek için geri dönüp bunları taramanız gerekiyor.

bir daha bakarsak FrogBuilder yapıcıyı daha önceki bir örnekten alırsak, yapıcıyı daha fazlasını hissedecek şekilde basitleştirebiliriz. “doğal” karışıklığı ortadan kaldırırken. Hala ekstra doğrulamaları yapıyor olurduk, sadece kendi küçük yöntemlerinde izole edilmiş olurdu, bu da yapıcı modelinin kalbi ve ruhudur.

2. Okunabilirlik

En son kod örneğine bir göz atarsak, bu farklı işleme varyasyonlarını aynı anda işlememiz gerektiğinden, okuması zaten biraz zorlaşıyor. anlamaktan başka çare yok tüm örneğini oluşturmak istiyorsak bir kerede bir şey Frog.

Ek olarak, bazı belgeler sağlamamız gerekiyor, aksi takdirde neden dünyada olduğundan emin olamayız. tongueWidth yeniden adlandırılmak width. Bu saçma!

Örneği oluşturucu desenini kullanacak şekilde dönüştürürsek, işleri daha kolay okunabilir hale getirebiliriz:

class FrogBuilder {
  constructor(name, gender) {
    
    this.name = name.charAt(0).toUpperCase() + name.slice(1)
    this.gender = gender
  }

  formatEyesCorrectly(eyes) {
    return Array.isArray(eyes) ? { left: eye[0], right: eye[1] } : eyes
  }

  setEyes(eyes) {
    this.eyes = this.formatEyes(eyes)
    return this
  }

  setLegs(legs) {
    if (!Array.isArray(legs)) {
      throw new Error('"legs" is not an array')
    }
    this.legs = legs
    return this
  }

  setScent(scent) {
    this.scent = scent
    return this
  }

  updateTongueWidthFieldName(tongue) {
    const newTongue = { ...tongue }
    delete newTongue['tongueWidth']
    newTongue.width = tongue.width
    return newTongue
  }

  setTongue(tongue) {
    const isOld = 'tongueWidth' in tongue
    this.tongue = isOld
      ? this.updateTongueWidthFieldName(tongue, tongue.tongueWidth)
      : tongue
    return this
  }

  setHeart(heart) {
    this.heart = heart
    return this
  }

  setWeight(weight) {
    if (typeof weight !== 'undefined') {
      this.weight = weight
    }
    return this
  }

  setHeight(height) {
    if (typeof height !== 'undefined') {
      this.height = height
    }
    return this
  }

  build() {
    return new Frog(
      this.name,
      this.gender,
      this.eyes,
      this.legs,
      this.scent,
      this.tongue,
      this.heart,
      this.weight,
      this.height,
    )
  }
}

const larry = new FrogBuilder('larry', 'male')
  .setEyes([{ volume: 1.1 }, { volume: 1.12 }])
  .setScent('sweaty socks')
  .setHeart({ rate: 22 })
  .setWeight(6)
  .setHeight(3.5)
  .setLegs([
    { size: 'small' },
    { size: 'small' },
    { size: 'small' },
    { size: 'small' },
  ])
  .setTongue({ tongueWidth: 18, color: 'dark red', type: 'round' })
  .build()

Kodumuzu yapma yeteneği kazandık çok daha okunabilir birkaç şekilde:

  1. Yöntemlerin adları yeterince kendi kendine belgeleme

    • updateTongueWidthFieldName bize ne yaptığını kolayca tanımlar ve Niye bunu yapıyor. Alan adını güncellediğini biliyoruz. Ve biz de biliyoruz Niye çünkü kelime “Güncelleme” zaten demek güncel hale getirmek! Bu kendi kendini belgeleyen kod, bazı alan adlarının eski olduğunu ve yeni alan adını kullanmak için değiştirilmesi gerektiğini varsaymamıza yardımcı olur.
  2. Yapıcı kısa ve basitleştirilmiştir.

    • Diğer özellikleri ayarlamak gayet iyi sonra!
  3. Yeni bir parametre başlatırken her parametreyi açıkça anlayabilir Frog

    • okumak gibi ingilizce. Gözleri, bacakları vb. Açıkça ayarlıyorsunuz ve sonunda bir oluşturmak için inşa yöntemini çağırıyorsunuz. Frog.
  4. Her mantık artık kolayca takip edebileceğimiz ayrı bloklarda izole edilmiştir.

    • Bazı değişiklikler yaparken sadece tek bir şeye odaklanmanız gerekir, o da fonksiyon bloklarında izole edilen şeydir.

3. Kontrol eksikliği

Bu listedeki en önemli şey, daha fazla faydadan yararlanmaktır. kontrol uygulanması üzerinde. Oluşturucu örneğinden önce, mümkün yapıcıya daha fazla kod yazmak için, ancak oraya ne kadar çok kod yapıştırmaya çalışırsanız, okunabilirliği o kadar çok düşürür ve bu da dağınıklığa ve kafa karışıklığına neden olur.

yapabildiğimizden beri izole etmek kendi fonksiyon bloklarının her birine uygulama ayrıntıları, artık birçok yönden daha hassas kontrole sahibiz.

Bunun bir yolu, daha fazla sorun eklemeden doğrulamalar ekleyebilmemizdir, bu da inşaat aşamasını daha sağlam hale getirir:

setHeart(heart) {
  if (typeof heart !== 'object') {
    throw new Error('heart is not an object')
  }
  if (!('rate' in heart)) {
    throw new Error('rate in heart is undefined')
  }
  
  
  if (typeof heart === 'function') {
    this.heart = heart({
      weight: this.weight,
      height: this.height
    })
  } else {
    this.heart = heart
  }

  return this
}

validate() {
  const requiredFields = ['name', 'gender', 'eyes', 'legs', 'scent', 'tongue', 'heart']
  for (let index = 0; index < requiredFields.length; index++) {
    const field = requiredFields[index]
    
    if (!(field in this)) {
      return false
    }
  }
  return true
}

build() {
  const isValid = this.validate(this)
  if (isValid) {
  return new Frog(
    this.name,
    this.gender,
    this.eyes,
    this.legs,
    this.scent,
    this.tongue,
    this.heart,
    this.weight,
    this.height,
  )
  } else {
    
    console.error('Parameters are invalid')
  }
}

Yapıcının her bir parçasının, doğrulamaların yanı sıra bir validate nihayet oluşturmadan önce gerekli tüm alanların ayarlandığından emin olmak için yöntem Frog.

Bir parametrenin orijinal dönüş değerini oluşturmak için daha fazla özel girdi veri türü eklemek için bu açılan fırsatlardan da yararlanabiliriz.

Örneğin, arayanın iletebileceği daha fazla özel yol ekleyebiliriz. eyeshatta onlara sağlamak için daha fazla daha önce sağladığımızdan daha fazla kolaylık:

formatEyesCorrectly(eyes) {
  
  
  if (Array.isArray(eyes)) {
    return {
      left: eye[0],
      right: eye[1]
    }
  }
  
  if (typeof eyes === 'number') {
    return {
      left: { volume: eyes },
      right: { volume: eyes },
    }
  }
  
  
  if (typeof eyes === 'function') {
    return eyes(this)
  }

    
  return eyes
}

setEyes(eyes) {
  this.eyes = this.formatEyes(eyes)
  return this
}

Bu şekilde, arayanın istediği giriş türleri varyasyonunu seçmesini kolaylaştırır:


larry.setEyes([{ volume: 1 }, { volume: 1.2 }])


larry.setEyes(1.1)


larry.setEyes(function(instance) {
  let leftEye, rightEye
  let weight, height
  if ('weight' in instance) {
    weight = instance.weight
  }
  if ('height' in instance) {
    height = instance.height
  }

  if (weight > 10) {
    
    leftEye = { volume: 5 }
    rightEye = { volume: 5 }
  } else {
    const volume = someApi.getVolume(weight, height)
    leftEye = { volume }
    
    rightEye = { volume: instance.gender === 'female' ? 0.8 : 1 }
  }

  return {
    left: leftEye,
    right: rightEye,
  }
})


larry.setEyes({
  left: { volume: 1.5 },
  right: { volume: 1.51 },
})

4. Genel Bilgi (Çözen: Şablonlama)

Gelecekte karşılaşabileceğimiz bir endişe, bazı tekrarlayan kodlarla sonuçlanmamızdır.

Örneğin, geriye dönüp baktığımızda Frog sınıf, belirli kurbağa türleri yaratmak istediğimizde, bazılarının aynı özelliklere sahip olabileceğini düşünüyor musunuz?

Gerçek bir dünya senaryosunda, kurbağaların farklı varyasyonları vardır. A karakurbağası örneğin bir kurbağa türüdür, ama tüm kurbağalar kurbağa değildir. Yani bu bize şunu söylüyor bazı ayırt edici özellikler var bir kurbağanın normal kurbağalara ait olmamalı.

Kurbağalar ve kurbağalar arasındaki bir fark, zamanlarının çoğunu suda geçiren normal kurbağaların aksine, kara kurbağalarının zamanlarının çoğunu karada geçirmesidir. Ek olarak, karakurbağaların kuru engebeli bir derisi vardır, oysa normal kurbağaların derisi biraz yapışkandır.

Bu, bir kurbağanın her başlatılışında nasıl olduğundan emin olmamız gerektiği anlamına gelir. sadece bazı değerler bazı değerlerin yanı sıra bunu da yapabilir zorunlu üstesinden gel.

kendimize dönelim Frog yapıcı ve iki yeni parametre ekleyin: habitatve skin:

class Frog {
  constructor(
    name,
    gender,
    eyes,
    legs,
    scent,
    tongue,
    heart,
    habitat,
    skin,
    weight,
    height,
  ) {
    this.name = name
    this.gender = gender
    this.eyes = eyes
    this.legs = legs
    this.scent = scent
    this.tongue = tongue
    this.heart = heart
    this.habitat = habitat
    this.skin = skin
    if (weight) {
      this.weight = weight
    }
    if (height) {
      this.height = height
    }
  }
}

Bu kurucuda iki basit değişiklik yapmak zaten biraz kafa karıştırıcıydı! Bu nedenle oluşturucu deseni önerilir. eğer koyarsak habitat ve skin sonunda parametreler, hatalara neden olabilir çünkü weight ve height Yapabilmek ikisi de isteğe bağlı olduğundan muhtemelen tanımsız olabilir! Ve isteğe bağlı olduklarından, arayan kişi bunları iletmezse, o zaman habitat ve skin yanlışlıkla onlar için kullanılacaktır. Evet!

düzenleyelim FrogBuilder desteklemek habitat ve skin:

setHabitat(habitat) {
  this.habitat = habitat
}

setSkin(skin) {
  this.skin = skin
}

Şimdi 2 ayrı kurbağa ve 1 normal kurbağa oluşturmamız gerektiğini varsayalım:


const sally = new FrogBuilder('sally', 'female')
  .setEyes([{ volume: 1.1 }, { volume: 1.12 }])
  .setScent('blueberry')
  .setHeart({ rate: 12 })
  .setWeight(5)
  .setHeight(3.1)
  .setLegs([
    { size: 'small' },
    { size: 'small' },
    { size: 'small' },
    { size: 'small' },
  ])
  .setTongue({ width: 12, color: 'navy blue', type: 'round' })
  .setHabitat('water')
  .setSkin('oily')
  .build()


const kelly = new FrogBuilder('kelly', 'female')
  .setEyes([{ volume: 1.1 }, { volume: 1.12 }])
  .setScent('black ice')
  .setHeart({ rate: 11 })
  .setWeight(5)
  .setHeight(3.1)
  .setLegs([
    { size: 'small' },
    { size: 'small' },
    { size: 'small' },
    { size: 'small' },
  ])
  .setTongue({ width: 12.5, color: 'olive', type: 'round' })
  .setHabitat('land')
  .setSkin('dry')
  .build()


const mike = new FrogBuilder('mike', 'male')
  .setEyes([{ volume: 1.1 }, { volume: 1.12 }])
  .setScent('smelly socks')
  .setHeart({ rate: 15 })
  .setWeight(12)
  .setHeight(5.2)
  .setLegs([
    { size: 'medium' },
    { size: 'medium' },
    { size: 'medium' },
    { size: 'medium' },
  ])
  .setTongue({ width: 12.5, color: 'olive', type: 'round' })
  .setHabitat('land')
  .setSkin('dry')
  .build()

Peki tekrarlayan kod bunun neresinde?

Yakından bakarsak, kurbağanın sözlerini tekrarlamamız gerektiğine dikkat edin. habitat ve skin ayarlayıcılar. Ya sadece kara kurbağalarına özel 5 ayarlayıcı daha olsaydı? Bunu manuel olarak uygulamamız gerekecek şablon kurbağalar için onları her yarattığımızda – aynı şey normal kurbağalar için de geçerli.

Yapabileceğimiz şey, bir şabloncunormalde konvansiyonel olarak adlandırılan Müdür.

Yönetmen, nesneler oluşturmak için adımların yürütülmesinden sorumludur – genellikle, bu durumda olduğu gibi, nihai nesneyi oluştururken önceden tanımlanabilecek bazı ortak yapılar olduğunda. karakurbağası.

Dolayısıyla, karakurbağalar arasındaki ayırt edici özellikleri manuel olarak ayarlamak yerine, yönetmenin bizim için bu şablonu oluşturmasını sağlayabiliriz:

class ToadBuilder {
  constructor(frogBuilder) {
    this.builder = frogBuilder
  }
  createToad() {
    return this.builder.setHabitat('land').setSkin('dry')
  }
}

let mike = new FrogBuilder('mike', 'male')
mike = new ToadBuilder(mike)
  .setEyes([{ volume: 1.1 }, { volume: 1.12 }])
  .setScent('smelly socks')
  .setHeart({ rate: 15 })
  .setWeight(12)
  .setHeight(5.2)
  .setLegs([
    { size: 'medium' },
    { size: 'medium' },
    { size: 'medium' },
    { size: 'medium' },
  ])
  .setTongue({ width: 12.5, color: 'olive', type: 'round' })
  .build()

Bu şekilde, uygulamaktan kaçınırsınız. tüm kara kurbağalarının ortak noktası ve yapabilir sadece ihtiyacınız olan özelliklere odaklanın. Bu, yalnızca kara kurbağalarına özel daha fazla özellik olduğunda daha kullanışlı hale gelir.

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