This Banner is For Sale !!
Get your ad here for a week in 20$ only and get upto 15k traffic Daily!!!

The Power of Factory Design Pattern in JavaScript


Post thumbnail

As programmers we’re all the time attempting to make good selections after we write code. It isn’t all the time a straightforward job particularly when our code turns into bigger over time. Thankfully there are confirmed methods to choose one implementation over one other when the precise alternative arrives.

Should you’re new to programming, you may need not come throughout a state of affairs but the place you could have a fancy object and wanted to make use of the Manufacturing unit sample to summary away the complexities. Should you plan on persevering with your future with writing code, then this put up will provide help to.

On this put up we will likely be going over the Energy of Manufacturing unit Design Sample in JavaScript, which is one option to break a fancy object aside into easier objects to keep away from pointless complexity. Understand that we’ll observe the DRY precept as a finest observe.

After we consider a manufacturing facility in the actual world we consider some laboratory that makes stuff. That’s precisely what the manufacturing facility sample is after we translate it to code.

Let’s faux we’re constructing an MMORPG recreation the place we are going to go over the components that benefit from this sample and we are going to see the way it advantages our functions.

We could have a Recreation class, a Profile to create profiles when customers open our software program, and the 4 courses that profiles will create as characters for our customers to decide on:

class Magazine extends Character {}
class Thief extends Character {}
class Archer extends Character {}
class Warrior extends Character {}

class Profile {
  constructor(title, e mail = '') {
    this.title = title
    this.e mail = e mail
  }

  createCharacter(classType) {
    change (classType) {
      case 'archer':
        this.character = new Archer()
        return this.character
      case 'mage':
        this.character = new Mage()
        return this.character
      case 'thief':
        this.character = new Thief()
        return this.character
      case 'warrior':
        this.character = new Warrior()
        return this.character
      default:
        throw new Error(
          `Invalid class sort "${classType}". Select certainly one of: "archer", "mage", "thief", or "warrior"`,
        )
    }
  }

  synchronizeProfileContacts(anotherProfile) {
    // Do one thing to inherit anotherProfile's contacts
  }

  setName(title) {
    this.title = title
  }

  setEmail(e mail) {
    this.e mail = e mail
  }
}

class Recreation {
  constructor() {
    this.customers = {}
  }

  createUser(title) {
    const person = new Profile(title)
    this.customers[user.id] = person
    return person
  }
}

const recreation = new Recreation()
const bobsProfile = recreation.createUser('bob')
const bobsMage = bobsProfile.create('mage')
Enter fullscreen mode

Exit fullscreen mode

Three months later we determine we need to implement one other character class known as Shaman.

As a way to do this we now have to create the category:

class Shaman extends Character {}
Enter fullscreen mode

Exit fullscreen mode

After we need to permit customers to pick out the Shaman class after the replace and name profile.createCharacter we’ll get this error:

Error: Invalid class sort "shaman". Select one of: "archer", "mage", "thief", or "warrior"
Enter fullscreen mode

Exit fullscreen mode

That is as a result of we now have to alter the create methodology on the Profile class.

After we modify it to this, it’s going to work:

class Profile {
  constructor(title, e mail = '') {
    this.title = title
    this.e mail = e mail
  }

  createCharacter(classType) {
    change (classType) {
      case 'archer':
        this.character = new Archer()
        return this.character
      case 'mage':
        this.character = new Mage()
        return this.character
      case 'shaman':
        this.character = new Shaman()
        return this.character
      case 'thief':
        this.character = new Thief()
        return this.character
      case 'warrior':
        this.character = new Warrior()
        return this.character
      default:
        throw new Error(
          `Invalid class sort "${classType}". Select certainly one of: "archer", "mage", "shaman", "thief", or "warrior"`,
        )
    }
  }

  synchronizeProfileContacts(anotherProfile) {
    // Do one thing to inherit anotherProfile's contacts
  }

  setName(title) {
    this.title = title
  }

  setEmail(e mail) {
    this.e mail = e mail
  }
}
Enter fullscreen mode

Exit fullscreen mode

That is the issue the manufacturing facility design sample solves.

What if we needed so as to add 3 extra character courses? We have now to alter the implementation 1-3 instances.

Keep in mind after we talked about we have been going to observe the DRY precept, as each developer ought to? This violates that rule!

Should you’re new to programming this does not sound like a giant deal judging solely from the code we at present have. That is as a result of our Recreation class solely has a createUser methodology, however in the actual world MMORPG video games undoubtedly develop far more in code dimension because of the entire needed options that make it extra useful for leisure for his or her customers.

Our Recreation class will probably have tons of various strategies wanted to implement loads of options, comparable to createTerrain, createEquipment, createMonster, createAttack, createPotion, createRaid, createBuilding, createShop, and so forth.

Sadly each a kind of strategies more than likely have to be prolonged additional as a result of they are going to every have to create differing kinds. For instance the createEquipment may have to implement a option to create sword tools, staffs, boots, armors, which all more than likely want to provide additional variants of varieties like the kind of sword and boots.

So if we needed to implement all of these proper now we now have to go change each methodology precisely like we did after we first wrote our Shaman class, and we already suffered from our first error as a result of we forgot so as to add in Shaman within the implementation of our Profile.createUser methodology.

If we stopped with the factories right here then three months later it will shortly turn out to be overwhelming as a result of we’re compelled to leap to each methodology and alter them.

That is the place the manufacturing facility sample shines because the code grows bigger.

What if Profile.createCharacter may simply keep unchanged so we do not have to the touch it ever once more? It does not have to know which sort or variety of character class it creates. It simply must be given a personality class and retailer it in its occasion.

If we needed so as to add 10 extra character courses we now have to manually hunt the identical operate down and replace it regardless that the Profile does not care about what sort of character courses is being produced as a result of it solely cares about strategies like setName and synchronizeProfileContacts.

We are able to summary out that half and put it right into a manufacturing facility to produce these objects as an alternative:

class CharacterClassCreator {
  create(classType) {
    change (classType) {
      case 'archer':
        return new Archer()
      case 'mage':
        return new Mage()
      case 'shaman':
        return new Shaman()
      case 'thief':
        return new Thief()
      case 'warrior':
        return new Warrior()
      default:
        throw new Error(
          `Invalid class sort "${classType}". Select certainly one of: "archer", "mage", "thief", or "warrior"`,
        )
    }
  }
}
Enter fullscreen mode

Exit fullscreen mode

Our Profile class can look extra elegantly to accommodate this transformation:

class Profile {
  constructor(title, e mail = '') {
    this.title = title
    this.e mail = e mail
  }

  synchronizeProfileContacts(anotherProfile) {
    // Do one thing to inherit anotherProfile's contacts
  }

  setName(title) {
    this.title = title
  }

  setEmail(e mail) {
    this.e mail = e mail
  }

  setCharacter(character) {
    this.character = character
  }
}
Enter fullscreen mode

Exit fullscreen mode

We aren’t violating the DRY precept anymore. Hurray! We solely want to alter CharacterClassCreator if we needed to implement extra character courses to create. It is the one duty we set it to do–to produce completely different character class objects.

Here’s a visible of what we initially had previous to the manufacturing facility:

profile-create-without-factory-design-pattern

And that is what the Profile appears to be like like now:

profile-create-with-the-power-of-factory-design-pattern

Nice! We stored the profile wanting good and clear. We enabled our Profile class to solely concentrate on its logic.

Should you’re questioning the place the CharacterClassCreator stands on this, that is really what is going on behind the scenes:

profile-create-with-character-class-creator-factory

We added a center man (the manufacturing facility) to deal with the logic for creating character courses as an alternative. Any further each time we have to replace the implementation to that code we solely want to alter the CharacterCreationClass.

I hope you can begin noticing the profit by this stage. Keep in mind after we talked about different strategies our Recreation class will finally have comparable to createBuilding and createTerrain? If we apply the same manufacturing facility strategy to all of them it is going to be the identical course of. This enables every of these courses to concentrate on their very own logic.

Let’s proceed additional with our code.

In MMORPG video games, completely different character courses put on completely different tools.

For instance, magicians often use staffs, warriors put on heavy metal armor and carry swords, thieves carry one or two daggers, and archers use crossbows.

As well as there are often some perks if customers register an account and purchase some sort of membership to return with it.

Here’s what that may appear like:

class Tools {
  constructor(title) {
    this.title = title
  }
}

class CharacterClassCreator {
  async applyMembershipCode(code) {
    // return fetch(`https://www.mymmorpg.com/api/v1/membership-code?code=${code}`)
    //   .then((resp) => resp.json())
    //   .then((information) => information)
    return { equipments: [{ type: 'ore' }] }
  }

  async create(profile, classType) {
    const creatorMap = {
      archer: {
        Class: Archer,
      },
      mage: {
        Class: Mage,
      },
      shaman: {
        Class: Shaman,
      },
      thief: {
        Class: Thief,
      },
      warrior: {
        Class: Warrior,
      },
    }

    let character
    // Every character class has a distinct starter weapon
    let starterWeapon

    if (creatorMap[classType]) {
      const { Class, membership } = creatorMap[classType]
      character = new Class()

      if (character instanceof Archer) {
        starterWeapon = new Tools('crossbow')
      } else if (character instanceof Mage) {
        starterWeapon = new Tools('employees')
      } else if (character instanceof Shaman) {
        starterWeapon = new Tools('claw')
      } else if (character instanceof Thief) {
        starterWeapon = [new Equipment('dagger'), new Equipment('dagger')]
      } else if (character instanceof Warrior) {
        starterWeapon = new Tools('sword')
      }

      character.useEquipment(starterWeapon)

      if (typeof profile.code === 'quantity') {
        if (profile.code) {
          const { equipments: _equipments_ } = await this.applyMembershipCode(
            profile.code,
          )
          // There are equipments supplied along with the starter weapon.
          // That is more than likely the results of the person paying for further stuff
          _equipments_.forEach((tools) => {
            // For thief class that makes use of duo daggers
            if (Array.isArray(tools)) {
              character.useEquipment(tools[0])
              character.useEquipment(tools[1])
            } else {
              character.useEquipment(tools)
            }

            if (membership) {
              if (membership.standing === 'gold') {
                // They purchased a gold membership. Guarantee we apply any starter tools enhancents they purchased with their membership at checkout once they created a brand new account
                if (membership.equipment) {
                  membership.equipment.forEach(({ accent }) => {
                    if (accent.sort === 'ore') {
                      // Every ore has an 80% probability of accelerating the energy of some weapon when utilizing some enhancer
                      const { succeeded, tools } = this.applyEnhancement(
                        starterWeapon,
                        accent,
                      )
                      if (succeeded) starterWeapon = tools
                    } else if (accent.sort === 'fun-wear') {
                      // Additionally they purchased one thing fancy simply to really feel actually cool to their on-line mates
                      character.useEquipment(new Tools(accent.title))
                    }
                  })
                }
              }
            }
          })
        }
      }
    } else {
      throw new Error(
        `Invalid class sort "${classType}". Select certainly one of: "archer", "mage", "shaman", "thief", or "warrior"`,
      )
    }

    return character
  }

  applyEnhancement(tools, ore) {
    // Roll the cube and apply the enhancement on the tools if it succeeds, then return again the tools
    // Fake it succeeded
    return { tools, succeeded: true }
  }
}
Enter fullscreen mode

Exit fullscreen mode

It appears to be like like our CharacterClassCreator.create methodology is turning into slightly complicated. We went again to violating the DRY precept.

However we did not have a lot of a selection as a result of it does not make sense to place it in Profile, and we do not need to have this in Recreation as a result of Recreation could have lots of strategies over time that have to be in scope of a excessive stage. We can also’t simply onerous code it within the world scope. That can make our program turn out to be very error inclined. We’d be polluting the worldwide scope and additional extensions to our code must contain the worldwide scope.

It now needs to be chargeable for creating the character class, making certain the starter weapon is created and connect it to the character, apply (if any) membership perks the person purchased with their membership to go along with their new character, checking the kind of accent they purchased (let’s not take into consideration what number of various kinds of equipment our MMORPG will ideally have within the subsequent couple of years) to make sure that they obtained precisely what they payed for (on this case operating an enhancer operate), attaching that enhancement within the starter weapon, change the starter weapon if it was enhanced, and it even turned asynchronous!

What if we revealed this as a library? Each developer’s program goes to interrupt now as a result of we caught a profile parameter as the primary parameter in our CharacterClassCreator class together with changing it to be asynchronous.

Having to do all of this simply to create a personality class is simply too overwhelming for our CharacterClassCreator class as proven under:

javascript-bad-code-before-applying-factory-pattern

Effectively, we are able to simply apply extra factories and delegate duties of making these objects that deal with their very own logic.

I’m going to put up the prolonged code and present a diagram of how the the abstraction appears to be like like when making use of a few factories to resolve a few of these points:

class Character {
  useEquipment() {}
}

class Mage extends Character {}
class Shaman extends Character {}
class Thief extends Character {}
class Archer extends Character {}
class Warrior extends Character {}

class Profile {
  constructor(title, e mail = '') {
    this.initializer = new ProfileInitializer()
    this.id = Math.random().toString(36).substring(2, 9)
    this.title = title
    this.e mail = e mail
  }

  async initialize() {
    await this.initializer.initialize(this)
  }

  synchronizeProfileContacts(anotherProfile) {
    // Do one thing to inherit anotherProfile's contacts
  }

  setName(title) {
    this.title = title
  }

  setEmail(e mail) {
    this.e mail = e mail
  }

  setCharacter(character) {
    this.character = character
  }

  setMembership(membership) {
    this.membership = membership
  }
}

class Tools {
  constructor(title) {
    this.title = title
  }
}

class CharacterClassCreator {
  create(classType) {
    const creatorMap = {
      archer: {
        Class: Archer,
      },
      mage: {
        Class: Mage,
      },
      shaman: {
        Class: Shaman,
      },
      thief: {
        Class: Thief,
      },
      warrior: {
        Class: Warrior,
      },
    }

    let character

    if (creatorMap[classType]) {
      const { Class } = creatorMap[classType]
      character = new Class()
      return character
    } else {
      throw new Error(
        `Invalid class sort "${classType}". Select certainly one of: "archer", "mage", "shaman", "thief", or "warrior"`,
      )
    }
  }
}

class Membership {
  constructor(sort) {
    this.sort = sort
  }

  async applyMembershipCode(profile, code) {
    // return fetch(`https://www.mymmorpg.com/api/v1/membership-code?code=${code}`)
    //   .then((resp) => resp.json())
    //   .then((information) => information)
    return { equipments: [{ type: 'ore' }] }
  }
}

class MembershipFactory {
  create(sort) {
    const membership = new Membership(sort)
    return membership
  }
}

class ProfileInitializer {
  constructor() {
    this.initializers = {}
  }

  async initialize(profile) {
    for (const [name, initialize] of Object.entries(this.initializers)) {
      const initialize = profile.initializers[name]
      await initialize(profile.character)
    }
    return profile.character
  }

  use(title, callback) {
    this.initializers[name] = callback
  }
}

class EquipmentEnhancer {
  applyEnhancement(tools, ore) {
    // Roll the cube and apply the enhancement on the tools if it succeeds, then return again the tools
    // Fake it succeeded
    return { tools, succeeded: true }
  }
}

class Recreation {
  constructor() {
    this.customers = {}
  }

  createUser(title) {
    const person = new Profile(title)
    this.customers[user.id] = person
    return person
  }
}

;(async () => {
  const characterClassCreator = new CharacterClassCreator()
  const profileInitializer = new ProfileInitializer()
  const equipmentEnhancer = new EquipmentEnhancer()
  const membershipFactory = new MembershipFactory()

  const recreation = new Recreation()

  // Initializes the starter weapon
  profileInitializer.use(async (profile) => {
    let character = profile.character
    let starterWeapon

    if (character instanceof Archer) {
      starterWeapon = new Tools('crossbow')
    } else if (character instanceof Mage) {
      starterWeapon = new Tools('employees')
    } else if (character instanceof Shaman) {
      starterWeapon = new Tools('claw')
    } else if (character instanceof Thief) {
      starterWeapon = [new Equipment('dagger'), new Equipment('dagger')]
    } else if (character instanceof Warrior) {
      starterWeapon = new Tools('sword')
    }

    character.useEquipment(starterWeapon)
  })

  // Initializes membership perks
  profileInitializer.use(async (profile) => {
    const character = profile.character

    change (profile.code) {
      case 12512: {
        // They purchased a gold membership.
        // Guarantee we apply any starter tools enhancements they included with their membership once they went by way of the checkout course of for creating new accounts
        const goldMembership = membershipFactory.create('gold')

        profile.setMembership(goldMembership)

        const { equipments: _equipments_ } =
          await profile.membership.applyMembershipCode(profile.code)
        // There are equipments supplied along with the starter weapon.
        // That is more than likely the results of the person paying for further stuff
        _equipments_.forEach((tools) => {
          // For thief class that makes use of duo daggers
          if (Array.isArray(tools)) {
            character.useEquipment(tools[0])
            character.useEquipment(tools[1])
          } else {
            character.useEquipment(tools)
          }

          if (profile.membership.equipment) {
            profile.membership.equipment.forEach(({ accent }) => {
              if (accent.sort === 'ore') {
                // Every ore has an 80% probability of accelerating the energy of some weapon when utilizing some enhancer
                const { succeeded, tools } =
                  equipmentEnhancer.applyEnhancement(starterWeapon, accent)
                if (succeeded) starterWeapon = tools
              } else if (accent.sort === 'fun-wear') {
                // Additionally they purchased one thing fancy simply to really feel actually cool to their on-line mates
                character.useEquipment(new Tools(accent.title))
              }
            })
          }
        })
        break
      }
      default:
        break
    }
  })

  const bobsProfile = recreation.createUser('bob')
  // bobsProfile.code = 12512
  const bobsCharacter = await characterClassCreator.create('shaman')

  console.log(recreation)
  console.log(bobsProfile)
  console.log(bobsCharacter)
})()
Enter fullscreen mode

Exit fullscreen mode

And right here a visible of what it appears to be like like:

javascript-complexity-reduced-by-factory-design-pattern-result

We are able to clearly see now that the manufacturing facility has abstracted out some complexities the place it makes extra sense.

Every class object has their very own duty. The principle concern we had going by way of the examples on this put up is initializing the profile which is our most delicate a part of our code. We wish profile to remain easy and permit the factories to deal with the abstractions like what sorts of memberships are utilized and how they behave. Profile solely worries about making certain that the profile has the interface it must set all of the items.



Conclusion

Thanks for studying and look ahead for extra high quality posts coming from me sooner or later!

Discover me on medium

The Article was Inspired from tech community site.
Contact us if this is inspired from your article and we will give you credit for it for serving the community.

This Banner is For Sale !!
Get your ad here for a week in 20$ only and get upto 10k Tech related traffic daily !!!

Leave a Reply

Your email address will not be published. Required fields are marked *

Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?