3

Description:

Given a parking lot containing multiple levels and each level containing multiple rows and each row in turn containing multiple spots. The questions is to determine if there are enough spots to check if a vehicle can be parked.

Which object is responsible to determine if there is enough spots, I mean its the parking lot object which is supposed to receive the first query but ultimately it should be either level or row to implement the actual algorithm, right?

Its not about a single class but system as a whole.

Requirement:

  1. The parking lot has multiple levels. Each level has multiple rows of spots.

  2. The parking lot can park motorcycles, cars, and buses.

  3. The parking lot has motorcycle spots, compact spots, and large spots.

  4. A motorcycle can park in any spot.

  5. A car can park in either a single compact spot or a single large spot.

  6. A bus can park in five large spots that are consecutive and within the same row. It cannot park in small spots.

The question is that the given system should be able to given answer to the queries like:

Given a car the system should be able to tell if there is a spot for car or not, if yes the answer should be something like:

There is a spot on level x row y and spot z

The issues I face while solving such problems is where to start and how to find correct objects which should represent the domain correctly.

I did try to start somewhere and below is my code:

Enum Size {
  SMALL,
  COMPACT,
  LARGE
}

private class Spot {
  private boolean empty;
  private int number;
  private Size size;

  Spot(Size size, int number) {
    this.size = size;
  }

  full() {
    this.empty = false;
  }

  empty() {
    this.empty = true;
  }

  status() {
    return this.empty;
  }

  boolean canPark(Vehicle vehicle) {
    return false;
  }
}

class LargeSpot extends Spot {
  boolean canPark(Vehicle vehicle) {
    return
      vehicle.size() == Size.LARGE ||
      vehicle.size() == Size.COMPACT ||
      vehicle.size() == Size.SMALL;
  }
}

class CompactSpot extends Spot {
  boolean canPark(Vehicle vehicle) {
    return
      vehicle.size() == Size.COMPACT ||
      vehicle.size() == Size.SMALL;
  }
}

class SmallSpot extends Spot {
  boolean canPark(Vehicle vehicle) {
    return vehicle.size() == Size.SMALL;
  }
}

private class Row {
  private ArrayList<Spot> spots;

  Row(List<Spot> spots) {
    this.spots = spots;
  }

  void spot(Spot spot) {
    this.spots.add(spot);
  }

  List<Spot> getSpots(Vehicle vehicle) {
    List<Spot> emptySpots = new ArrayList<>();
    if (vehicle.size() == Size.LARGE) {
      // five consecutive large spots
      int length = spots.size();
      for (int i = 0; i < length; i++) {
        int j = 0;
        for (; j < 5; j++) {
          if (!spot.canPark(vehicle)) break; // break out of inner loop
        }
        if (j == 5) { // found five spots
          for (int k = i; k < i + j; k++) {
            emptySpots.add(spot);
          }
          i += j;
        }
      }
    } else {
      for (Spot spot : spots) {
        if (spot.canPark(vehicle)) {
          emptySpots.add(spot);
        }
      }
    }

    return emptySpots;
  }
}

private class Level {
  private final int number;
  private final ArrayList<Row> rows;

  Level(number, rows) {
    this.number = number;
    this.rows = rows;
  }
}

class ParkingLot {
  private List<Level> levels;

  ParkingLot(List<Level> levels) {
    this.levels = levels;
  }

  public String status(Vehicle vehicle) {
    List<Spot> emptySpots = new ArrayList<>();
    for (Level level : levels) {
      for (Row : level.rows()) {
        if (row.canPark(vehicle)) {
          emptySpots.addAll(row.getSpots(vehicle));
        }
      }
    }
    return emptySpots;
  }
}

public class Main {
  private final List<Level> floors = new ArrayList<>();
  floors.add(
    new Level(
      0,
      new Row[4]{new SmallSpotSpot(), new CarSpot(), new CarSpot()}));

  floors.add(
    new Level(
      1,
      new Row[4]{
        new LargeSpot(), new LargeSpot(), new LargeSpot(), new LargeSpot()}));

  private final Lot lot = new ParkingLot(floors);
  private final Car car = new Car(Size.Compact);

  System.out.println(lot.status(car));
}

The code definitely broken at many places but the readers can get the faint idea what I am trying to achieve. Also, it can be seen parking Large vehicle is one of the pain points in designing a good solution.

6
  • 2
    Possible duplicate of How to determine if a class meets the single responsibility principle?
    – gnat
    Aug 27 '17 at 11:38
  • If you have an object for the parking lot which seems to be appropriate, start there, and when it works and you have your tests in place, refactor until the "parking lot" does not do "too much" any more.
    – Doc Brown
    Aug 27 '17 at 12:10
  • @CodeYogi and can two motorcycles be parked in the same spot? Aug 27 '17 at 20:33
  • @CandiedOrange that was the only list of requirement I got, I think they can be parked in the small spots only.
    – CodeYogi
    Aug 27 '17 at 20:56
  • You said "A motorcycle can park in any spot." why are you saying "they can be parked in the small spots only"? Aug 27 '17 at 21:18
7

You're question is about what to model.

On the one hand, there is an obvious structure to the real-world situation: a lot, having levels, having rows, and ultimately having spots. So, one approach would be to model all of those items as entities, many of them as collections, e.g. in a hierarchy of objects.

However, in creating an application, you don't necessarily have to model everything that is in the real world, and, the things you do model don't all have to be first-class entities.

What I'm getting at is that you can model all the spots with a single table having primary key as: lot, level, row, spot, and then with attributes type/size (e.g. compact, handicapped, boat/rv), free/occupied.

A search for an available spot is a query against the table, and an update that marks a spot as free/occupied is a simple transaction.

There are lots of other options: in OOP, you can maintain two simple lists: occupied and non-occupied. The non-occupied list can be broken out into multiple lists by categtories, or maybe even sorted. The manager holding the collection of spots could search and move items between the lists.

In summary, you should study the domain queries and commands beyond the obvious real-world structure of the real-world parking lot. It could be that this extra structure (e.g levels, rows) provides almost no real domain value other than simply identifying where the free spot is. So, there is little reason to organize into multiple tables, and/or, perhaps there is no reason to model a "level" itself as an entity when the level can be captured as a simple (value) attribute.


(There is no way that a parking lot of even tens of thousands of spots would tax a modern computer system, so almost any approach would work. In such case do the simplest as @DocBrown is suggesting. Now, if you were doing hotels, you might reach internet scale, and then eventually you'd have some scaling things to deal with.)


We can never model everything, thus, modeling, by definition, is abstraction, in the sense of hiding irrelevant detail so we can focus on what is truly relevant.

Identify the use cases that are relevant to the domain; from those identify the queries and commands to modify the model — starting with noting well the inputs/givens & outputs/desired results — and from those identify what to model along with the actual queries and commands. With this level of understanding, you will begin to see which modeled entities have what responsibilities and which are merely value attributes. If we model more than necessary, we may loose clarity.


Update for your update: Don't model more than you need. Don't use subclasses when value attributes will do. Don't use object hierarchy when attributes will do.

For example, you're showing modeling the size of the spot using subclassing. What happens when a particular spot is a handicapped spot? Now you need a subclass for each handicapped spot size (e.g. class RVHandicappedSpot). This is class explosion, and a code smell when using classes for modeling things that could otherwise be modeled with attributes.

Further, using attributes you can have numeric ranges rather than only enums, e.g. for the length of a spot. When we model using classes, it constrains us to distinct values more like the equivalent of enums (compared to numbers).


Regarding the bus parking on multiple spots:

Essentially there are two separate problems: identifying the bus parking spots, and allocating them. These can be performed at different times; they do not need to be done together.

I'd suggest you model all potential bus parking spots in advance, as spots that themselves are a collection of other spots. Then, you can determine which of the potential bus parking spots are fully unoccupied, and hand them out as needed. There are a number of considerations here, such as not carelessly hampering bus parking by handing out random car parking spots that belong to the bus parking.

I see the bus parking operations as mostly algorithmic, rather than something solved by a complex data structure. It is similar to register allocation in compiler technology (e.g. graph coloring) when the processor has register pairs, and such.


There are probably three options for checking whether a spot and vehicle match: the Spot can check if a Vehicle fits in it, or a Vehicle can check if a Spot fits it, or a manager (the Lot or even some third party) can check for the match.

Which is best for your domain is a good question to ask.

Some of the criteria for trade offs involve: do either spot or vehicle have specialization (subclasses) to whom question should be delegated. If so they should be consulted. Do either the spot or vehicle have private data that influences matching that cannot be see by the other? If so, they should be given a chance to approve/deny the match.

I might make the bus parking spots a spot subclass that is a collection of spots. In that case, there would be some class hierarchy among kinds of spots, but it is not clear to me whether the generic spot algorithm (based on spot size) is sufficient or a specializing override would be needed for the subclass.

Also, we want a stable interface for the outside consuming clients to use.

So, my first take on that would be that I would have that be the manager's (the Lot's) responsibility so that from an external interface point of view the consuming client code only has to deal with the manager, even as the code evolves internally.

However, the manager may simply choose to delegate to the spot or to the vehicle or both (e.g. both have to say yes, then it is a match).

class ParkingLot {
    boolean Matches(Spot spot, Vehicle vehicle) {
        return spot.Matches(vehicle) && vehicle.Matches(spot);
    }
}
4
  • "I'd suggest you model all potential bus parking spots in advance, as spots that themselves are a collection of other spots." can you please give some detail example/explanation?
    – CodeYogi
    Aug 29 '17 at 15:55
  • Regarding usage of the spot classes I was not clear who would be responsible for checking if the given vehicle fits into that spot, handling that responsibility to some other class may introduce procedural code. But all these thinking fail in case of bus which require more than one spot.
    – CodeYogi
    Aug 29 '17 at 15:57
  • (1) there are two separate problems. One is identifying bus parking areas, the other is finding free parking. The former could be statically pre-computed, as it is fully independent of/orthogonal to free/busy. Thus, all possible bus parking areas can be composed in advance; in the simplest, it is a permutation over all the spots to size N, with a validity check (that rules out candidates that are not properly composed of adjacent spots).
    – Erik Eidt
    Aug 29 '17 at 16:43
  • (2) see addendum
    – Erik Eidt
    Aug 29 '17 at 17:06
2

A first look at the requirements

When you enter a parking lot, and have to drive around to find a free place, the responsibility is not in the spot, nor in the row, nor in the lot, but the driver's. The lot's responsibility is only to count the places that should be free and let you enter if there's place left.

But your case suggest a much more intelligent approach:

  • being able to determine the spot to be used, depending on the size of the vehicle, requires to know the characteristics and state of each spot.
  • being able to determine consecutive spots for a bus, involves the aggregate above the spot.
  • answering the request for a new car requires the parking lot to find the place and chose the optimal one in order to maximize the yield (i.e. not wasting a large spot for a motorcycle if it could be avoided, which implies to allocate motorcycle spots in priority to motorcycles; or allocate isolated free spots to cars, in order to save consecutive free spots for busses).

So clearly, the responsibility will be shared between several objects.

Your model, with a naive approach

Your parking lot looks like a constrained composite: the parking spot would be a leaf, the row and the lot would be non leaf composites.

At first sight, the prime responsibility would go to the composite, who would pass it down the chain until the first matching element is found. A kind of recursive responsibility...

However the fact that there is a higher level optimisation, i.e. a higher level element making the choice of the best matching place (e.g. if a motorcycle arrives, favor a free motorcycle spot and only if none is available shoulda compact place be envisaged), to split the responsibility between the leaf (i.e. tell if it's free), the row (i.e. tell the nearest matching free spot), and the parking lot (i.e. chose the row in which there are the most cars, in order to keep contiguous free area for the buses).

Your model with a better approach

The responsibility to chose the best places is in reality not a responsibility of the parking, but a responsibility of an assignment authority. Nevertheless, the interaction between the car and the parking lot requires to give a partial responsibility to the lot:

  Car ---<request>--->  Parking lot ---<request>---> Allocator
      <---<answer>----              <---<choice>----   

If the parking lot is a kind of composite, the allocator could be implemented as a visitor, i.e. a separate object that explores the composite, and constructs some aggregation (e.g. an ordered list of potential free spots). The composite has to collaborate with the visitor.

To make it even more flexible and powerful, the visitor could use a strategy for ordering the potential free spaces to be given for a specific size of vehicle, and implementing different kind of allocation heuristics.

Is this overengineered ? Link to the real world

There are a couple of airports in my area, where a detector on each spot displays a red line if it's taken and a green light if it's free (we are at leaf level, for the state of the spot). In front on each row, there's a sign that shows how many free spots there are in the row, and at the entry, there's the number of free spots in the lot. THere is no allocation responsibility, but clearly, each of your identified object would be somehow involved in the overall responsibility.

So, no, I don't think this is over-engineered in an aera of smart parkings and smart cities ;-)

2
  • 1
    I find your answer much easier to understand maybe because of the tree structure you explained. I will try to read the links you posted and then would retry with the new approach, hope you could help me in the process.
    – CodeYogi
    Aug 29 '17 at 16:03
  • If I see the diagram then the parking lot is nothing but a data structure and all the behaviour is delegated to the Allocator which seems a design flaw.
    – CodeYogi
    Oct 19 '17 at 13:22
0

For simple systems, as yours appears to be, the easiest way to determine responsibilities is to simply write a quicky use-case. From that, draw a sequence diagram. Once you learn how to write a use-case at the appropriate level of abstraction you are working at then it tends to become fairly trivial.

Use-Case: Find Parking Spot

1 - Operator requests to find parking spot for vehicle from system.

2 - System requests that Parking lot find a parking spot that vehicle can fit in.

3 - Parking lot returns parking spot to operator.

4 - Operator reports location of parking spot.

From this you can infer that you have an 'Operator' subsystem (the UI). ParkingLot and ParkingSpot subsystems. A System subsystem (usually just the subsystem that sequences the use-case processing steps. I tend to call them Operation classes). 'Vehicle' can be modeled as a subsystem or just a parameter to the 'FindParkingSpot' operation; sometimes one approach works better than the other.

Usually drawing the corresponding sequence diagram makes the responsibilities obvious. Which in this case would be the ParkingLot knows how to search ParkingSpots to determine where to park.

How ParkingLot makes that determination would depend entirely on the next level of design where obsessing over 'which object is responsible' is usually pointless as it tends to boil down to meeting some nebulous idea of 'good' the designer has in their mind that in practicality is generally pretty arbitrary.

Since it seems like you are still in the OOP learning process there's no reason to worry about anything other than turning the identified subsystems directly into classes. As you advance then there will be other issues that you may be concerned about which causes those subsystems to possibly turn into a number of classes. But, that isn't worth worrying about because without the necessary experience, attempting to apply more advanced concepts is almost certainly going to result in a much worse design than simply converting the subsystems directly to classes. You will get a very usable design if you simply do a good job of assigning responsibilities to subsystems.

It is also worth pointing out that Row and Level are simply attributes of a parking spot, so they wouldn't have any responsibilities.

4
  • I am confused about row and spot, who will decide if the vehicle is suitable to be parked in that spot?
    – CodeYogi
    Aug 28 '17 at 20:36
  • @CodeYogi - There is no specific answer because it depends on how the algorithm represents the data. In general the algorithm for determining if a vehicle fits is responsible for knowing if a vehicle fits, not the parking spot or row. The algorithm would also be responsible for knowing if multiple consecutive spots can be used and for picking those consecutive spots. My answer basically says, put the algorithm in the ParkingLot class to start. If it later becomes messy then you can break it into more classes but in that case, the only class that should need to change is the ParkingLot class.
    – Dunk
    Aug 28 '17 at 20:56
  • I am still not clear about system and sub-system terminology.
    – CodeYogi
    Aug 29 '17 at 15:49
  • System is something I use in use-cases. You won't find it in any book or article that I'm aware of. It basically stands for high-level application processing. I use it because defining anything more specific is going to lead me down the path of too much detail for a use-case and prematurely force an implementation. When I eventually do the design, it'll usually end up being several very-application specific modules. Just start by modelling System as a class. It'll morph as you go. Subsystems are just modules, packages, components or whatever your preferred choice of terms. Look online.
    – Dunk
    Aug 29 '17 at 18:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.