Featured image of post BP1 - Gun (Iterator) - Design Pattern From Simple Things

BP1 - Gun (Iterator) - Design Pattern From Simple Things

There are bullets that need to be fired one by one in a specific order. You need a tool to set it up neatly and easily upgrade later.

Gun (Iterator) is a behavioral design pattern: 🖤🖤🖤🖤🤍

Iterator and Iterable often confused me, so I renamed them. Gun represents Iterator, Bullets represents Iterable.

What is Gun and Bullets?

  • Bullets (Iterable) is a group of bullets.
    • def __iter__() helps us arrange bullets and other accessories to turn them into a gun.
  • Gun (Iterator) is an object that can fire bullet one by one.
    • def __next__() help us fire the bullets.

Gun Diagram

Why use Gun?

It makes shooting more manageable, more professional. Your gun can integrate other tools such as a compass, binoculars, and silencer…!

Question: Just throw the bullet to the target, the gun seems to just make it fancy!

Answer: It is more useful when you need to use plugins or customize many features with your gun.

Girl throw stone

When to use Gun?

Question: When I need to use Gun?

Answer: When you need to fire the bullets!

Input:

  • Having many bullets:
    • 1stBullet
    • 2ndBullet
    • 3rdBullet
1
# nothing here

Expected Output:

  • Firing bullets in expected order by:
    • Getting a Normal Gun
      • First Shoot will fire 1stBullet
      • Then 2ndBullet, 3rdBullet
    • Getting a LIFO Gun
      • First Shoot will fire 3rdBullet
      • Then 2ndBullet, 1stBullet
1
2
3
4
5
6
⬅️ Normal Gun 🔫:
🎯First Shoot: 1stBullet 
🎯The Rest: 2ndBullet-3rdBullet
🔄 LIFO Gun 🔫:
🎯First Shoot: 3rdBullet 
🎯The Rest: 2ndBullet-1stBullet

Gun and Bullets

How to implement Gun?

Non-Gun implementation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
if __name__ == "__main__":
    bullets = []
    # Input 1: Having many bullets:
    bullets.append("1stBullet")
    bullets.append("2ndBullet")
    bullets.append("3rdBullet")

    gun = iter(bullets)
    first_fired_bullet = next(gun)
    remaining_bullets = "-".join(gun)
    # Expected Output 1: Firing bullets in expected order by: Normal Gun
    print(
        f"⬅️ Normal Gun 🔫:\n🎯First Shoot: {first_fired_bullet} \n🎯The Rest: {remaining_bullets}"
    )

    lifo_gun = iter(bullets[::-1])
    first_fired_bullet = next(lifo_gun)
    remaining_bullets = "-".join(lifo_gun)
    # Expected Output 2: Firing bullets in expected order by: LIFO Gun
    print(
        f"🔄 LIFO Gun 🔫:\n🎯First Shoot: {first_fired_bullet} \n🎯The Rest: {remaining_bullets}"
    )

Gun implementation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
from collections.abc import Iterable, Iterator


class Bullets(Iterable):
    def __init__(self, bullets=None):
        self._bullets = bullets or []

    # to_normal_gun
    def __iter__(self):
        return Gun(self._bullets)

    def to_lifo_gun(self):
        return Gun(self._bullets, reverse=True)

    def load(self, bullet):
        self._bullets.append(bullet)


class Gun(Iterator):
    _position = None
    _reverse = False

    def __init__(self, bullets, reverse=False):
        self._bullets = bullets
        self._reverse = reverse
        self._position = -1 if reverse else 0

    def __next__(self):
        try:
            value = self._bullets[self._position]
            self._position += -1 if self._reverse else 1
        except IndexError:
            raise StopIteration()
        return value


if __name__ == "__main__":
    bullets = Bullets()
    # Input 1: Having many bullets:
    bullets.load("1stBullet")
    bullets.load("2ndBullet")
    bullets.load("3rdBullet")

    gun = iter(bullets)
    first_fired_bullet = next(gun)
    remaining_bullets = "-".join(gun)
    # Expected Output 1: Firing bullets in expected order by: Normal Gun
    print(
        f"⬅️ Normal Gun 🔫:\n🎯First Shoot: {first_fired_bullet} \n🎯The Rest: {remaining_bullets}"
    )

    lifo_gun = bullets.to_lifo_gun()
    first_fired_bullet = next(lifo_gun)
    remaining_bullets = "-".join(lifo_gun)
    # Expected Output 2: Firing bullets in expected order by: LIFO Gun
    print(
        f"🔄 LIFO Gun 🔫:\n🎯First Shoot: {first_fired_bullet} \n🎯The Rest: {remaining_bullets}"
    )

Source Code

Made with the laziness 🦥
by a busy guy