0

I am trying to design a Parking Lot system using an object-oriented approach. The parking lot can have multiple types of parking spots such as Handicapped, Compact, Large, MotorBike etc.

Initially, I was thinking of creating an enum to model these different types as follows:

public enum ParkingSpotType {
  HANDICAPPED, 
  COMPACT, 
  LARGE, 
  MOTORBIKE
}

And, then use these inside the ParkingSpot class as follows:

public abstract class ParkingSpot {
    private ParkingSpotType type;
}

Similarly, the Vehicle class would also have VehicleType, which would map to the ParkingSpotType it requires for parking

public class Vehicle {
    private VehicleType vehicleType;
    // other fields 
}

public enum VehicleType {
  CAR (ParkingSpotType.COMPACT), 
  BUS (ParkingSpotType.LARGE), 
  TRUCK (ParkingSpotType.LARGE), 
  BIKE (ParkingSpotType.MOTORBIKE),
  CYCLE (ParkingSpotType.MOTORBIKE);
  
  private ParkingSpotType parkingSpotType;

  VehicleType(ParkingSpotType parkingSpotType) {
      this.parkingSpotType = parkingSpotType;
  }
}

This was enabling me to find the ParkingSpotType from Vehicle simply by doing:

vehicle.getVehicleType().getParkingSpotType()

However, someone told me that this would violate the Open/Closed design principle. Any addition of a new type may require code changes in various existing places, which would violate the open/closed design principle which states that existing and well-tested classes should not be modified when a new feature needs to be introduced. It was suggested that I create different sub-classes for different types as follows:

public class HandicappedSpot extends ParkingSpot {
}

public class CompactSpot extends ParkingSpot {
}

public class LargeSpot extends ParkingSpot {
}

public class MotorbikeSpot extends ParkingSpot {
}

But with this approach, how would I map Vehicle to ParkingSpot if I don't have an enum modelling the same. Could someone please have a look and comment if the first approach is indeed bad wrt OO-Design. If yes, how would I solve the above-mentioned problem in the second approach ?

OneMoreError
  • 7,518
  • 20
  • 73
  • 112
  • 1
    It depends on how different you anticipate your parking spots being. If the only question you would need to ask of them is "what kind of car goes here?", to which the answer is a kind of car, then an enum is more than appropriate. But if you plan to make functionality that only works on specific kinds of parking spots (maybe you have an `ElectricCarSpot` that has a special "refuel car" function), then those should be subclasses, as they add functionality. – Silvio Mayolo Mar 07 '21 at 06:41
  • 2
    Don't add subclasses for the sake of adding them; only extend a class if you're adding functionality. And even if you are, it's worth asking if there's a better way than straight implementation inheritance, which frankly is seldom the answer. Consider composition (a handicapped spot "contains" an ordinary parking spot, from which we can query ordinary parking spot properties) or, more likely in your case, replacing `ParkingSpot` with an interface and having several classes extend it. Interface inheritance is the much more common and less harmful cousin of implementation inheritance. – Silvio Mayolo Mar 07 '21 at 06:42

2 Answers2

1

I'm not an expert, but I don't follow the critique of your enum solution. It seems fine to me based on a few observations:

The open/closed principle allows for extension. This includes not only creating new implementations of interfaces and child classes, but also the addition of properties and methods to existing classes - the line between 'extension' and 'modification' can seem blurry, but as I understand it, the principle BEHIND open/closed is that if some client, consumer, or module is using your code base, they shouldn't have to refactor due to your code changes (in an ideal world).

To this end, I don't see how adding a new element to your enum is any different from adding a new class that extends ParkingSpot. Could be wrong.

I guess I would ask for an example of where current code would be disrupted if we added a new parking spot enum element, but not if we added a new child class to ParkingSpot, because I don't see one currently.

Wickerbough
  • 365
  • 1
  • 4
  • 15
  • *"they shouldn't have to refactor due to your code changes"* one reason they might is because enums are often used exhaustively in `switch` statements, where if there is no match then an exception is thrown in the `default` block, with the presumption that this exception should never be thrown. Of course, an exhaustive `if`/`else if` chain of `instamceof` checks would need to be refactored for the same reason if a new type was added, but enums encourage `switch` statements whereas Java programmers should know that doing a lot of `instanceof` checks is bad practice. – kaya3 Apr 06 '21 at 19:53
1

Since the question is "do enums violate the Open/Closed Principle" my answer is that because you cannot write subclasses of enum types, and users of the enum type cannot create their own instances of it to meet their own requirements, then in most circumstances this would violate the Open/Closed Principle, with two caveats:

  • If the enum type is something that is inherently an exhaustive list (e.g. the days of the week) then there would be no need for it to be "open for extension" for the same reason integers don't need to be "open for extension".
  • If the enum type has methods which can have different behaviours when passed user-defined classes meeting some interface (e.g. Comparable for a sorting function) then it is "open for extension" in that sense. See this other Q&A for a discussion about how "open for extension" doesn't necessarily mean allowing subclasses.

That said, the Open/Closed Principle is kind of a red herring here, because the two alternative models you propose are not actually modelling the same things as each other. Your ParkingSpotType enum models a type of parking spot, such that the enum values each represent types of parking spot; whereas your ParkingSpot class models a parking spot, such that an instance of this class (or one if its subclasses, say MotorbikeSpot) represents an individual parking spot, and multiple instances of the same subclass would represent different parking spots of the same type. Consider:

  • You could use an enum ParkingSpotTypes to represent the different types of parking spot, or you could use an enum ParkingSpots to represent the actual parking spots in a specific parking lot (of which there is a definite list, if the program only has to work for one parking lot).
  • You could use a class ParkingSpot to represent an actual parking spot, with subclasses for different types of parking spot, or you could use a class ParkingSpotType whose instances each represent a type of parking spot.

Which option you choose should depend on whether your program needs to represent actual parking spots, or just types of parking spots. For example, if you are selling parking tickets which can only be used in the parking spot named on the ticket, then your code would have to know what the parking spots are; if you are selling parking tickets which can be used in any spot of the type named on the ticket, then your code probably only needs to know how many spots are available. In the latter case there would be no need to model actual parking spots, just parking spot types.

kaya3
  • 47,440
  • 4
  • 68
  • 97