mardi 14 septembre 2021

Questions on a Python Creational Design Pattern

I came across a potential Python creational design pattern that I haven't seen before and I wanted to get some opinions on whether it is advisable to use this approach.

Problem: You are creating a class that consists of a group of instances of a single object. In the example below, the group is a vehicle Fleet that can only consist of Vans or Trucks. How do you design the class so that the type of constituent object can be selected without modifying the group class?

Typical Solution: The most typical approach would probably be to use the Factory design pattern by internally constructing a VehicleFactory or passing one into the __init__ function of the group object. Dependency Injection (DI) can also be used. For example, the buildFleet method in the code below could be modified to take in a list of concrete Vehicle instances generated by external code.

Different Solution: Pass the constructor of a concrete Vehicle class into the __init__ function of the group object. This constructor will be called when building the constituent objects. This feels natural in Python, but it can work in any object-oriented language that has some way of effectively passing a function as an argument.

This solution differs from the Factory pattern since no Factory class is required. It is a type of DI, but not at the level of the constituent objects since they are constructed within the group class.

Example Code:

This example creates an informal abstract Vehicle class with two concrete derived classes Van and Truck. The Fleet class constructor takes the constructor from one of the concrete derived classes and uses this in the buildFleet method to populate the Fleet object with one type of Vehicle. The demo code at the bottom prints the capacity of a Van fleet and a Truck fleet to show that the two Fleet instances behave as expected.

class Vehicle:
    def printCapacity(self):
        print(self.capacity)


class Van(Vehicle):
    def __init__(self):
        self.capacity = 10

        
class Truck(Vehicle):
    def __init__(self):
        self.capacity = 100

     
class Fleet:    
    def __init__(self, Vehicle):
        self.Vehicle = Vehicle
        self.vehicles = []
        
    def buildFleet(self, vehicleCount):
        for _ in range(vehicleCount):
            self.vehicles.append(self.Vehicle())
            
    def printCapacity(self):
        capacity = 0
        for vehicle in self.vehicles:
            capacity += vehicle.capacity
        print(capacity)


vanFleet = Fleet(Van)
vanFleet.buildFleet(12)
vanFleet.printCapacity() # Prints 120


truckFleet = Fleet(Truck)
truckFleet.buildFleet(12)
truckFleet.printCapacity() # Prints 1200

Benefits:

  1. Solves the indicated problem.
  2. Requires less code than the Factory pattern and likely less than the DI approach depending on the external code.
  3. In my opinion, this has a nice syntactic flow and improves readability.

Potential Drawbacks:

  1. This approach only allows the group to consist of a single type of constituent object. This does remove some flexibility compared to the Factory pattern or DI solutions. However, this solution is only intended to address situations where this is not a limitation.

Questions:

  1. Is this something that is done in Python? I haven't seen it before, but I certainly could have missed it.
  2. Are there any potential drawbacks to this approach that I'm missing?

Aucun commentaire:

Enregistrer un commentaire