1

I am designing a parking lot. Now I have three kinds of parking spots. I want to initialize my parking lot like this:

Building({Size.SMALL: 5, Size.MEDIUM: 6, Size.LARGE: 7})

This means this parking lot has

  • 5 small spots,
  • 6 medium spots and
  • 7 large spots.

And below is my code for spots and the parking lot (i.e., Building).

from enum import Enum

class Size(Enum):
    SMALL = 1
    MEDIUM = 2
    LARGE = 3

class Spot:
    def __init__(self, id) -> None:
        self.id = id
        self.vehicle = None

class SmallSpot(Spot):
    def __init__(self, id) -> None:
        super().__init__(id)

class MediumSpot(Spot):
    def __init__(self, id) -> None:
        super().__init__(id)

class LargeSpot(Spot):
    def __init__(self, id) -> None:
        super().__init__(id)

class Building:
    def __init__(self, dic: dict[Size, int]) -> None:
        for size, count in list(dic.items()):
            if size == Size.SMALL:
                self.small_spot_list = [SmallSpot(i) for i in range(count)]
            elif size == Size.MEDIUM:
                self.medium_spot_list = [MediumSpot(i) for i in range(count)]
            elif size == Size.LARGE:
                self.large_spot_list = [LargeSpot(i) for i in range(count)]

My question is, how can I rewrite the __init__() function of Building so that I don't need to explicitly write these enums in the code so that if there are new kinds of spots added, I will not need to modify the code in this function (such as add more if-else statements for new vehicle types)?

I believe it's called Open-Closed Principle.

Hangji He
  • 11
  • 2

1 Answers1

2

I assume that when you say "new kinds of vehicles added" you mean new additions to your size enum:

from enum import Enum

class Size(Enum):
    SMALL = 1
    MEDIUM = 2
    LARGE = 3

rather than inherit the base parking spot class, you could just include the Size enum as a property of the spot. Therefore:

class Spot:
    def __init__(self, id, size: Size) -> None:
        self.id = id
        self.vehicle = None
        self.size = size # this will be a Size enum when initiating

Your building initialisation function can then be simplified:

  • removed conditionals
  • key of spot_dict: Size
  • values of spot_dict: list of Spots
class Building:
    def __init__(self, dic: "dict[Size, int]") -> None:
        # initialise/save the variables
        self.spot_dict = {}
        self.vehicle_dict = dic
        
        # run the function to fill the lots
        self.fill_lots()
    
    def fill_lots(self):
        for size, count in self.vehicle_dict.items():
            self.spot_dict[size] = [Spot(i,size) for i in range(count)]

the benefits of this method allow you to find the spots in the specific Size category easily.

Putting it all together

from enum import Enum

class Size(Enum):
    SMALL = 1
    MEDIUM = 2
    LARGE = 3

class Spot:
    def __init__(self, id, size: Size) -> None:
        self.id = id
        self.vehicle = None
        self.size = size

class Building:
    def __init__(self, dic: "dict[Size, int]") -> None:
        # initialise/save the variables
        self.spot_dict = {}
        self.vehicle_dict = dic
        
        # run the function to fill the lots
        self.fill_lots()
    
    def fill_lots(self):
        for size, count in self.vehicle_dict.items():
            self.spot_dict[size] = [Spot(i,size) for i in range(count)]
        
        
                
def main():
    building = Building({Size.SMALL: 5, Size.MEDIUM: 6, Size.LARGE: 7})
    
if __name__=="__main__":
    main()

example of adding more categories

Assuming Building code stays the same:

class Size(Enum):
    SMALL = 1
    MEDIUM = 2
    LARGE = 3
    HEAVY_VEHICLE = 4 # new kind
    OVERSIZED = 5 # new kind

bigger_building = Building({
            Size.SMALL: 5, 
            Size.MEDIUM: 6, 
            Size.LARGE: 7,
            Size.HEAVY_VEHICLE: 2,
            Size.OVERSIZED: 1,
            })
# access the list of spots
hv_vehicle_spot_ls = bigger_building.spot_dict[Size.HEAVY_VEHICLE]
mcursa-jwt
  • 141
  • 5
  • What if I only want to use inheritance? Some spots may have unique attributes, such as chargers, and some may be for disabled drivers. How can I **dynamically** choose the type of slot I want to initiate just from my input? – Hangji He Oct 19 '22 at 07:33
  • Hm, I would add those in as attributes as well. You could think composition rather than inheritance here. – mcursa-jwt Oct 20 '22 at 03:07
  • E.g. `self.ev_charge_spot = True` or `self.handicap_spot = True`. You would then have to include more parameters, and change the `fill_spots` method. Hope this helps – mcursa-jwt Oct 20 '22 at 03:39