Parking lot and vehicle classes












3














The expected functionality is that a user/vehicle interacts with a ParkingLot and it allots the space in Park method based on type of vehicle (here I can expect a few changes, like increase from regular and handicapped to one more functionality for celebrities etc). What would be the route to perfect object oriented code?



class Vehicle
attr_accessor :regno,:type
def initialize(regno,type)
@regno=regno
@type=type
end
end

class ParkingLot
attr_accessor :size , :handicapped_size , :regular_size

def initialize(size)
@lot={}
@size=size
@handicapped_size=(0.1)*size
@regular_size=size-@handicapped_size

end
def check_regular
@regular_size>0 ? @regular_size-=1 : false
end
def check_handicapped
@handicapped_size>0 ? @handicapped_size-=1 : false
end

def park(vehicle,hour)
case vehicle.type
when "regular"
check_regular ? @lot[vehicle.regno]=Regular_ParkingSpace.new(hour).payment : error()
when "handicapped"
check_handicapped ? @lot[vehicle.regno]=Handicapped_ParkingSpace.new(hour).payment : error()
end
end

def unpark(vehicle)
pay=@lot[vehicle.regno]
puts "Pls pay us #{pay} rupees"
@lot.delete(vehicle.regno){|el| puts "#{el} not found in this parking lot n"}
end

def error
raise "No more vehicles can be parked !! n"
end
end
class ParkingSpace
attr_accessor :hour,:rate
def initialize(hour,rate)
@hour=hour
@rate=rate
end
def payment
@hour * @rate
end
end

class Regular_ParkingSpace < ParkingSpace
def initialize(hour,rate=20)
super
end
def payment
super
end
end
class Handicapped_ParkingSpace < ParkingSpace
def initialize(hour,rate=5)
super
end
end


Please mention the principle in use too.










share|improve this question
























  • ...Are you asking which design would be best to use, or do you want a review of your current code?
    – Nic Hartley
    Jun 21 '15 at 23:04










  • Both if there are any design improvements you can mention that that would be much helpful.
    – Anony-mouse
    Jun 22 '15 at 6:49
















3














The expected functionality is that a user/vehicle interacts with a ParkingLot and it allots the space in Park method based on type of vehicle (here I can expect a few changes, like increase from regular and handicapped to one more functionality for celebrities etc). What would be the route to perfect object oriented code?



class Vehicle
attr_accessor :regno,:type
def initialize(regno,type)
@regno=regno
@type=type
end
end

class ParkingLot
attr_accessor :size , :handicapped_size , :regular_size

def initialize(size)
@lot={}
@size=size
@handicapped_size=(0.1)*size
@regular_size=size-@handicapped_size

end
def check_regular
@regular_size>0 ? @regular_size-=1 : false
end
def check_handicapped
@handicapped_size>0 ? @handicapped_size-=1 : false
end

def park(vehicle,hour)
case vehicle.type
when "regular"
check_regular ? @lot[vehicle.regno]=Regular_ParkingSpace.new(hour).payment : error()
when "handicapped"
check_handicapped ? @lot[vehicle.regno]=Handicapped_ParkingSpace.new(hour).payment : error()
end
end

def unpark(vehicle)
pay=@lot[vehicle.regno]
puts "Pls pay us #{pay} rupees"
@lot.delete(vehicle.regno){|el| puts "#{el} not found in this parking lot n"}
end

def error
raise "No more vehicles can be parked !! n"
end
end
class ParkingSpace
attr_accessor :hour,:rate
def initialize(hour,rate)
@hour=hour
@rate=rate
end
def payment
@hour * @rate
end
end

class Regular_ParkingSpace < ParkingSpace
def initialize(hour,rate=20)
super
end
def payment
super
end
end
class Handicapped_ParkingSpace < ParkingSpace
def initialize(hour,rate=5)
super
end
end


Please mention the principle in use too.










share|improve this question
























  • ...Are you asking which design would be best to use, or do you want a review of your current code?
    – Nic Hartley
    Jun 21 '15 at 23:04










  • Both if there are any design improvements you can mention that that would be much helpful.
    – Anony-mouse
    Jun 22 '15 at 6:49














3












3








3


2





The expected functionality is that a user/vehicle interacts with a ParkingLot and it allots the space in Park method based on type of vehicle (here I can expect a few changes, like increase from regular and handicapped to one more functionality for celebrities etc). What would be the route to perfect object oriented code?



class Vehicle
attr_accessor :regno,:type
def initialize(regno,type)
@regno=regno
@type=type
end
end

class ParkingLot
attr_accessor :size , :handicapped_size , :regular_size

def initialize(size)
@lot={}
@size=size
@handicapped_size=(0.1)*size
@regular_size=size-@handicapped_size

end
def check_regular
@regular_size>0 ? @regular_size-=1 : false
end
def check_handicapped
@handicapped_size>0 ? @handicapped_size-=1 : false
end

def park(vehicle,hour)
case vehicle.type
when "regular"
check_regular ? @lot[vehicle.regno]=Regular_ParkingSpace.new(hour).payment : error()
when "handicapped"
check_handicapped ? @lot[vehicle.regno]=Handicapped_ParkingSpace.new(hour).payment : error()
end
end

def unpark(vehicle)
pay=@lot[vehicle.regno]
puts "Pls pay us #{pay} rupees"
@lot.delete(vehicle.regno){|el| puts "#{el} not found in this parking lot n"}
end

def error
raise "No more vehicles can be parked !! n"
end
end
class ParkingSpace
attr_accessor :hour,:rate
def initialize(hour,rate)
@hour=hour
@rate=rate
end
def payment
@hour * @rate
end
end

class Regular_ParkingSpace < ParkingSpace
def initialize(hour,rate=20)
super
end
def payment
super
end
end
class Handicapped_ParkingSpace < ParkingSpace
def initialize(hour,rate=5)
super
end
end


Please mention the principle in use too.










share|improve this question















The expected functionality is that a user/vehicle interacts with a ParkingLot and it allots the space in Park method based on type of vehicle (here I can expect a few changes, like increase from regular and handicapped to one more functionality for celebrities etc). What would be the route to perfect object oriented code?



class Vehicle
attr_accessor :regno,:type
def initialize(regno,type)
@regno=regno
@type=type
end
end

class ParkingLot
attr_accessor :size , :handicapped_size , :regular_size

def initialize(size)
@lot={}
@size=size
@handicapped_size=(0.1)*size
@regular_size=size-@handicapped_size

end
def check_regular
@regular_size>0 ? @regular_size-=1 : false
end
def check_handicapped
@handicapped_size>0 ? @handicapped_size-=1 : false
end

def park(vehicle,hour)
case vehicle.type
when "regular"
check_regular ? @lot[vehicle.regno]=Regular_ParkingSpace.new(hour).payment : error()
when "handicapped"
check_handicapped ? @lot[vehicle.regno]=Handicapped_ParkingSpace.new(hour).payment : error()
end
end

def unpark(vehicle)
pay=@lot[vehicle.regno]
puts "Pls pay us #{pay} rupees"
@lot.delete(vehicle.regno){|el| puts "#{el} not found in this parking lot n"}
end

def error
raise "No more vehicles can be parked !! n"
end
end
class ParkingSpace
attr_accessor :hour,:rate
def initialize(hour,rate)
@hour=hour
@rate=rate
end
def payment
@hour * @rate
end
end

class Regular_ParkingSpace < ParkingSpace
def initialize(hour,rate=20)
super
end
def payment
super
end
end
class Handicapped_ParkingSpace < ParkingSpace
def initialize(hour,rate=5)
super
end
end


Please mention the principle in use too.







object-oriented ruby






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jun 21 '15 at 17:40

























asked Jun 21 '15 at 17:18









Anony-mouse

212111




212111












  • ...Are you asking which design would be best to use, or do you want a review of your current code?
    – Nic Hartley
    Jun 21 '15 at 23:04










  • Both if there are any design improvements you can mention that that would be much helpful.
    – Anony-mouse
    Jun 22 '15 at 6:49


















  • ...Are you asking which design would be best to use, or do you want a review of your current code?
    – Nic Hartley
    Jun 21 '15 at 23:04










  • Both if there are any design improvements you can mention that that would be much helpful.
    – Anony-mouse
    Jun 22 '15 at 6:49
















...Are you asking which design would be best to use, or do you want a review of your current code?
– Nic Hartley
Jun 21 '15 at 23:04




...Are you asking which design would be best to use, or do you want a review of your current code?
– Nic Hartley
Jun 21 '15 at 23:04












Both if there are any design improvements you can mention that that would be much helpful.
– Anony-mouse
Jun 22 '15 at 6:49




Both if there are any design improvements you can mention that that would be much helpful.
– Anony-mouse
Jun 22 '15 at 6:49










3 Answers
3






active

oldest

votes


















1














Since @Flambino critiqued your existing code, I will focus more on application architecture.



When I approach this subject, first I determine what "things" are interacting in the system.




  • Parking lots

  • Vehicles

  • Parking fees (of which there are "unrestricted" and "handicapped")

  • Payments

  • Parking spaces (of which there are "unrestricted" and "handicapped")


This gives us 5 classes. Classes are nouns (people, places or things) and methods are the verbs (actions). We will throw in two more classes to descibe the permits a vehicle can have (a handicapped sticker for those in the USA) and a custom error class for more intelligent error handling in the application.




  1. Vehicle

  2. ParkingFee

  3. ParkingSpace

  4. ParkingSpacePayment

  5. ParkingLot

  6. VehiclePermit

  7. ParkingSpaceError


We'll get the easiest one of of the way:



class ParkingSpaceError < StandardError
end


Now we've got a specific error class when problems arise parking or paying for a parking spot allowing more intelligent error handling.



Once I've identified the basic "things" in the system, I think of how they are related. The first class I build is the one with no relations to anything. It's quick and simple: The ParkingFee class.



The ParkingFee class



A parking fee consists of a rate multiplied by how many hours you've parked. Plus, you only have two rates. We will create static properties for both fee types that we can reference later:



class ParkingFee
def self.unrestricted
@@unrestricted ||= ParkingFee.new 20
end

def self.handicapped
@@handicapped ||= ParkingFee.new 5
end

def initialize(rate)
@rate = rate;
end

def calculate(hours)
rate * hours
end

def rate
@rate
end
end


Easy peasy, rice and cheesey. The next class that's easy to create is the VehiclePermit class.



The VehiclePermit class



Since you must determine whether or not a vehicle is a "handicapped vehicle," this got me thinking. First, I'm assuming since you reference "rupees" in your post that you are in India, or are building software that will be used in India. In the USA, vehicles can have multiple additional attributes describing how they can be used, and where they can be parked. I believe the term that we use is "permit."



Without understanding how vehicles are registered as "handicap" vehicles in India, I'll call this next class VehiclePermit. As we will see later, every Vehicle could have multiple permits attached to it. A "handicapped" vehicle is just a Vehicle that has the handicapped VehiclePermit associated with it.



This class doesn't have much to it. An id and a name, plus a static property for convenience. This could be just as easily mapped from a database if you want.



class VehiclePermit
def self.handicapped
@@handicapped ||= VehiclePermit.new 1, "Handicapped"
end

def initialize(id, name)
@id = id;
@name = name;
end

def id
@id
end

def name
@name
end
end


Now we can move on to the "thing" this is all about: vehicles.



The Vehicle class



This class needs one or more permits, and a registration number. Still, not very complex. We'll also expose a public method to test this vehicle to see if it has the "handicapped" permit, which we will use later when parking the vehicle.



class Vehicle
def initialize(registration_number)
@registration_number = registration_number
@permits =
end

def is_handicapped?
@permits.any? {|p| p == VehiclePermit.handicapped }
end

def permits
@permits
end

def registration_number
@registration_number
end
end


We have our ParkingFee, VehiclePermit and Vehicle, now we are ready to park this thing.



The ParkingSpace class



The ParkingSpace class holds a vehicle, the date and time the vehicle parked, and the fee associated with it. It allows you to calculate the payment as well. We'll define public methods for all the actions you can perform on a parking space:




  • Park a car

  • Vacate the space

  • Calculate the fee

  • Test to see if a vehicle is currently occupying it

  • Test to see if it contains a particular vehicle

  • Test a vehicle to see if it can park here


The code:



class ParkingSpace
def initialize(parking_lot, fee, number)
@parking_lot = parking_lot
@fee = fee
@number = number
end

def payment
ParkingSpacePayment.new self
end

def can_park?(vehicle)
!occupied?
end

def contains?(vehicle)
self.vehicle == vehicle
end

def number
@number
end

def occupied?
!vehicle.nil?
end

def park(vehicle)
raise ParkingSpaceError "Cannot park vehicle #{vehicle.registration_number}" unless can_park? vehicle
self.vehicle = vehicle
date_occupied = DateTime.current
end

def vacate
payment = nil
date_occupied = nil
vehicle = nil
end

def vehicle
@vehicle
end

private

def payment=(new_payment)
@payment = new_payment
end

def calculate_fee(date)
fee.calculate(date)
end

def vehicle=(new_vehicle)
@vehicle = new_vehicle
end
end


We aren't done yet, because there are two kinds of parking spaces. We will define concrete classes for each:



class UnrestrictedParkingSpace < ParkingSpace
def initialize(parking_lot, number)
super(parking_lot, ParkingFee.unrestricted, number)
end
end


An "unrestricted" parking space doesn't require much code. We just override the constructor to pass the proper ParkingFee object. The can_park? method on the parent class has the correct logic for this type of space, so we don't bother overriding it.



class HandicappedParkingSpace < ParkingSpace
def initialize(parking_lot, number)
super(parking_lot, ParkingFee.handicapped, number)
end

def can_park?(vehicle)
super(vehicle) && vehicle.is_handicapped?
end
end


The "handicapped" parking space overrides the constructor, which passes the handicapped fee, and overrides the can_park? method. The can_park? method first delegates to the method on the super class, and if that returns true, we have an additional test to see if the vehicle has a handicapped permit, so we also call Vehicle#can_park?.



Each type of parking space has the fee baked in so no one can make the "Unrestricted" parking space fee less that 20 Rupees. Now that we've got a parking spaces, we need some place to put them: the ParkingLot.



The ParkingLot class



A parking lot has a bunch of spaces, both unrestricted and handicapped. We also need to do the following things in the parking lot:




  • Park a vehicle

  • Exit the parking lot

  • Calculate a payment for a vehicle

  • Check to see if spaces are available


The constructor for the ParkingLot class takes two arguments: the number of unrestricted spaces and the number of handicapped spaces.



class ParkingLot
def initialize(unrestricted_count, handicapped_count)
count = 0
@parking_spaces =

unrestricted_count.times do |n|
@parking_spaces << UnrestrictedParkingSpace.new self, n
end

count = @parking_spaces.count

handicapped_count.times do |n|
@parking_spaces << HandicappedParkingSpace.new self, count + n
end
end

def exit(vehicle, payment)
parking_space = parking_space_for vehicle
raise ParkingSpaceError "Balance not paid: #{payment.balance}" unless payment.paid?
parking_space.vacate vehicle
parking_space
end

def park(vehicle)
index = @parking_spaces.index {|space| space.can_park? vehicle }
raise ParkingSpaceError "No spaces available" if index < 0
parking_space = @parking_spaces[index]
parking_space.park vehicle
parking_space
end

def payment_for(vehicle)
parking_space_for(vehicle).payment
end

def spaces_available?(vehicle = nil)
if vehicle.nil?
@parking_spaces.any? {|space| !space.occupied? }
else
@parking_spaces.any? {|space| space.can_park? vehicle }
end
end

private

def parking_space_for(vehicle)
index = @parking_spaces.index {|space| space.contains? vehicle }
raise ParkingSpaceError "Vehicle #{vehicle.registration_number} is not parked in this lot" if index < 0
@parking_spaces[index]
end
end


Lastly, let's pay for our spot using a ParkingSpacePayment



The ParkingSpacePayment class



This class contains all the logic for paying for a parking spot. It needs the parking space, vehicle and fee. All three things are provided by the ParkingSpace object, so we require this in the constructor.



class ParkingSpacePayment
def initialize(parking_space)
@parking_space = parking_space
@payment_date = DateTime.current
@total_hours = ((@payment_date - parking_space.date_occupied) / 1.hour).round
@amount_due = @parking_space.calculate_fee @total_hours
@amount_paid = 0
end

def amount_due
@amount_due
end

def amount_paid
@amount_paid
end

def balance
@amount_due - @amount_paid
end

def paid?
@amount_paid <= 0
end

def parking_space
@parking_space
end

def pay(amount)
@amount_paid += amount
balance
end

def payment_date
@payment_date
end

def total_hours
@total_hours
end
end


Using these classes to manage a parking lot



Now that we've got all the pieces built, let's see how we can park a car, and pay for it.



begin
# Our parking lot as 10 unrestricted spaces and 5 handicapped spaces
parking_lot = ParkingLot.new 10, 5

# Create two vehicles, one of each type.
unrestricted_vehicle = Vehicle.new "1"
handicapped_vehicle = Vehicle.new "2"
handicapped_vehicle.permits << VehiclePermit.handicapped

# Park the cars
parking_lot.park unrestricted_vehicle
parking_lot.park handicapped_vehicle

# Let's do some shopping.
sleep 3.hours

# Time to check out.
unrestricted_payment = parking_lot.payment_for unrestricted_vehicle
handicapped_payment parking_lot.payment_for handicapped_vehicle

# 20 * 3 hours should be 60. Opps! This could be a problem later
unrestricted_payment.pay 55

# 5 * 3 hours = 15
handicapped_payment.pay 15

# I guess we went to the ATM. Whew!
unrestricted_payment.pay 5 unless unrestricted_payment.paid?

# Now let's exit the parking lot. Time to go home.
unrestricted_space = parking_lot.exit unrestricted_vehicle, unrestricted_payment
puts unrestricted_space.occupied? # -> "false"

handicapped_space = parking_lot.exit handicapped_vehicle, handicapped_payment
puts handicapped_space.occupied? # -> "false"
rescue ParkingSpaceError => parking_error
puts "Oops! Had a problem parking a car: #{parking_error}"
else Exception => e
raise e # A non parking error occurred.
end


Extra Credit



Since a parking lot could have multiple levels and rows on each level, the layout of a parking lot could be abstracted away to a ParkingLotLayout class, which takes a ParkingLot as a constructor argument and defines the floors and rows that are available.






share|improve this answer





















  • absolutely wonderful answer. Where can I gain this much insight into the designs ? Please help.
    – Anony-mouse
    Jul 24 '15 at 16:01










  • this was done for fun, just to learn design principles(I'm just out of college). It's not going into production. Even though with your code it can be.
    – Anony-mouse
    Jul 24 '15 at 16:06










  • @Anony-mouse: Start out with SOLID, which is a set of foundational principals for object oriented development. Also search for "software design patterns" to learn which patterns are out there. Lastly, the "Fat Model, Skinny Controller" methodology that Ruby on Rails enthusiasts have bolstered for years has a more formal definition with Domain Driven Design.
    – Greg Burghardt
    Jul 24 '15 at 16:20












  • I am done with SOLID (Uncle bob martin and c2.com). Even though I have still issues in understanding the direct application of the Principles in Ruby. Currently I am after TDD, and it has substantially increased my ability to write good code.Can you like give me your email id or something as the discussions are personal and I have more things to ask(that is if you do not mind)
    – Anony-mouse
    Jul 24 '15 at 16:26



















4















  1. Indentation and whitespace

    The Ruby convention is 2 spaces of indentation, and blank lines between methods. I'd also recommend spaces between arguments and operators, e.g. @hour = hour.


  2. Naming

    Don't use underscores in class names. It's clear that Regular_ParkingSpace is a kind of ParkingSpace because it inherit directly from that parent class. A name like RegularSpace would be more straightforward and less of a mouthful.


  3. attr_accessor

    You're adding a number of synthesized accessor methods with attr_accessor but you never use those methods. Instead you access instance variables directly. My advice is to always use accessor methods when you can. However, attr_accessor generates both readers and writers, and they're public. Which means that external code can just say parking_lot.size = 9999, which doesn't make sense. Of course, you never actually use the size attribute for anything, which brings me to my next point:


  4. Junk code

    It sounds harsh, but I just mean "code that doesn't actually do anything". For example the @size variable in ParkingLot which is never used. Or Regular_ParkingSpace having a payment method that just calls super - something that'd happen automatically if the method wasn't there.


  5. Dangerous assumptions

    Speaking of the parking lot's size, your way of determining the number of handicap spaces is not vary robust. You just assume it's going to be one tenth of the spaces. Well, what if the parking lot has 8 spaces in total? Then you have 0.8 of a handicap space, and 7.2 regular ones. Or what if it has 213 spaces? Then you have 21.3 handicap spaces. Neither situation makes any sense. For that matter who says there are any handicap spaces at all? There's no reason - that I know if - to assume there's any proportional relationship between the two numbers.


  6. Outright bugs

    Following from the above: The way you check for remaining spaces assumes integers. If the remaining number of spaces is anything above zero, you assume that that means there's a whole parking space there. So, in turn, you assume that whatever size was originally passed to ParkingLot.new is cleanly divisible by 10. But that's not given.

    End result is that if I make a parking lot with 8 spaces, I can fit 9 vehicles: 1 handicap vehicle (in the 0.8 of a space), and 8 regular ones (the first 7 get a space each, and the last one has to fit in 0.2 of a space).

    Oh, and I can just park a car for zero hours, and I'll pay zero rupees.


  7. Pointless classes

    You parking lot classes don't really serve any purpose being classes. You instantiate one of them, only to call payment and then discard the instance. In the end, you classes could be replaced with methods, or even just an expression: a * b.


  8. Informal exceptions

    Don't just raise a string; create an exception class that inherits from StandardError and raise it instead.



In other words, there's a lot going on here. Making it "more object-oriented" is a secondary concern. And with no concept of time passing, hourly rates don't really make much difference. It's a little weird that the cost gets calculated immediately when parking, and "paid" when the car's retrieved. A real parking lot would do one or the other: Pre-pay for x amount of time (with the possibility of an extra fee if you overstay), or pay for time used when leaving. This is neither of those.






share|improve this answer





















  • wow that was nice any other improvements pertaining to the design ie like it violates SRP etc
    – Anony-mouse
    Jun 22 '15 at 6:51










  • "Don't just raise a string; create an exception class that inherits from StandardError and raise it instead." Er, why? I'm sure there's a perfectly valid reason, but could you explain that?
    – Nic Hartley
    Jun 22 '15 at 11:07










  • @QPaysTaxes Rescuing an arbitrary string is messier than rescuing from a named error type. Besides, there might be other, different exceptions you'd want to raise - like if try to park a car with no reg. number or something. Such things could have their own exception classes, making them easier to handle. The docs have a guideline that explains it well (4th paragraph of the intro). Obviously it's not required for an exercise, but best practices and all that.
    – Flambino
    Jun 22 '15 at 12:45










  • @Flambino Oh, I see. Thanks! (Crap, time to rewrite some code...)
    – Nic Hartley
    Jun 22 '15 at 15:01










  • Thank you for the help if there is any design issues please tell me.I am marking it correct.Can you propose some good books to learn design ?
    – Anony-mouse
    Jun 24 '15 at 13:14



















0














if someone is interested in Greg Burghardt's code in Laravel 5.5 in PHP.



/// ParkingFee.php
namespace AppK11Test;


class ParkingFee {
const UNRESTRICTED = 20;
const HANDICAPPED = 5;
private $rate;

public function __construct($rate) {
$this->rate = $rate;
}

public function calculate($hours) {
return $this->rate * $hours;
}
}



// VehiclePermit.php
namespace AppK11Test;


class VehiclePermit {
const HANDICAPPED = 1;
const CELEBRITY = 2;
}

// HandicappedParkingSpace.php
namespace AppK11Test;


class HandicappedParkingSpace extends ParkingSpace {
public function __construct($parkingLot, $fee, $number) {
parent::__construct($parkingLot, ParkingFee::HANDICAPPED, $number);
}

public function canPark(Vehicle $vehicle) {
return parent::canPark($vehicle) && $vehicle->isHandicapped();
}
}


// UnrestrictedParkingSpace.php
namespace AppK11Test;


class UnrestrictedParkingSpace extends ParkingSpace {
public function __construct($parkingLot, $fee, $number) {
return parent::__construct($parkingLot, ParkingFee::UNRESTRICTED, $number);
}
}


// Vehicle.php
namespace AppK11Test;

class Vehicle {
private $registrationNumber;
public $permits;

public function __construct($registrationNumber) {
$this->registrationNumber = $registrationNumber;
}

public function isHandicapped() {
if(!is_array($this->permits)) {
return false;
}

return in_array(VehiclePermit::HANDICAPPED, $this->permits);
}

/**
* @return mixed
*/
public function getRegistrationNumber() {
return $this->registrationNumber;
}
}

// ParkingSpacePayment.php
namespace AppK11Test;

use CarbonCarbon;

class ParkingSpacePayment {
private $parkingSpace;
private $paymentDate;
private $totalHours;
private $amountDue;
private $amountPaid;

public function __construct(ParkingSpace $parkingSpace) {
$this->parkingSpace = $parkingSpace;
$this->paymentDate = now();
$this->totalHours = Carbon::now()->diffInHours(Carbon::createFromTimestamp( $this->parkingSpace->dateOccupied) );
$this->amountDue = $parkingSpace->calculateFee($this->totalHours);
$this->amountPaid = 0;
}

/**
* @return int
*/
public function getAmountDue(): int {
return $this->amountDue;
}

public function balance() {
return $this->amountDue - $this->amountPaid;
}

public function isPaid() {
return self::balance() <= 0;
}

public function pay($amount) {
$this->amountPaid += $amount;
}


}


// ParkingSpace.php
namespace AppK11Test;

use MockeryException;

/**
class ParkingSpace {
private $parkingLot;
private $fee;
public $number;
private $vehicle = null;
public $dateOccupied;

public function __construct($parkingLot, $fee, $number) {
$this->parkingLot = $parkingLot;
$this->fee = $fee;
$this->number = $number;
$this->vehicle = null;
}

public function payment() {
return new ParkingSpacePayment($this);
}

public function canPark(Vehicle $vehicle) {
return !self::isOccupied();
}

public function contain(Vehicle $vehicle) {
return $this->vehicle == $vehicle;
}

public function vehicle(Vehicle $vehicle) {
$this->vehicle = $vehicle;
}

/**
* @return null
*/
public function getVehicle() {
return $this->vehicle;
}

/**
* @param null $vehicle
*/
public function setVehicle($vehicle) {
$this->vehicle = $vehicle;
}

/**
* @param mixed $number
*/
public function setNumber($number) {
$this->number = $number;
}

/**
* @return mixed
*/
public function getNumber() {
return $this->number;
}

public function isOccupied() {
return $this->vehicle !== null;
}

public function park(Vehicle $vehicle) {
if (!self::canPark($vehicle)) {
throw new Exception('Cannot park vehicle. ' . $vehicle->getRegistrationNumber());
}

$this->vehicle = $vehicle;

$this->dateOccupied = strtotime("-7 hours");

Log::info($this->dateOccupied);
}

public function vacate() {
$this->vehicle = null;
$this->dateOccupied = null;
}

public function calculateFee($hours) {
$parkingFee = new ParkingFee($this->fee);

return $parkingFee->calculate($hours);
}


}

// ParkingLot.php
namespace AppK11Test;

use MockeryException;

class ParkingLot {
private $parkingSpaces;

public function __construct($unrestrictedCount, $handicappedCount) {
$this->parkingSpaces = collect();

for ($i = 0; $i < $unrestrictedCount; $i++) {
$this->parkingSpaces->push(new UnrestrictedParkingSpace($this, ParkingFee::UNRESTRICTED, $i + 1));
}

for ($j = 1; $j <= $handicappedCount; $j++) {
$this->parkingSpaces->push(new HandicappedParkingSpace($this, ParkingFee::HANDICAPPED, $i + $j));
}
}

public function details() {
$this->parkingSpaces->each(function ($parkingSpace) {

$vNum = 0;

if ($vehicle = $parkingSpace->getVehicle()) {
$vNum = $vehicle->getRegistrationNumber();
}

Log::info('number:' . $parkingSpace->number . ', Vehicle: ' . get_class($parkingSpace) . 'Vehicle # ' . $vNum);
});
}

public function exit(Vehicle $vehicle, ParkingSpacePayment $parkingSpacePayment): ParkingSpace {
if (!$parkingSpacePayment->isPaid()) {
throw new Exception('Balance not paid: ' . $parkingSpacePayment->balance());
}

$parkingSpace = self::getParkingSpace($vehicle);
$parkingSpace->vacate();

return $parkingSpace;
}

public function park(Vehicle $vehicle): ParkingSpace {
$freeParkingSpace = self::getFreeParkingSpace($vehicle);
$freeParkingSpace->park($vehicle);

return $freeParkingSpace;
}

private function getFreeParkingSpace(Vehicle $vehicle): ParkingSpace {
$parkingSpaces = $this->parkingSpaces;

if ($vehicle->isHandicapped()) {
$parkingSpaces = $this->parkingSpaces->filter(function (ParkingSpace $parkingSpace) use ($vehicle) {
if (class_basename($parkingSpace) === 'HandicappedParkingSpace') {
return true;
}

return false;
});
}

$parkingSpace = $parkingSpaces->filter(function (ParkingSpace $parkingSpace) use ($vehicle) {

return $parkingSpace->canPark($vehicle);
})->first();

return $parkingSpace;
}

public function getPayment(Vehicle $vehicle) {
return self::getParkingSpace($vehicle)->payment();
}

private function getParkingSpace(Vehicle $vehicle): ParkingSpace {
$currentParkingSpace = null;

foreach ($this->parkingSpaces as $parkingSpace) {
if ($parkingSpace->contain($vehicle)) {
$currentParkingSpace = $parkingSpace;

break;
}
}

if (is_null($currentParkingSpace)) {
throw new Exception('Vehicle is not parked in this lot. ' . $vehicle->getRegistrationNumber());
}

return $currentParkingSpace;
}
}


// index.php
namespace AppK11Test;

use MockeryException;

try {
// Our parking lot as 10 unrestricted spaces and 5 handicapped spaces
$parkingLot = new ParkingLot(10, 5);

// Create two vehicles, one of each type.
$uVehicle = new Vehicle(2255);
$parkingLot->park($uVehicle);

$parkingLot->details();
$uVehiclePayment = $parkingLot->getPayment($uVehicle);
//echo '$uVehiclePayment->getAmountDue(): ' . $uVehiclePayment->getAmountDue();
$uVehiclePayment->pay($uVehiclePayment->getAmountDue());
$uSpace = $parkingLot->exit($uVehicle, $uVehiclePayment);

$parkingLot->details();

if ($uSpace->isOccupied()) {
echo('Still occupied!');
} else {
echo('Vacant slot: ' . $uSpace->getNumber());
}

$hVehicle = new Vehicle(2);
$hVehicle->permits = VehiclePermit::HANDICAPPED;
$parkingLot->park($hVehicle);
$parkingLot->details();
$hVehiclePayment = $parkingLot->getPayment($hVehicle);
$hVehiclePayment->pay(15);

$hSpace = $parkingLot->exit($hVehicle, $hVehiclePayment);

if ($hSpace->isOccupied()) {
echo('Still occupied!');
} else {
echo("n".'Vacant slot: ' . $hSpace->getNumber());
}
} catch (Exception $e) {
echo $e->getMessage();
}





share|improve this answer








New contributor




San is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.


















    Your Answer





    StackExchange.ifUsing("editor", function () {
    return StackExchange.using("mathjaxEditing", function () {
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    });
    });
    }, "mathjax-editing");

    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "196"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f94270%2fparking-lot-and-vehicle-classes%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1














    Since @Flambino critiqued your existing code, I will focus more on application architecture.



    When I approach this subject, first I determine what "things" are interacting in the system.




    • Parking lots

    • Vehicles

    • Parking fees (of which there are "unrestricted" and "handicapped")

    • Payments

    • Parking spaces (of which there are "unrestricted" and "handicapped")


    This gives us 5 classes. Classes are nouns (people, places or things) and methods are the verbs (actions). We will throw in two more classes to descibe the permits a vehicle can have (a handicapped sticker for those in the USA) and a custom error class for more intelligent error handling in the application.




    1. Vehicle

    2. ParkingFee

    3. ParkingSpace

    4. ParkingSpacePayment

    5. ParkingLot

    6. VehiclePermit

    7. ParkingSpaceError


    We'll get the easiest one of of the way:



    class ParkingSpaceError < StandardError
    end


    Now we've got a specific error class when problems arise parking or paying for a parking spot allowing more intelligent error handling.



    Once I've identified the basic "things" in the system, I think of how they are related. The first class I build is the one with no relations to anything. It's quick and simple: The ParkingFee class.



    The ParkingFee class



    A parking fee consists of a rate multiplied by how many hours you've parked. Plus, you only have two rates. We will create static properties for both fee types that we can reference later:



    class ParkingFee
    def self.unrestricted
    @@unrestricted ||= ParkingFee.new 20
    end

    def self.handicapped
    @@handicapped ||= ParkingFee.new 5
    end

    def initialize(rate)
    @rate = rate;
    end

    def calculate(hours)
    rate * hours
    end

    def rate
    @rate
    end
    end


    Easy peasy, rice and cheesey. The next class that's easy to create is the VehiclePermit class.



    The VehiclePermit class



    Since you must determine whether or not a vehicle is a "handicapped vehicle," this got me thinking. First, I'm assuming since you reference "rupees" in your post that you are in India, or are building software that will be used in India. In the USA, vehicles can have multiple additional attributes describing how they can be used, and where they can be parked. I believe the term that we use is "permit."



    Without understanding how vehicles are registered as "handicap" vehicles in India, I'll call this next class VehiclePermit. As we will see later, every Vehicle could have multiple permits attached to it. A "handicapped" vehicle is just a Vehicle that has the handicapped VehiclePermit associated with it.



    This class doesn't have much to it. An id and a name, plus a static property for convenience. This could be just as easily mapped from a database if you want.



    class VehiclePermit
    def self.handicapped
    @@handicapped ||= VehiclePermit.new 1, "Handicapped"
    end

    def initialize(id, name)
    @id = id;
    @name = name;
    end

    def id
    @id
    end

    def name
    @name
    end
    end


    Now we can move on to the "thing" this is all about: vehicles.



    The Vehicle class



    This class needs one or more permits, and a registration number. Still, not very complex. We'll also expose a public method to test this vehicle to see if it has the "handicapped" permit, which we will use later when parking the vehicle.



    class Vehicle
    def initialize(registration_number)
    @registration_number = registration_number
    @permits =
    end

    def is_handicapped?
    @permits.any? {|p| p == VehiclePermit.handicapped }
    end

    def permits
    @permits
    end

    def registration_number
    @registration_number
    end
    end


    We have our ParkingFee, VehiclePermit and Vehicle, now we are ready to park this thing.



    The ParkingSpace class



    The ParkingSpace class holds a vehicle, the date and time the vehicle parked, and the fee associated with it. It allows you to calculate the payment as well. We'll define public methods for all the actions you can perform on a parking space:




    • Park a car

    • Vacate the space

    • Calculate the fee

    • Test to see if a vehicle is currently occupying it

    • Test to see if it contains a particular vehicle

    • Test a vehicle to see if it can park here


    The code:



    class ParkingSpace
    def initialize(parking_lot, fee, number)
    @parking_lot = parking_lot
    @fee = fee
    @number = number
    end

    def payment
    ParkingSpacePayment.new self
    end

    def can_park?(vehicle)
    !occupied?
    end

    def contains?(vehicle)
    self.vehicle == vehicle
    end

    def number
    @number
    end

    def occupied?
    !vehicle.nil?
    end

    def park(vehicle)
    raise ParkingSpaceError "Cannot park vehicle #{vehicle.registration_number}" unless can_park? vehicle
    self.vehicle = vehicle
    date_occupied = DateTime.current
    end

    def vacate
    payment = nil
    date_occupied = nil
    vehicle = nil
    end

    def vehicle
    @vehicle
    end

    private

    def payment=(new_payment)
    @payment = new_payment
    end

    def calculate_fee(date)
    fee.calculate(date)
    end

    def vehicle=(new_vehicle)
    @vehicle = new_vehicle
    end
    end


    We aren't done yet, because there are two kinds of parking spaces. We will define concrete classes for each:



    class UnrestrictedParkingSpace < ParkingSpace
    def initialize(parking_lot, number)
    super(parking_lot, ParkingFee.unrestricted, number)
    end
    end


    An "unrestricted" parking space doesn't require much code. We just override the constructor to pass the proper ParkingFee object. The can_park? method on the parent class has the correct logic for this type of space, so we don't bother overriding it.



    class HandicappedParkingSpace < ParkingSpace
    def initialize(parking_lot, number)
    super(parking_lot, ParkingFee.handicapped, number)
    end

    def can_park?(vehicle)
    super(vehicle) && vehicle.is_handicapped?
    end
    end


    The "handicapped" parking space overrides the constructor, which passes the handicapped fee, and overrides the can_park? method. The can_park? method first delegates to the method on the super class, and if that returns true, we have an additional test to see if the vehicle has a handicapped permit, so we also call Vehicle#can_park?.



    Each type of parking space has the fee baked in so no one can make the "Unrestricted" parking space fee less that 20 Rupees. Now that we've got a parking spaces, we need some place to put them: the ParkingLot.



    The ParkingLot class



    A parking lot has a bunch of spaces, both unrestricted and handicapped. We also need to do the following things in the parking lot:




    • Park a vehicle

    • Exit the parking lot

    • Calculate a payment for a vehicle

    • Check to see if spaces are available


    The constructor for the ParkingLot class takes two arguments: the number of unrestricted spaces and the number of handicapped spaces.



    class ParkingLot
    def initialize(unrestricted_count, handicapped_count)
    count = 0
    @parking_spaces =

    unrestricted_count.times do |n|
    @parking_spaces << UnrestrictedParkingSpace.new self, n
    end

    count = @parking_spaces.count

    handicapped_count.times do |n|
    @parking_spaces << HandicappedParkingSpace.new self, count + n
    end
    end

    def exit(vehicle, payment)
    parking_space = parking_space_for vehicle
    raise ParkingSpaceError "Balance not paid: #{payment.balance}" unless payment.paid?
    parking_space.vacate vehicle
    parking_space
    end

    def park(vehicle)
    index = @parking_spaces.index {|space| space.can_park? vehicle }
    raise ParkingSpaceError "No spaces available" if index < 0
    parking_space = @parking_spaces[index]
    parking_space.park vehicle
    parking_space
    end

    def payment_for(vehicle)
    parking_space_for(vehicle).payment
    end

    def spaces_available?(vehicle = nil)
    if vehicle.nil?
    @parking_spaces.any? {|space| !space.occupied? }
    else
    @parking_spaces.any? {|space| space.can_park? vehicle }
    end
    end

    private

    def parking_space_for(vehicle)
    index = @parking_spaces.index {|space| space.contains? vehicle }
    raise ParkingSpaceError "Vehicle #{vehicle.registration_number} is not parked in this lot" if index < 0
    @parking_spaces[index]
    end
    end


    Lastly, let's pay for our spot using a ParkingSpacePayment



    The ParkingSpacePayment class



    This class contains all the logic for paying for a parking spot. It needs the parking space, vehicle and fee. All three things are provided by the ParkingSpace object, so we require this in the constructor.



    class ParkingSpacePayment
    def initialize(parking_space)
    @parking_space = parking_space
    @payment_date = DateTime.current
    @total_hours = ((@payment_date - parking_space.date_occupied) / 1.hour).round
    @amount_due = @parking_space.calculate_fee @total_hours
    @amount_paid = 0
    end

    def amount_due
    @amount_due
    end

    def amount_paid
    @amount_paid
    end

    def balance
    @amount_due - @amount_paid
    end

    def paid?
    @amount_paid <= 0
    end

    def parking_space
    @parking_space
    end

    def pay(amount)
    @amount_paid += amount
    balance
    end

    def payment_date
    @payment_date
    end

    def total_hours
    @total_hours
    end
    end


    Using these classes to manage a parking lot



    Now that we've got all the pieces built, let's see how we can park a car, and pay for it.



    begin
    # Our parking lot as 10 unrestricted spaces and 5 handicapped spaces
    parking_lot = ParkingLot.new 10, 5

    # Create two vehicles, one of each type.
    unrestricted_vehicle = Vehicle.new "1"
    handicapped_vehicle = Vehicle.new "2"
    handicapped_vehicle.permits << VehiclePermit.handicapped

    # Park the cars
    parking_lot.park unrestricted_vehicle
    parking_lot.park handicapped_vehicle

    # Let's do some shopping.
    sleep 3.hours

    # Time to check out.
    unrestricted_payment = parking_lot.payment_for unrestricted_vehicle
    handicapped_payment parking_lot.payment_for handicapped_vehicle

    # 20 * 3 hours should be 60. Opps! This could be a problem later
    unrestricted_payment.pay 55

    # 5 * 3 hours = 15
    handicapped_payment.pay 15

    # I guess we went to the ATM. Whew!
    unrestricted_payment.pay 5 unless unrestricted_payment.paid?

    # Now let's exit the parking lot. Time to go home.
    unrestricted_space = parking_lot.exit unrestricted_vehicle, unrestricted_payment
    puts unrestricted_space.occupied? # -> "false"

    handicapped_space = parking_lot.exit handicapped_vehicle, handicapped_payment
    puts handicapped_space.occupied? # -> "false"
    rescue ParkingSpaceError => parking_error
    puts "Oops! Had a problem parking a car: #{parking_error}"
    else Exception => e
    raise e # A non parking error occurred.
    end


    Extra Credit



    Since a parking lot could have multiple levels and rows on each level, the layout of a parking lot could be abstracted away to a ParkingLotLayout class, which takes a ParkingLot as a constructor argument and defines the floors and rows that are available.






    share|improve this answer





















    • absolutely wonderful answer. Where can I gain this much insight into the designs ? Please help.
      – Anony-mouse
      Jul 24 '15 at 16:01










    • this was done for fun, just to learn design principles(I'm just out of college). It's not going into production. Even though with your code it can be.
      – Anony-mouse
      Jul 24 '15 at 16:06










    • @Anony-mouse: Start out with SOLID, which is a set of foundational principals for object oriented development. Also search for "software design patterns" to learn which patterns are out there. Lastly, the "Fat Model, Skinny Controller" methodology that Ruby on Rails enthusiasts have bolstered for years has a more formal definition with Domain Driven Design.
      – Greg Burghardt
      Jul 24 '15 at 16:20












    • I am done with SOLID (Uncle bob martin and c2.com). Even though I have still issues in understanding the direct application of the Principles in Ruby. Currently I am after TDD, and it has substantially increased my ability to write good code.Can you like give me your email id or something as the discussions are personal and I have more things to ask(that is if you do not mind)
      – Anony-mouse
      Jul 24 '15 at 16:26
















    1














    Since @Flambino critiqued your existing code, I will focus more on application architecture.



    When I approach this subject, first I determine what "things" are interacting in the system.




    • Parking lots

    • Vehicles

    • Parking fees (of which there are "unrestricted" and "handicapped")

    • Payments

    • Parking spaces (of which there are "unrestricted" and "handicapped")


    This gives us 5 classes. Classes are nouns (people, places or things) and methods are the verbs (actions). We will throw in two more classes to descibe the permits a vehicle can have (a handicapped sticker for those in the USA) and a custom error class for more intelligent error handling in the application.




    1. Vehicle

    2. ParkingFee

    3. ParkingSpace

    4. ParkingSpacePayment

    5. ParkingLot

    6. VehiclePermit

    7. ParkingSpaceError


    We'll get the easiest one of of the way:



    class ParkingSpaceError < StandardError
    end


    Now we've got a specific error class when problems arise parking or paying for a parking spot allowing more intelligent error handling.



    Once I've identified the basic "things" in the system, I think of how they are related. The first class I build is the one with no relations to anything. It's quick and simple: The ParkingFee class.



    The ParkingFee class



    A parking fee consists of a rate multiplied by how many hours you've parked. Plus, you only have two rates. We will create static properties for both fee types that we can reference later:



    class ParkingFee
    def self.unrestricted
    @@unrestricted ||= ParkingFee.new 20
    end

    def self.handicapped
    @@handicapped ||= ParkingFee.new 5
    end

    def initialize(rate)
    @rate = rate;
    end

    def calculate(hours)
    rate * hours
    end

    def rate
    @rate
    end
    end


    Easy peasy, rice and cheesey. The next class that's easy to create is the VehiclePermit class.



    The VehiclePermit class



    Since you must determine whether or not a vehicle is a "handicapped vehicle," this got me thinking. First, I'm assuming since you reference "rupees" in your post that you are in India, or are building software that will be used in India. In the USA, vehicles can have multiple additional attributes describing how they can be used, and where they can be parked. I believe the term that we use is "permit."



    Without understanding how vehicles are registered as "handicap" vehicles in India, I'll call this next class VehiclePermit. As we will see later, every Vehicle could have multiple permits attached to it. A "handicapped" vehicle is just a Vehicle that has the handicapped VehiclePermit associated with it.



    This class doesn't have much to it. An id and a name, plus a static property for convenience. This could be just as easily mapped from a database if you want.



    class VehiclePermit
    def self.handicapped
    @@handicapped ||= VehiclePermit.new 1, "Handicapped"
    end

    def initialize(id, name)
    @id = id;
    @name = name;
    end

    def id
    @id
    end

    def name
    @name
    end
    end


    Now we can move on to the "thing" this is all about: vehicles.



    The Vehicle class



    This class needs one or more permits, and a registration number. Still, not very complex. We'll also expose a public method to test this vehicle to see if it has the "handicapped" permit, which we will use later when parking the vehicle.



    class Vehicle
    def initialize(registration_number)
    @registration_number = registration_number
    @permits =
    end

    def is_handicapped?
    @permits.any? {|p| p == VehiclePermit.handicapped }
    end

    def permits
    @permits
    end

    def registration_number
    @registration_number
    end
    end


    We have our ParkingFee, VehiclePermit and Vehicle, now we are ready to park this thing.



    The ParkingSpace class



    The ParkingSpace class holds a vehicle, the date and time the vehicle parked, and the fee associated with it. It allows you to calculate the payment as well. We'll define public methods for all the actions you can perform on a parking space:




    • Park a car

    • Vacate the space

    • Calculate the fee

    • Test to see if a vehicle is currently occupying it

    • Test to see if it contains a particular vehicle

    • Test a vehicle to see if it can park here


    The code:



    class ParkingSpace
    def initialize(parking_lot, fee, number)
    @parking_lot = parking_lot
    @fee = fee
    @number = number
    end

    def payment
    ParkingSpacePayment.new self
    end

    def can_park?(vehicle)
    !occupied?
    end

    def contains?(vehicle)
    self.vehicle == vehicle
    end

    def number
    @number
    end

    def occupied?
    !vehicle.nil?
    end

    def park(vehicle)
    raise ParkingSpaceError "Cannot park vehicle #{vehicle.registration_number}" unless can_park? vehicle
    self.vehicle = vehicle
    date_occupied = DateTime.current
    end

    def vacate
    payment = nil
    date_occupied = nil
    vehicle = nil
    end

    def vehicle
    @vehicle
    end

    private

    def payment=(new_payment)
    @payment = new_payment
    end

    def calculate_fee(date)
    fee.calculate(date)
    end

    def vehicle=(new_vehicle)
    @vehicle = new_vehicle
    end
    end


    We aren't done yet, because there are two kinds of parking spaces. We will define concrete classes for each:



    class UnrestrictedParkingSpace < ParkingSpace
    def initialize(parking_lot, number)
    super(parking_lot, ParkingFee.unrestricted, number)
    end
    end


    An "unrestricted" parking space doesn't require much code. We just override the constructor to pass the proper ParkingFee object. The can_park? method on the parent class has the correct logic for this type of space, so we don't bother overriding it.



    class HandicappedParkingSpace < ParkingSpace
    def initialize(parking_lot, number)
    super(parking_lot, ParkingFee.handicapped, number)
    end

    def can_park?(vehicle)
    super(vehicle) && vehicle.is_handicapped?
    end
    end


    The "handicapped" parking space overrides the constructor, which passes the handicapped fee, and overrides the can_park? method. The can_park? method first delegates to the method on the super class, and if that returns true, we have an additional test to see if the vehicle has a handicapped permit, so we also call Vehicle#can_park?.



    Each type of parking space has the fee baked in so no one can make the "Unrestricted" parking space fee less that 20 Rupees. Now that we've got a parking spaces, we need some place to put them: the ParkingLot.



    The ParkingLot class



    A parking lot has a bunch of spaces, both unrestricted and handicapped. We also need to do the following things in the parking lot:




    • Park a vehicle

    • Exit the parking lot

    • Calculate a payment for a vehicle

    • Check to see if spaces are available


    The constructor for the ParkingLot class takes two arguments: the number of unrestricted spaces and the number of handicapped spaces.



    class ParkingLot
    def initialize(unrestricted_count, handicapped_count)
    count = 0
    @parking_spaces =

    unrestricted_count.times do |n|
    @parking_spaces << UnrestrictedParkingSpace.new self, n
    end

    count = @parking_spaces.count

    handicapped_count.times do |n|
    @parking_spaces << HandicappedParkingSpace.new self, count + n
    end
    end

    def exit(vehicle, payment)
    parking_space = parking_space_for vehicle
    raise ParkingSpaceError "Balance not paid: #{payment.balance}" unless payment.paid?
    parking_space.vacate vehicle
    parking_space
    end

    def park(vehicle)
    index = @parking_spaces.index {|space| space.can_park? vehicle }
    raise ParkingSpaceError "No spaces available" if index < 0
    parking_space = @parking_spaces[index]
    parking_space.park vehicle
    parking_space
    end

    def payment_for(vehicle)
    parking_space_for(vehicle).payment
    end

    def spaces_available?(vehicle = nil)
    if vehicle.nil?
    @parking_spaces.any? {|space| !space.occupied? }
    else
    @parking_spaces.any? {|space| space.can_park? vehicle }
    end
    end

    private

    def parking_space_for(vehicle)
    index = @parking_spaces.index {|space| space.contains? vehicle }
    raise ParkingSpaceError "Vehicle #{vehicle.registration_number} is not parked in this lot" if index < 0
    @parking_spaces[index]
    end
    end


    Lastly, let's pay for our spot using a ParkingSpacePayment



    The ParkingSpacePayment class



    This class contains all the logic for paying for a parking spot. It needs the parking space, vehicle and fee. All three things are provided by the ParkingSpace object, so we require this in the constructor.



    class ParkingSpacePayment
    def initialize(parking_space)
    @parking_space = parking_space
    @payment_date = DateTime.current
    @total_hours = ((@payment_date - parking_space.date_occupied) / 1.hour).round
    @amount_due = @parking_space.calculate_fee @total_hours
    @amount_paid = 0
    end

    def amount_due
    @amount_due
    end

    def amount_paid
    @amount_paid
    end

    def balance
    @amount_due - @amount_paid
    end

    def paid?
    @amount_paid <= 0
    end

    def parking_space
    @parking_space
    end

    def pay(amount)
    @amount_paid += amount
    balance
    end

    def payment_date
    @payment_date
    end

    def total_hours
    @total_hours
    end
    end


    Using these classes to manage a parking lot



    Now that we've got all the pieces built, let's see how we can park a car, and pay for it.



    begin
    # Our parking lot as 10 unrestricted spaces and 5 handicapped spaces
    parking_lot = ParkingLot.new 10, 5

    # Create two vehicles, one of each type.
    unrestricted_vehicle = Vehicle.new "1"
    handicapped_vehicle = Vehicle.new "2"
    handicapped_vehicle.permits << VehiclePermit.handicapped

    # Park the cars
    parking_lot.park unrestricted_vehicle
    parking_lot.park handicapped_vehicle

    # Let's do some shopping.
    sleep 3.hours

    # Time to check out.
    unrestricted_payment = parking_lot.payment_for unrestricted_vehicle
    handicapped_payment parking_lot.payment_for handicapped_vehicle

    # 20 * 3 hours should be 60. Opps! This could be a problem later
    unrestricted_payment.pay 55

    # 5 * 3 hours = 15
    handicapped_payment.pay 15

    # I guess we went to the ATM. Whew!
    unrestricted_payment.pay 5 unless unrestricted_payment.paid?

    # Now let's exit the parking lot. Time to go home.
    unrestricted_space = parking_lot.exit unrestricted_vehicle, unrestricted_payment
    puts unrestricted_space.occupied? # -> "false"

    handicapped_space = parking_lot.exit handicapped_vehicle, handicapped_payment
    puts handicapped_space.occupied? # -> "false"
    rescue ParkingSpaceError => parking_error
    puts "Oops! Had a problem parking a car: #{parking_error}"
    else Exception => e
    raise e # A non parking error occurred.
    end


    Extra Credit



    Since a parking lot could have multiple levels and rows on each level, the layout of a parking lot could be abstracted away to a ParkingLotLayout class, which takes a ParkingLot as a constructor argument and defines the floors and rows that are available.






    share|improve this answer





















    • absolutely wonderful answer. Where can I gain this much insight into the designs ? Please help.
      – Anony-mouse
      Jul 24 '15 at 16:01










    • this was done for fun, just to learn design principles(I'm just out of college). It's not going into production. Even though with your code it can be.
      – Anony-mouse
      Jul 24 '15 at 16:06










    • @Anony-mouse: Start out with SOLID, which is a set of foundational principals for object oriented development. Also search for "software design patterns" to learn which patterns are out there. Lastly, the "Fat Model, Skinny Controller" methodology that Ruby on Rails enthusiasts have bolstered for years has a more formal definition with Domain Driven Design.
      – Greg Burghardt
      Jul 24 '15 at 16:20












    • I am done with SOLID (Uncle bob martin and c2.com). Even though I have still issues in understanding the direct application of the Principles in Ruby. Currently I am after TDD, and it has substantially increased my ability to write good code.Can you like give me your email id or something as the discussions are personal and I have more things to ask(that is if you do not mind)
      – Anony-mouse
      Jul 24 '15 at 16:26














    1












    1








    1






    Since @Flambino critiqued your existing code, I will focus more on application architecture.



    When I approach this subject, first I determine what "things" are interacting in the system.




    • Parking lots

    • Vehicles

    • Parking fees (of which there are "unrestricted" and "handicapped")

    • Payments

    • Parking spaces (of which there are "unrestricted" and "handicapped")


    This gives us 5 classes. Classes are nouns (people, places or things) and methods are the verbs (actions). We will throw in two more classes to descibe the permits a vehicle can have (a handicapped sticker for those in the USA) and a custom error class for more intelligent error handling in the application.




    1. Vehicle

    2. ParkingFee

    3. ParkingSpace

    4. ParkingSpacePayment

    5. ParkingLot

    6. VehiclePermit

    7. ParkingSpaceError


    We'll get the easiest one of of the way:



    class ParkingSpaceError < StandardError
    end


    Now we've got a specific error class when problems arise parking or paying for a parking spot allowing more intelligent error handling.



    Once I've identified the basic "things" in the system, I think of how they are related. The first class I build is the one with no relations to anything. It's quick and simple: The ParkingFee class.



    The ParkingFee class



    A parking fee consists of a rate multiplied by how many hours you've parked. Plus, you only have two rates. We will create static properties for both fee types that we can reference later:



    class ParkingFee
    def self.unrestricted
    @@unrestricted ||= ParkingFee.new 20
    end

    def self.handicapped
    @@handicapped ||= ParkingFee.new 5
    end

    def initialize(rate)
    @rate = rate;
    end

    def calculate(hours)
    rate * hours
    end

    def rate
    @rate
    end
    end


    Easy peasy, rice and cheesey. The next class that's easy to create is the VehiclePermit class.



    The VehiclePermit class



    Since you must determine whether or not a vehicle is a "handicapped vehicle," this got me thinking. First, I'm assuming since you reference "rupees" in your post that you are in India, or are building software that will be used in India. In the USA, vehicles can have multiple additional attributes describing how they can be used, and where they can be parked. I believe the term that we use is "permit."



    Without understanding how vehicles are registered as "handicap" vehicles in India, I'll call this next class VehiclePermit. As we will see later, every Vehicle could have multiple permits attached to it. A "handicapped" vehicle is just a Vehicle that has the handicapped VehiclePermit associated with it.



    This class doesn't have much to it. An id and a name, plus a static property for convenience. This could be just as easily mapped from a database if you want.



    class VehiclePermit
    def self.handicapped
    @@handicapped ||= VehiclePermit.new 1, "Handicapped"
    end

    def initialize(id, name)
    @id = id;
    @name = name;
    end

    def id
    @id
    end

    def name
    @name
    end
    end


    Now we can move on to the "thing" this is all about: vehicles.



    The Vehicle class



    This class needs one or more permits, and a registration number. Still, not very complex. We'll also expose a public method to test this vehicle to see if it has the "handicapped" permit, which we will use later when parking the vehicle.



    class Vehicle
    def initialize(registration_number)
    @registration_number = registration_number
    @permits =
    end

    def is_handicapped?
    @permits.any? {|p| p == VehiclePermit.handicapped }
    end

    def permits
    @permits
    end

    def registration_number
    @registration_number
    end
    end


    We have our ParkingFee, VehiclePermit and Vehicle, now we are ready to park this thing.



    The ParkingSpace class



    The ParkingSpace class holds a vehicle, the date and time the vehicle parked, and the fee associated with it. It allows you to calculate the payment as well. We'll define public methods for all the actions you can perform on a parking space:




    • Park a car

    • Vacate the space

    • Calculate the fee

    • Test to see if a vehicle is currently occupying it

    • Test to see if it contains a particular vehicle

    • Test a vehicle to see if it can park here


    The code:



    class ParkingSpace
    def initialize(parking_lot, fee, number)
    @parking_lot = parking_lot
    @fee = fee
    @number = number
    end

    def payment
    ParkingSpacePayment.new self
    end

    def can_park?(vehicle)
    !occupied?
    end

    def contains?(vehicle)
    self.vehicle == vehicle
    end

    def number
    @number
    end

    def occupied?
    !vehicle.nil?
    end

    def park(vehicle)
    raise ParkingSpaceError "Cannot park vehicle #{vehicle.registration_number}" unless can_park? vehicle
    self.vehicle = vehicle
    date_occupied = DateTime.current
    end

    def vacate
    payment = nil
    date_occupied = nil
    vehicle = nil
    end

    def vehicle
    @vehicle
    end

    private

    def payment=(new_payment)
    @payment = new_payment
    end

    def calculate_fee(date)
    fee.calculate(date)
    end

    def vehicle=(new_vehicle)
    @vehicle = new_vehicle
    end
    end


    We aren't done yet, because there are two kinds of parking spaces. We will define concrete classes for each:



    class UnrestrictedParkingSpace < ParkingSpace
    def initialize(parking_lot, number)
    super(parking_lot, ParkingFee.unrestricted, number)
    end
    end


    An "unrestricted" parking space doesn't require much code. We just override the constructor to pass the proper ParkingFee object. The can_park? method on the parent class has the correct logic for this type of space, so we don't bother overriding it.



    class HandicappedParkingSpace < ParkingSpace
    def initialize(parking_lot, number)
    super(parking_lot, ParkingFee.handicapped, number)
    end

    def can_park?(vehicle)
    super(vehicle) && vehicle.is_handicapped?
    end
    end


    The "handicapped" parking space overrides the constructor, which passes the handicapped fee, and overrides the can_park? method. The can_park? method first delegates to the method on the super class, and if that returns true, we have an additional test to see if the vehicle has a handicapped permit, so we also call Vehicle#can_park?.



    Each type of parking space has the fee baked in so no one can make the "Unrestricted" parking space fee less that 20 Rupees. Now that we've got a parking spaces, we need some place to put them: the ParkingLot.



    The ParkingLot class



    A parking lot has a bunch of spaces, both unrestricted and handicapped. We also need to do the following things in the parking lot:




    • Park a vehicle

    • Exit the parking lot

    • Calculate a payment for a vehicle

    • Check to see if spaces are available


    The constructor for the ParkingLot class takes two arguments: the number of unrestricted spaces and the number of handicapped spaces.



    class ParkingLot
    def initialize(unrestricted_count, handicapped_count)
    count = 0
    @parking_spaces =

    unrestricted_count.times do |n|
    @parking_spaces << UnrestrictedParkingSpace.new self, n
    end

    count = @parking_spaces.count

    handicapped_count.times do |n|
    @parking_spaces << HandicappedParkingSpace.new self, count + n
    end
    end

    def exit(vehicle, payment)
    parking_space = parking_space_for vehicle
    raise ParkingSpaceError "Balance not paid: #{payment.balance}" unless payment.paid?
    parking_space.vacate vehicle
    parking_space
    end

    def park(vehicle)
    index = @parking_spaces.index {|space| space.can_park? vehicle }
    raise ParkingSpaceError "No spaces available" if index < 0
    parking_space = @parking_spaces[index]
    parking_space.park vehicle
    parking_space
    end

    def payment_for(vehicle)
    parking_space_for(vehicle).payment
    end

    def spaces_available?(vehicle = nil)
    if vehicle.nil?
    @parking_spaces.any? {|space| !space.occupied? }
    else
    @parking_spaces.any? {|space| space.can_park? vehicle }
    end
    end

    private

    def parking_space_for(vehicle)
    index = @parking_spaces.index {|space| space.contains? vehicle }
    raise ParkingSpaceError "Vehicle #{vehicle.registration_number} is not parked in this lot" if index < 0
    @parking_spaces[index]
    end
    end


    Lastly, let's pay for our spot using a ParkingSpacePayment



    The ParkingSpacePayment class



    This class contains all the logic for paying for a parking spot. It needs the parking space, vehicle and fee. All three things are provided by the ParkingSpace object, so we require this in the constructor.



    class ParkingSpacePayment
    def initialize(parking_space)
    @parking_space = parking_space
    @payment_date = DateTime.current
    @total_hours = ((@payment_date - parking_space.date_occupied) / 1.hour).round
    @amount_due = @parking_space.calculate_fee @total_hours
    @amount_paid = 0
    end

    def amount_due
    @amount_due
    end

    def amount_paid
    @amount_paid
    end

    def balance
    @amount_due - @amount_paid
    end

    def paid?
    @amount_paid <= 0
    end

    def parking_space
    @parking_space
    end

    def pay(amount)
    @amount_paid += amount
    balance
    end

    def payment_date
    @payment_date
    end

    def total_hours
    @total_hours
    end
    end


    Using these classes to manage a parking lot



    Now that we've got all the pieces built, let's see how we can park a car, and pay for it.



    begin
    # Our parking lot as 10 unrestricted spaces and 5 handicapped spaces
    parking_lot = ParkingLot.new 10, 5

    # Create two vehicles, one of each type.
    unrestricted_vehicle = Vehicle.new "1"
    handicapped_vehicle = Vehicle.new "2"
    handicapped_vehicle.permits << VehiclePermit.handicapped

    # Park the cars
    parking_lot.park unrestricted_vehicle
    parking_lot.park handicapped_vehicle

    # Let's do some shopping.
    sleep 3.hours

    # Time to check out.
    unrestricted_payment = parking_lot.payment_for unrestricted_vehicle
    handicapped_payment parking_lot.payment_for handicapped_vehicle

    # 20 * 3 hours should be 60. Opps! This could be a problem later
    unrestricted_payment.pay 55

    # 5 * 3 hours = 15
    handicapped_payment.pay 15

    # I guess we went to the ATM. Whew!
    unrestricted_payment.pay 5 unless unrestricted_payment.paid?

    # Now let's exit the parking lot. Time to go home.
    unrestricted_space = parking_lot.exit unrestricted_vehicle, unrestricted_payment
    puts unrestricted_space.occupied? # -> "false"

    handicapped_space = parking_lot.exit handicapped_vehicle, handicapped_payment
    puts handicapped_space.occupied? # -> "false"
    rescue ParkingSpaceError => parking_error
    puts "Oops! Had a problem parking a car: #{parking_error}"
    else Exception => e
    raise e # A non parking error occurred.
    end


    Extra Credit



    Since a parking lot could have multiple levels and rows on each level, the layout of a parking lot could be abstracted away to a ParkingLotLayout class, which takes a ParkingLot as a constructor argument and defines the floors and rows that are available.






    share|improve this answer












    Since @Flambino critiqued your existing code, I will focus more on application architecture.



    When I approach this subject, first I determine what "things" are interacting in the system.




    • Parking lots

    • Vehicles

    • Parking fees (of which there are "unrestricted" and "handicapped")

    • Payments

    • Parking spaces (of which there are "unrestricted" and "handicapped")


    This gives us 5 classes. Classes are nouns (people, places or things) and methods are the verbs (actions). We will throw in two more classes to descibe the permits a vehicle can have (a handicapped sticker for those in the USA) and a custom error class for more intelligent error handling in the application.




    1. Vehicle

    2. ParkingFee

    3. ParkingSpace

    4. ParkingSpacePayment

    5. ParkingLot

    6. VehiclePermit

    7. ParkingSpaceError


    We'll get the easiest one of of the way:



    class ParkingSpaceError < StandardError
    end


    Now we've got a specific error class when problems arise parking or paying for a parking spot allowing more intelligent error handling.



    Once I've identified the basic "things" in the system, I think of how they are related. The first class I build is the one with no relations to anything. It's quick and simple: The ParkingFee class.



    The ParkingFee class



    A parking fee consists of a rate multiplied by how many hours you've parked. Plus, you only have two rates. We will create static properties for both fee types that we can reference later:



    class ParkingFee
    def self.unrestricted
    @@unrestricted ||= ParkingFee.new 20
    end

    def self.handicapped
    @@handicapped ||= ParkingFee.new 5
    end

    def initialize(rate)
    @rate = rate;
    end

    def calculate(hours)
    rate * hours
    end

    def rate
    @rate
    end
    end


    Easy peasy, rice and cheesey. The next class that's easy to create is the VehiclePermit class.



    The VehiclePermit class



    Since you must determine whether or not a vehicle is a "handicapped vehicle," this got me thinking. First, I'm assuming since you reference "rupees" in your post that you are in India, or are building software that will be used in India. In the USA, vehicles can have multiple additional attributes describing how they can be used, and where they can be parked. I believe the term that we use is "permit."



    Without understanding how vehicles are registered as "handicap" vehicles in India, I'll call this next class VehiclePermit. As we will see later, every Vehicle could have multiple permits attached to it. A "handicapped" vehicle is just a Vehicle that has the handicapped VehiclePermit associated with it.



    This class doesn't have much to it. An id and a name, plus a static property for convenience. This could be just as easily mapped from a database if you want.



    class VehiclePermit
    def self.handicapped
    @@handicapped ||= VehiclePermit.new 1, "Handicapped"
    end

    def initialize(id, name)
    @id = id;
    @name = name;
    end

    def id
    @id
    end

    def name
    @name
    end
    end


    Now we can move on to the "thing" this is all about: vehicles.



    The Vehicle class



    This class needs one or more permits, and a registration number. Still, not very complex. We'll also expose a public method to test this vehicle to see if it has the "handicapped" permit, which we will use later when parking the vehicle.



    class Vehicle
    def initialize(registration_number)
    @registration_number = registration_number
    @permits =
    end

    def is_handicapped?
    @permits.any? {|p| p == VehiclePermit.handicapped }
    end

    def permits
    @permits
    end

    def registration_number
    @registration_number
    end
    end


    We have our ParkingFee, VehiclePermit and Vehicle, now we are ready to park this thing.



    The ParkingSpace class



    The ParkingSpace class holds a vehicle, the date and time the vehicle parked, and the fee associated with it. It allows you to calculate the payment as well. We'll define public methods for all the actions you can perform on a parking space:




    • Park a car

    • Vacate the space

    • Calculate the fee

    • Test to see if a vehicle is currently occupying it

    • Test to see if it contains a particular vehicle

    • Test a vehicle to see if it can park here


    The code:



    class ParkingSpace
    def initialize(parking_lot, fee, number)
    @parking_lot = parking_lot
    @fee = fee
    @number = number
    end

    def payment
    ParkingSpacePayment.new self
    end

    def can_park?(vehicle)
    !occupied?
    end

    def contains?(vehicle)
    self.vehicle == vehicle
    end

    def number
    @number
    end

    def occupied?
    !vehicle.nil?
    end

    def park(vehicle)
    raise ParkingSpaceError "Cannot park vehicle #{vehicle.registration_number}" unless can_park? vehicle
    self.vehicle = vehicle
    date_occupied = DateTime.current
    end

    def vacate
    payment = nil
    date_occupied = nil
    vehicle = nil
    end

    def vehicle
    @vehicle
    end

    private

    def payment=(new_payment)
    @payment = new_payment
    end

    def calculate_fee(date)
    fee.calculate(date)
    end

    def vehicle=(new_vehicle)
    @vehicle = new_vehicle
    end
    end


    We aren't done yet, because there are two kinds of parking spaces. We will define concrete classes for each:



    class UnrestrictedParkingSpace < ParkingSpace
    def initialize(parking_lot, number)
    super(parking_lot, ParkingFee.unrestricted, number)
    end
    end


    An "unrestricted" parking space doesn't require much code. We just override the constructor to pass the proper ParkingFee object. The can_park? method on the parent class has the correct logic for this type of space, so we don't bother overriding it.



    class HandicappedParkingSpace < ParkingSpace
    def initialize(parking_lot, number)
    super(parking_lot, ParkingFee.handicapped, number)
    end

    def can_park?(vehicle)
    super(vehicle) && vehicle.is_handicapped?
    end
    end


    The "handicapped" parking space overrides the constructor, which passes the handicapped fee, and overrides the can_park? method. The can_park? method first delegates to the method on the super class, and if that returns true, we have an additional test to see if the vehicle has a handicapped permit, so we also call Vehicle#can_park?.



    Each type of parking space has the fee baked in so no one can make the "Unrestricted" parking space fee less that 20 Rupees. Now that we've got a parking spaces, we need some place to put them: the ParkingLot.



    The ParkingLot class



    A parking lot has a bunch of spaces, both unrestricted and handicapped. We also need to do the following things in the parking lot:




    • Park a vehicle

    • Exit the parking lot

    • Calculate a payment for a vehicle

    • Check to see if spaces are available


    The constructor for the ParkingLot class takes two arguments: the number of unrestricted spaces and the number of handicapped spaces.



    class ParkingLot
    def initialize(unrestricted_count, handicapped_count)
    count = 0
    @parking_spaces =

    unrestricted_count.times do |n|
    @parking_spaces << UnrestrictedParkingSpace.new self, n
    end

    count = @parking_spaces.count

    handicapped_count.times do |n|
    @parking_spaces << HandicappedParkingSpace.new self, count + n
    end
    end

    def exit(vehicle, payment)
    parking_space = parking_space_for vehicle
    raise ParkingSpaceError "Balance not paid: #{payment.balance}" unless payment.paid?
    parking_space.vacate vehicle
    parking_space
    end

    def park(vehicle)
    index = @parking_spaces.index {|space| space.can_park? vehicle }
    raise ParkingSpaceError "No spaces available" if index < 0
    parking_space = @parking_spaces[index]
    parking_space.park vehicle
    parking_space
    end

    def payment_for(vehicle)
    parking_space_for(vehicle).payment
    end

    def spaces_available?(vehicle = nil)
    if vehicle.nil?
    @parking_spaces.any? {|space| !space.occupied? }
    else
    @parking_spaces.any? {|space| space.can_park? vehicle }
    end
    end

    private

    def parking_space_for(vehicle)
    index = @parking_spaces.index {|space| space.contains? vehicle }
    raise ParkingSpaceError "Vehicle #{vehicle.registration_number} is not parked in this lot" if index < 0
    @parking_spaces[index]
    end
    end


    Lastly, let's pay for our spot using a ParkingSpacePayment



    The ParkingSpacePayment class



    This class contains all the logic for paying for a parking spot. It needs the parking space, vehicle and fee. All three things are provided by the ParkingSpace object, so we require this in the constructor.



    class ParkingSpacePayment
    def initialize(parking_space)
    @parking_space = parking_space
    @payment_date = DateTime.current
    @total_hours = ((@payment_date - parking_space.date_occupied) / 1.hour).round
    @amount_due = @parking_space.calculate_fee @total_hours
    @amount_paid = 0
    end

    def amount_due
    @amount_due
    end

    def amount_paid
    @amount_paid
    end

    def balance
    @amount_due - @amount_paid
    end

    def paid?
    @amount_paid <= 0
    end

    def parking_space
    @parking_space
    end

    def pay(amount)
    @amount_paid += amount
    balance
    end

    def payment_date
    @payment_date
    end

    def total_hours
    @total_hours
    end
    end


    Using these classes to manage a parking lot



    Now that we've got all the pieces built, let's see how we can park a car, and pay for it.



    begin
    # Our parking lot as 10 unrestricted spaces and 5 handicapped spaces
    parking_lot = ParkingLot.new 10, 5

    # Create two vehicles, one of each type.
    unrestricted_vehicle = Vehicle.new "1"
    handicapped_vehicle = Vehicle.new "2"
    handicapped_vehicle.permits << VehiclePermit.handicapped

    # Park the cars
    parking_lot.park unrestricted_vehicle
    parking_lot.park handicapped_vehicle

    # Let's do some shopping.
    sleep 3.hours

    # Time to check out.
    unrestricted_payment = parking_lot.payment_for unrestricted_vehicle
    handicapped_payment parking_lot.payment_for handicapped_vehicle

    # 20 * 3 hours should be 60. Opps! This could be a problem later
    unrestricted_payment.pay 55

    # 5 * 3 hours = 15
    handicapped_payment.pay 15

    # I guess we went to the ATM. Whew!
    unrestricted_payment.pay 5 unless unrestricted_payment.paid?

    # Now let's exit the parking lot. Time to go home.
    unrestricted_space = parking_lot.exit unrestricted_vehicle, unrestricted_payment
    puts unrestricted_space.occupied? # -> "false"

    handicapped_space = parking_lot.exit handicapped_vehicle, handicapped_payment
    puts handicapped_space.occupied? # -> "false"
    rescue ParkingSpaceError => parking_error
    puts "Oops! Had a problem parking a car: #{parking_error}"
    else Exception => e
    raise e # A non parking error occurred.
    end


    Extra Credit



    Since a parking lot could have multiple levels and rows on each level, the layout of a parking lot could be abstracted away to a ParkingLotLayout class, which takes a ParkingLot as a constructor argument and defines the floors and rows that are available.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Jul 24 '15 at 13:34









    Greg Burghardt

    4,946620




    4,946620












    • absolutely wonderful answer. Where can I gain this much insight into the designs ? Please help.
      – Anony-mouse
      Jul 24 '15 at 16:01










    • this was done for fun, just to learn design principles(I'm just out of college). It's not going into production. Even though with your code it can be.
      – Anony-mouse
      Jul 24 '15 at 16:06










    • @Anony-mouse: Start out with SOLID, which is a set of foundational principals for object oriented development. Also search for "software design patterns" to learn which patterns are out there. Lastly, the "Fat Model, Skinny Controller" methodology that Ruby on Rails enthusiasts have bolstered for years has a more formal definition with Domain Driven Design.
      – Greg Burghardt
      Jul 24 '15 at 16:20












    • I am done with SOLID (Uncle bob martin and c2.com). Even though I have still issues in understanding the direct application of the Principles in Ruby. Currently I am after TDD, and it has substantially increased my ability to write good code.Can you like give me your email id or something as the discussions are personal and I have more things to ask(that is if you do not mind)
      – Anony-mouse
      Jul 24 '15 at 16:26


















    • absolutely wonderful answer. Where can I gain this much insight into the designs ? Please help.
      – Anony-mouse
      Jul 24 '15 at 16:01










    • this was done for fun, just to learn design principles(I'm just out of college). It's not going into production. Even though with your code it can be.
      – Anony-mouse
      Jul 24 '15 at 16:06










    • @Anony-mouse: Start out with SOLID, which is a set of foundational principals for object oriented development. Also search for "software design patterns" to learn which patterns are out there. Lastly, the "Fat Model, Skinny Controller" methodology that Ruby on Rails enthusiasts have bolstered for years has a more formal definition with Domain Driven Design.
      – Greg Burghardt
      Jul 24 '15 at 16:20












    • I am done with SOLID (Uncle bob martin and c2.com). Even though I have still issues in understanding the direct application of the Principles in Ruby. Currently I am after TDD, and it has substantially increased my ability to write good code.Can you like give me your email id or something as the discussions are personal and I have more things to ask(that is if you do not mind)
      – Anony-mouse
      Jul 24 '15 at 16:26
















    absolutely wonderful answer. Where can I gain this much insight into the designs ? Please help.
    – Anony-mouse
    Jul 24 '15 at 16:01




    absolutely wonderful answer. Where can I gain this much insight into the designs ? Please help.
    – Anony-mouse
    Jul 24 '15 at 16:01












    this was done for fun, just to learn design principles(I'm just out of college). It's not going into production. Even though with your code it can be.
    – Anony-mouse
    Jul 24 '15 at 16:06




    this was done for fun, just to learn design principles(I'm just out of college). It's not going into production. Even though with your code it can be.
    – Anony-mouse
    Jul 24 '15 at 16:06












    @Anony-mouse: Start out with SOLID, which is a set of foundational principals for object oriented development. Also search for "software design patterns" to learn which patterns are out there. Lastly, the "Fat Model, Skinny Controller" methodology that Ruby on Rails enthusiasts have bolstered for years has a more formal definition with Domain Driven Design.
    – Greg Burghardt
    Jul 24 '15 at 16:20






    @Anony-mouse: Start out with SOLID, which is a set of foundational principals for object oriented development. Also search for "software design patterns" to learn which patterns are out there. Lastly, the "Fat Model, Skinny Controller" methodology that Ruby on Rails enthusiasts have bolstered for years has a more formal definition with Domain Driven Design.
    – Greg Burghardt
    Jul 24 '15 at 16:20














    I am done with SOLID (Uncle bob martin and c2.com). Even though I have still issues in understanding the direct application of the Principles in Ruby. Currently I am after TDD, and it has substantially increased my ability to write good code.Can you like give me your email id or something as the discussions are personal and I have more things to ask(that is if you do not mind)
    – Anony-mouse
    Jul 24 '15 at 16:26




    I am done with SOLID (Uncle bob martin and c2.com). Even though I have still issues in understanding the direct application of the Principles in Ruby. Currently I am after TDD, and it has substantially increased my ability to write good code.Can you like give me your email id or something as the discussions are personal and I have more things to ask(that is if you do not mind)
    – Anony-mouse
    Jul 24 '15 at 16:26













    4















    1. Indentation and whitespace

      The Ruby convention is 2 spaces of indentation, and blank lines between methods. I'd also recommend spaces between arguments and operators, e.g. @hour = hour.


    2. Naming

      Don't use underscores in class names. It's clear that Regular_ParkingSpace is a kind of ParkingSpace because it inherit directly from that parent class. A name like RegularSpace would be more straightforward and less of a mouthful.


    3. attr_accessor

      You're adding a number of synthesized accessor methods with attr_accessor but you never use those methods. Instead you access instance variables directly. My advice is to always use accessor methods when you can. However, attr_accessor generates both readers and writers, and they're public. Which means that external code can just say parking_lot.size = 9999, which doesn't make sense. Of course, you never actually use the size attribute for anything, which brings me to my next point:


    4. Junk code

      It sounds harsh, but I just mean "code that doesn't actually do anything". For example the @size variable in ParkingLot which is never used. Or Regular_ParkingSpace having a payment method that just calls super - something that'd happen automatically if the method wasn't there.


    5. Dangerous assumptions

      Speaking of the parking lot's size, your way of determining the number of handicap spaces is not vary robust. You just assume it's going to be one tenth of the spaces. Well, what if the parking lot has 8 spaces in total? Then you have 0.8 of a handicap space, and 7.2 regular ones. Or what if it has 213 spaces? Then you have 21.3 handicap spaces. Neither situation makes any sense. For that matter who says there are any handicap spaces at all? There's no reason - that I know if - to assume there's any proportional relationship between the two numbers.


    6. Outright bugs

      Following from the above: The way you check for remaining spaces assumes integers. If the remaining number of spaces is anything above zero, you assume that that means there's a whole parking space there. So, in turn, you assume that whatever size was originally passed to ParkingLot.new is cleanly divisible by 10. But that's not given.

      End result is that if I make a parking lot with 8 spaces, I can fit 9 vehicles: 1 handicap vehicle (in the 0.8 of a space), and 8 regular ones (the first 7 get a space each, and the last one has to fit in 0.2 of a space).

      Oh, and I can just park a car for zero hours, and I'll pay zero rupees.


    7. Pointless classes

      You parking lot classes don't really serve any purpose being classes. You instantiate one of them, only to call payment and then discard the instance. In the end, you classes could be replaced with methods, or even just an expression: a * b.


    8. Informal exceptions

      Don't just raise a string; create an exception class that inherits from StandardError and raise it instead.



    In other words, there's a lot going on here. Making it "more object-oriented" is a secondary concern. And with no concept of time passing, hourly rates don't really make much difference. It's a little weird that the cost gets calculated immediately when parking, and "paid" when the car's retrieved. A real parking lot would do one or the other: Pre-pay for x amount of time (with the possibility of an extra fee if you overstay), or pay for time used when leaving. This is neither of those.






    share|improve this answer





















    • wow that was nice any other improvements pertaining to the design ie like it violates SRP etc
      – Anony-mouse
      Jun 22 '15 at 6:51










    • "Don't just raise a string; create an exception class that inherits from StandardError and raise it instead." Er, why? I'm sure there's a perfectly valid reason, but could you explain that?
      – Nic Hartley
      Jun 22 '15 at 11:07










    • @QPaysTaxes Rescuing an arbitrary string is messier than rescuing from a named error type. Besides, there might be other, different exceptions you'd want to raise - like if try to park a car with no reg. number or something. Such things could have their own exception classes, making them easier to handle. The docs have a guideline that explains it well (4th paragraph of the intro). Obviously it's not required for an exercise, but best practices and all that.
      – Flambino
      Jun 22 '15 at 12:45










    • @Flambino Oh, I see. Thanks! (Crap, time to rewrite some code...)
      – Nic Hartley
      Jun 22 '15 at 15:01










    • Thank you for the help if there is any design issues please tell me.I am marking it correct.Can you propose some good books to learn design ?
      – Anony-mouse
      Jun 24 '15 at 13:14
















    4















    1. Indentation and whitespace

      The Ruby convention is 2 spaces of indentation, and blank lines between methods. I'd also recommend spaces between arguments and operators, e.g. @hour = hour.


    2. Naming

      Don't use underscores in class names. It's clear that Regular_ParkingSpace is a kind of ParkingSpace because it inherit directly from that parent class. A name like RegularSpace would be more straightforward and less of a mouthful.


    3. attr_accessor

      You're adding a number of synthesized accessor methods with attr_accessor but you never use those methods. Instead you access instance variables directly. My advice is to always use accessor methods when you can. However, attr_accessor generates both readers and writers, and they're public. Which means that external code can just say parking_lot.size = 9999, which doesn't make sense. Of course, you never actually use the size attribute for anything, which brings me to my next point:


    4. Junk code

      It sounds harsh, but I just mean "code that doesn't actually do anything". For example the @size variable in ParkingLot which is never used. Or Regular_ParkingSpace having a payment method that just calls super - something that'd happen automatically if the method wasn't there.


    5. Dangerous assumptions

      Speaking of the parking lot's size, your way of determining the number of handicap spaces is not vary robust. You just assume it's going to be one tenth of the spaces. Well, what if the parking lot has 8 spaces in total? Then you have 0.8 of a handicap space, and 7.2 regular ones. Or what if it has 213 spaces? Then you have 21.3 handicap spaces. Neither situation makes any sense. For that matter who says there are any handicap spaces at all? There's no reason - that I know if - to assume there's any proportional relationship between the two numbers.


    6. Outright bugs

      Following from the above: The way you check for remaining spaces assumes integers. If the remaining number of spaces is anything above zero, you assume that that means there's a whole parking space there. So, in turn, you assume that whatever size was originally passed to ParkingLot.new is cleanly divisible by 10. But that's not given.

      End result is that if I make a parking lot with 8 spaces, I can fit 9 vehicles: 1 handicap vehicle (in the 0.8 of a space), and 8 regular ones (the first 7 get a space each, and the last one has to fit in 0.2 of a space).

      Oh, and I can just park a car for zero hours, and I'll pay zero rupees.


    7. Pointless classes

      You parking lot classes don't really serve any purpose being classes. You instantiate one of them, only to call payment and then discard the instance. In the end, you classes could be replaced with methods, or even just an expression: a * b.


    8. Informal exceptions

      Don't just raise a string; create an exception class that inherits from StandardError and raise it instead.



    In other words, there's a lot going on here. Making it "more object-oriented" is a secondary concern. And with no concept of time passing, hourly rates don't really make much difference. It's a little weird that the cost gets calculated immediately when parking, and "paid" when the car's retrieved. A real parking lot would do one or the other: Pre-pay for x amount of time (with the possibility of an extra fee if you overstay), or pay for time used when leaving. This is neither of those.






    share|improve this answer





















    • wow that was nice any other improvements pertaining to the design ie like it violates SRP etc
      – Anony-mouse
      Jun 22 '15 at 6:51










    • "Don't just raise a string; create an exception class that inherits from StandardError and raise it instead." Er, why? I'm sure there's a perfectly valid reason, but could you explain that?
      – Nic Hartley
      Jun 22 '15 at 11:07










    • @QPaysTaxes Rescuing an arbitrary string is messier than rescuing from a named error type. Besides, there might be other, different exceptions you'd want to raise - like if try to park a car with no reg. number or something. Such things could have their own exception classes, making them easier to handle. The docs have a guideline that explains it well (4th paragraph of the intro). Obviously it's not required for an exercise, but best practices and all that.
      – Flambino
      Jun 22 '15 at 12:45










    • @Flambino Oh, I see. Thanks! (Crap, time to rewrite some code...)
      – Nic Hartley
      Jun 22 '15 at 15:01










    • Thank you for the help if there is any design issues please tell me.I am marking it correct.Can you propose some good books to learn design ?
      – Anony-mouse
      Jun 24 '15 at 13:14














    4












    4








    4







    1. Indentation and whitespace

      The Ruby convention is 2 spaces of indentation, and blank lines between methods. I'd also recommend spaces between arguments and operators, e.g. @hour = hour.


    2. Naming

      Don't use underscores in class names. It's clear that Regular_ParkingSpace is a kind of ParkingSpace because it inherit directly from that parent class. A name like RegularSpace would be more straightforward and less of a mouthful.


    3. attr_accessor

      You're adding a number of synthesized accessor methods with attr_accessor but you never use those methods. Instead you access instance variables directly. My advice is to always use accessor methods when you can. However, attr_accessor generates both readers and writers, and they're public. Which means that external code can just say parking_lot.size = 9999, which doesn't make sense. Of course, you never actually use the size attribute for anything, which brings me to my next point:


    4. Junk code

      It sounds harsh, but I just mean "code that doesn't actually do anything". For example the @size variable in ParkingLot which is never used. Or Regular_ParkingSpace having a payment method that just calls super - something that'd happen automatically if the method wasn't there.


    5. Dangerous assumptions

      Speaking of the parking lot's size, your way of determining the number of handicap spaces is not vary robust. You just assume it's going to be one tenth of the spaces. Well, what if the parking lot has 8 spaces in total? Then you have 0.8 of a handicap space, and 7.2 regular ones. Or what if it has 213 spaces? Then you have 21.3 handicap spaces. Neither situation makes any sense. For that matter who says there are any handicap spaces at all? There's no reason - that I know if - to assume there's any proportional relationship between the two numbers.


    6. Outright bugs

      Following from the above: The way you check for remaining spaces assumes integers. If the remaining number of spaces is anything above zero, you assume that that means there's a whole parking space there. So, in turn, you assume that whatever size was originally passed to ParkingLot.new is cleanly divisible by 10. But that's not given.

      End result is that if I make a parking lot with 8 spaces, I can fit 9 vehicles: 1 handicap vehicle (in the 0.8 of a space), and 8 regular ones (the first 7 get a space each, and the last one has to fit in 0.2 of a space).

      Oh, and I can just park a car for zero hours, and I'll pay zero rupees.


    7. Pointless classes

      You parking lot classes don't really serve any purpose being classes. You instantiate one of them, only to call payment and then discard the instance. In the end, you classes could be replaced with methods, or even just an expression: a * b.


    8. Informal exceptions

      Don't just raise a string; create an exception class that inherits from StandardError and raise it instead.



    In other words, there's a lot going on here. Making it "more object-oriented" is a secondary concern. And with no concept of time passing, hourly rates don't really make much difference. It's a little weird that the cost gets calculated immediately when parking, and "paid" when the car's retrieved. A real parking lot would do one or the other: Pre-pay for x amount of time (with the possibility of an extra fee if you overstay), or pay for time used when leaving. This is neither of those.






    share|improve this answer













    1. Indentation and whitespace

      The Ruby convention is 2 spaces of indentation, and blank lines between methods. I'd also recommend spaces between arguments and operators, e.g. @hour = hour.


    2. Naming

      Don't use underscores in class names. It's clear that Regular_ParkingSpace is a kind of ParkingSpace because it inherit directly from that parent class. A name like RegularSpace would be more straightforward and less of a mouthful.


    3. attr_accessor

      You're adding a number of synthesized accessor methods with attr_accessor but you never use those methods. Instead you access instance variables directly. My advice is to always use accessor methods when you can. However, attr_accessor generates both readers and writers, and they're public. Which means that external code can just say parking_lot.size = 9999, which doesn't make sense. Of course, you never actually use the size attribute for anything, which brings me to my next point:


    4. Junk code

      It sounds harsh, but I just mean "code that doesn't actually do anything". For example the @size variable in ParkingLot which is never used. Or Regular_ParkingSpace having a payment method that just calls super - something that'd happen automatically if the method wasn't there.


    5. Dangerous assumptions

      Speaking of the parking lot's size, your way of determining the number of handicap spaces is not vary robust. You just assume it's going to be one tenth of the spaces. Well, what if the parking lot has 8 spaces in total? Then you have 0.8 of a handicap space, and 7.2 regular ones. Or what if it has 213 spaces? Then you have 21.3 handicap spaces. Neither situation makes any sense. For that matter who says there are any handicap spaces at all? There's no reason - that I know if - to assume there's any proportional relationship between the two numbers.


    6. Outright bugs

      Following from the above: The way you check for remaining spaces assumes integers. If the remaining number of spaces is anything above zero, you assume that that means there's a whole parking space there. So, in turn, you assume that whatever size was originally passed to ParkingLot.new is cleanly divisible by 10. But that's not given.

      End result is that if I make a parking lot with 8 spaces, I can fit 9 vehicles: 1 handicap vehicle (in the 0.8 of a space), and 8 regular ones (the first 7 get a space each, and the last one has to fit in 0.2 of a space).

      Oh, and I can just park a car for zero hours, and I'll pay zero rupees.


    7. Pointless classes

      You parking lot classes don't really serve any purpose being classes. You instantiate one of them, only to call payment and then discard the instance. In the end, you classes could be replaced with methods, or even just an expression: a * b.


    8. Informal exceptions

      Don't just raise a string; create an exception class that inherits from StandardError and raise it instead.



    In other words, there's a lot going on here. Making it "more object-oriented" is a secondary concern. And with no concept of time passing, hourly rates don't really make much difference. It's a little weird that the cost gets calculated immediately when parking, and "paid" when the car's retrieved. A real parking lot would do one or the other: Pre-pay for x amount of time (with the possibility of an extra fee if you overstay), or pay for time used when leaving. This is neither of those.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Jun 21 '15 at 23:19









    Flambino

    31.6k23386




    31.6k23386












    • wow that was nice any other improvements pertaining to the design ie like it violates SRP etc
      – Anony-mouse
      Jun 22 '15 at 6:51










    • "Don't just raise a string; create an exception class that inherits from StandardError and raise it instead." Er, why? I'm sure there's a perfectly valid reason, but could you explain that?
      – Nic Hartley
      Jun 22 '15 at 11:07










    • @QPaysTaxes Rescuing an arbitrary string is messier than rescuing from a named error type. Besides, there might be other, different exceptions you'd want to raise - like if try to park a car with no reg. number or something. Such things could have their own exception classes, making them easier to handle. The docs have a guideline that explains it well (4th paragraph of the intro). Obviously it's not required for an exercise, but best practices and all that.
      – Flambino
      Jun 22 '15 at 12:45










    • @Flambino Oh, I see. Thanks! (Crap, time to rewrite some code...)
      – Nic Hartley
      Jun 22 '15 at 15:01










    • Thank you for the help if there is any design issues please tell me.I am marking it correct.Can you propose some good books to learn design ?
      – Anony-mouse
      Jun 24 '15 at 13:14


















    • wow that was nice any other improvements pertaining to the design ie like it violates SRP etc
      – Anony-mouse
      Jun 22 '15 at 6:51










    • "Don't just raise a string; create an exception class that inherits from StandardError and raise it instead." Er, why? I'm sure there's a perfectly valid reason, but could you explain that?
      – Nic Hartley
      Jun 22 '15 at 11:07










    • @QPaysTaxes Rescuing an arbitrary string is messier than rescuing from a named error type. Besides, there might be other, different exceptions you'd want to raise - like if try to park a car with no reg. number or something. Such things could have their own exception classes, making them easier to handle. The docs have a guideline that explains it well (4th paragraph of the intro). Obviously it's not required for an exercise, but best practices and all that.
      – Flambino
      Jun 22 '15 at 12:45










    • @Flambino Oh, I see. Thanks! (Crap, time to rewrite some code...)
      – Nic Hartley
      Jun 22 '15 at 15:01










    • Thank you for the help if there is any design issues please tell me.I am marking it correct.Can you propose some good books to learn design ?
      – Anony-mouse
      Jun 24 '15 at 13:14
















    wow that was nice any other improvements pertaining to the design ie like it violates SRP etc
    – Anony-mouse
    Jun 22 '15 at 6:51




    wow that was nice any other improvements pertaining to the design ie like it violates SRP etc
    – Anony-mouse
    Jun 22 '15 at 6:51












    "Don't just raise a string; create an exception class that inherits from StandardError and raise it instead." Er, why? I'm sure there's a perfectly valid reason, but could you explain that?
    – Nic Hartley
    Jun 22 '15 at 11:07




    "Don't just raise a string; create an exception class that inherits from StandardError and raise it instead." Er, why? I'm sure there's a perfectly valid reason, but could you explain that?
    – Nic Hartley
    Jun 22 '15 at 11:07












    @QPaysTaxes Rescuing an arbitrary string is messier than rescuing from a named error type. Besides, there might be other, different exceptions you'd want to raise - like if try to park a car with no reg. number or something. Such things could have their own exception classes, making them easier to handle. The docs have a guideline that explains it well (4th paragraph of the intro). Obviously it's not required for an exercise, but best practices and all that.
    – Flambino
    Jun 22 '15 at 12:45




    @QPaysTaxes Rescuing an arbitrary string is messier than rescuing from a named error type. Besides, there might be other, different exceptions you'd want to raise - like if try to park a car with no reg. number or something. Such things could have their own exception classes, making them easier to handle. The docs have a guideline that explains it well (4th paragraph of the intro). Obviously it's not required for an exercise, but best practices and all that.
    – Flambino
    Jun 22 '15 at 12:45












    @Flambino Oh, I see. Thanks! (Crap, time to rewrite some code...)
    – Nic Hartley
    Jun 22 '15 at 15:01




    @Flambino Oh, I see. Thanks! (Crap, time to rewrite some code...)
    – Nic Hartley
    Jun 22 '15 at 15:01












    Thank you for the help if there is any design issues please tell me.I am marking it correct.Can you propose some good books to learn design ?
    – Anony-mouse
    Jun 24 '15 at 13:14




    Thank you for the help if there is any design issues please tell me.I am marking it correct.Can you propose some good books to learn design ?
    – Anony-mouse
    Jun 24 '15 at 13:14











    0














    if someone is interested in Greg Burghardt's code in Laravel 5.5 in PHP.



    /// ParkingFee.php
    namespace AppK11Test;


    class ParkingFee {
    const UNRESTRICTED = 20;
    const HANDICAPPED = 5;
    private $rate;

    public function __construct($rate) {
    $this->rate = $rate;
    }

    public function calculate($hours) {
    return $this->rate * $hours;
    }
    }



    // VehiclePermit.php
    namespace AppK11Test;


    class VehiclePermit {
    const HANDICAPPED = 1;
    const CELEBRITY = 2;
    }

    // HandicappedParkingSpace.php
    namespace AppK11Test;


    class HandicappedParkingSpace extends ParkingSpace {
    public function __construct($parkingLot, $fee, $number) {
    parent::__construct($parkingLot, ParkingFee::HANDICAPPED, $number);
    }

    public function canPark(Vehicle $vehicle) {
    return parent::canPark($vehicle) && $vehicle->isHandicapped();
    }
    }


    // UnrestrictedParkingSpace.php
    namespace AppK11Test;


    class UnrestrictedParkingSpace extends ParkingSpace {
    public function __construct($parkingLot, $fee, $number) {
    return parent::__construct($parkingLot, ParkingFee::UNRESTRICTED, $number);
    }
    }


    // Vehicle.php
    namespace AppK11Test;

    class Vehicle {
    private $registrationNumber;
    public $permits;

    public function __construct($registrationNumber) {
    $this->registrationNumber = $registrationNumber;
    }

    public function isHandicapped() {
    if(!is_array($this->permits)) {
    return false;
    }

    return in_array(VehiclePermit::HANDICAPPED, $this->permits);
    }

    /**
    * @return mixed
    */
    public function getRegistrationNumber() {
    return $this->registrationNumber;
    }
    }

    // ParkingSpacePayment.php
    namespace AppK11Test;

    use CarbonCarbon;

    class ParkingSpacePayment {
    private $parkingSpace;
    private $paymentDate;
    private $totalHours;
    private $amountDue;
    private $amountPaid;

    public function __construct(ParkingSpace $parkingSpace) {
    $this->parkingSpace = $parkingSpace;
    $this->paymentDate = now();
    $this->totalHours = Carbon::now()->diffInHours(Carbon::createFromTimestamp( $this->parkingSpace->dateOccupied) );
    $this->amountDue = $parkingSpace->calculateFee($this->totalHours);
    $this->amountPaid = 0;
    }

    /**
    * @return int
    */
    public function getAmountDue(): int {
    return $this->amountDue;
    }

    public function balance() {
    return $this->amountDue - $this->amountPaid;
    }

    public function isPaid() {
    return self::balance() <= 0;
    }

    public function pay($amount) {
    $this->amountPaid += $amount;
    }


    }


    // ParkingSpace.php
    namespace AppK11Test;

    use MockeryException;

    /**
    class ParkingSpace {
    private $parkingLot;
    private $fee;
    public $number;
    private $vehicle = null;
    public $dateOccupied;

    public function __construct($parkingLot, $fee, $number) {
    $this->parkingLot = $parkingLot;
    $this->fee = $fee;
    $this->number = $number;
    $this->vehicle = null;
    }

    public function payment() {
    return new ParkingSpacePayment($this);
    }

    public function canPark(Vehicle $vehicle) {
    return !self::isOccupied();
    }

    public function contain(Vehicle $vehicle) {
    return $this->vehicle == $vehicle;
    }

    public function vehicle(Vehicle $vehicle) {
    $this->vehicle = $vehicle;
    }

    /**
    * @return null
    */
    public function getVehicle() {
    return $this->vehicle;
    }

    /**
    * @param null $vehicle
    */
    public function setVehicle($vehicle) {
    $this->vehicle = $vehicle;
    }

    /**
    * @param mixed $number
    */
    public function setNumber($number) {
    $this->number = $number;
    }

    /**
    * @return mixed
    */
    public function getNumber() {
    return $this->number;
    }

    public function isOccupied() {
    return $this->vehicle !== null;
    }

    public function park(Vehicle $vehicle) {
    if (!self::canPark($vehicle)) {
    throw new Exception('Cannot park vehicle. ' . $vehicle->getRegistrationNumber());
    }

    $this->vehicle = $vehicle;

    $this->dateOccupied = strtotime("-7 hours");

    Log::info($this->dateOccupied);
    }

    public function vacate() {
    $this->vehicle = null;
    $this->dateOccupied = null;
    }

    public function calculateFee($hours) {
    $parkingFee = new ParkingFee($this->fee);

    return $parkingFee->calculate($hours);
    }


    }

    // ParkingLot.php
    namespace AppK11Test;

    use MockeryException;

    class ParkingLot {
    private $parkingSpaces;

    public function __construct($unrestrictedCount, $handicappedCount) {
    $this->parkingSpaces = collect();

    for ($i = 0; $i < $unrestrictedCount; $i++) {
    $this->parkingSpaces->push(new UnrestrictedParkingSpace($this, ParkingFee::UNRESTRICTED, $i + 1));
    }

    for ($j = 1; $j <= $handicappedCount; $j++) {
    $this->parkingSpaces->push(new HandicappedParkingSpace($this, ParkingFee::HANDICAPPED, $i + $j));
    }
    }

    public function details() {
    $this->parkingSpaces->each(function ($parkingSpace) {

    $vNum = 0;

    if ($vehicle = $parkingSpace->getVehicle()) {
    $vNum = $vehicle->getRegistrationNumber();
    }

    Log::info('number:' . $parkingSpace->number . ', Vehicle: ' . get_class($parkingSpace) . 'Vehicle # ' . $vNum);
    });
    }

    public function exit(Vehicle $vehicle, ParkingSpacePayment $parkingSpacePayment): ParkingSpace {
    if (!$parkingSpacePayment->isPaid()) {
    throw new Exception('Balance not paid: ' . $parkingSpacePayment->balance());
    }

    $parkingSpace = self::getParkingSpace($vehicle);
    $parkingSpace->vacate();

    return $parkingSpace;
    }

    public function park(Vehicle $vehicle): ParkingSpace {
    $freeParkingSpace = self::getFreeParkingSpace($vehicle);
    $freeParkingSpace->park($vehicle);

    return $freeParkingSpace;
    }

    private function getFreeParkingSpace(Vehicle $vehicle): ParkingSpace {
    $parkingSpaces = $this->parkingSpaces;

    if ($vehicle->isHandicapped()) {
    $parkingSpaces = $this->parkingSpaces->filter(function (ParkingSpace $parkingSpace) use ($vehicle) {
    if (class_basename($parkingSpace) === 'HandicappedParkingSpace') {
    return true;
    }

    return false;
    });
    }

    $parkingSpace = $parkingSpaces->filter(function (ParkingSpace $parkingSpace) use ($vehicle) {

    return $parkingSpace->canPark($vehicle);
    })->first();

    return $parkingSpace;
    }

    public function getPayment(Vehicle $vehicle) {
    return self::getParkingSpace($vehicle)->payment();
    }

    private function getParkingSpace(Vehicle $vehicle): ParkingSpace {
    $currentParkingSpace = null;

    foreach ($this->parkingSpaces as $parkingSpace) {
    if ($parkingSpace->contain($vehicle)) {
    $currentParkingSpace = $parkingSpace;

    break;
    }
    }

    if (is_null($currentParkingSpace)) {
    throw new Exception('Vehicle is not parked in this lot. ' . $vehicle->getRegistrationNumber());
    }

    return $currentParkingSpace;
    }
    }


    // index.php
    namespace AppK11Test;

    use MockeryException;

    try {
    // Our parking lot as 10 unrestricted spaces and 5 handicapped spaces
    $parkingLot = new ParkingLot(10, 5);

    // Create two vehicles, one of each type.
    $uVehicle = new Vehicle(2255);
    $parkingLot->park($uVehicle);

    $parkingLot->details();
    $uVehiclePayment = $parkingLot->getPayment($uVehicle);
    //echo '$uVehiclePayment->getAmountDue(): ' . $uVehiclePayment->getAmountDue();
    $uVehiclePayment->pay($uVehiclePayment->getAmountDue());
    $uSpace = $parkingLot->exit($uVehicle, $uVehiclePayment);

    $parkingLot->details();

    if ($uSpace->isOccupied()) {
    echo('Still occupied!');
    } else {
    echo('Vacant slot: ' . $uSpace->getNumber());
    }

    $hVehicle = new Vehicle(2);
    $hVehicle->permits = VehiclePermit::HANDICAPPED;
    $parkingLot->park($hVehicle);
    $parkingLot->details();
    $hVehiclePayment = $parkingLot->getPayment($hVehicle);
    $hVehiclePayment->pay(15);

    $hSpace = $parkingLot->exit($hVehicle, $hVehiclePayment);

    if ($hSpace->isOccupied()) {
    echo('Still occupied!');
    } else {
    echo("n".'Vacant slot: ' . $hSpace->getNumber());
    }
    } catch (Exception $e) {
    echo $e->getMessage();
    }





    share|improve this answer








    New contributor




    San is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.























      0














      if someone is interested in Greg Burghardt's code in Laravel 5.5 in PHP.



      /// ParkingFee.php
      namespace AppK11Test;


      class ParkingFee {
      const UNRESTRICTED = 20;
      const HANDICAPPED = 5;
      private $rate;

      public function __construct($rate) {
      $this->rate = $rate;
      }

      public function calculate($hours) {
      return $this->rate * $hours;
      }
      }



      // VehiclePermit.php
      namespace AppK11Test;


      class VehiclePermit {
      const HANDICAPPED = 1;
      const CELEBRITY = 2;
      }

      // HandicappedParkingSpace.php
      namespace AppK11Test;


      class HandicappedParkingSpace extends ParkingSpace {
      public function __construct($parkingLot, $fee, $number) {
      parent::__construct($parkingLot, ParkingFee::HANDICAPPED, $number);
      }

      public function canPark(Vehicle $vehicle) {
      return parent::canPark($vehicle) && $vehicle->isHandicapped();
      }
      }


      // UnrestrictedParkingSpace.php
      namespace AppK11Test;


      class UnrestrictedParkingSpace extends ParkingSpace {
      public function __construct($parkingLot, $fee, $number) {
      return parent::__construct($parkingLot, ParkingFee::UNRESTRICTED, $number);
      }
      }


      // Vehicle.php
      namespace AppK11Test;

      class Vehicle {
      private $registrationNumber;
      public $permits;

      public function __construct($registrationNumber) {
      $this->registrationNumber = $registrationNumber;
      }

      public function isHandicapped() {
      if(!is_array($this->permits)) {
      return false;
      }

      return in_array(VehiclePermit::HANDICAPPED, $this->permits);
      }

      /**
      * @return mixed
      */
      public function getRegistrationNumber() {
      return $this->registrationNumber;
      }
      }

      // ParkingSpacePayment.php
      namespace AppK11Test;

      use CarbonCarbon;

      class ParkingSpacePayment {
      private $parkingSpace;
      private $paymentDate;
      private $totalHours;
      private $amountDue;
      private $amountPaid;

      public function __construct(ParkingSpace $parkingSpace) {
      $this->parkingSpace = $parkingSpace;
      $this->paymentDate = now();
      $this->totalHours = Carbon::now()->diffInHours(Carbon::createFromTimestamp( $this->parkingSpace->dateOccupied) );
      $this->amountDue = $parkingSpace->calculateFee($this->totalHours);
      $this->amountPaid = 0;
      }

      /**
      * @return int
      */
      public function getAmountDue(): int {
      return $this->amountDue;
      }

      public function balance() {
      return $this->amountDue - $this->amountPaid;
      }

      public function isPaid() {
      return self::balance() <= 0;
      }

      public function pay($amount) {
      $this->amountPaid += $amount;
      }


      }


      // ParkingSpace.php
      namespace AppK11Test;

      use MockeryException;

      /**
      class ParkingSpace {
      private $parkingLot;
      private $fee;
      public $number;
      private $vehicle = null;
      public $dateOccupied;

      public function __construct($parkingLot, $fee, $number) {
      $this->parkingLot = $parkingLot;
      $this->fee = $fee;
      $this->number = $number;
      $this->vehicle = null;
      }

      public function payment() {
      return new ParkingSpacePayment($this);
      }

      public function canPark(Vehicle $vehicle) {
      return !self::isOccupied();
      }

      public function contain(Vehicle $vehicle) {
      return $this->vehicle == $vehicle;
      }

      public function vehicle(Vehicle $vehicle) {
      $this->vehicle = $vehicle;
      }

      /**
      * @return null
      */
      public function getVehicle() {
      return $this->vehicle;
      }

      /**
      * @param null $vehicle
      */
      public function setVehicle($vehicle) {
      $this->vehicle = $vehicle;
      }

      /**
      * @param mixed $number
      */
      public function setNumber($number) {
      $this->number = $number;
      }

      /**
      * @return mixed
      */
      public function getNumber() {
      return $this->number;
      }

      public function isOccupied() {
      return $this->vehicle !== null;
      }

      public function park(Vehicle $vehicle) {
      if (!self::canPark($vehicle)) {
      throw new Exception('Cannot park vehicle. ' . $vehicle->getRegistrationNumber());
      }

      $this->vehicle = $vehicle;

      $this->dateOccupied = strtotime("-7 hours");

      Log::info($this->dateOccupied);
      }

      public function vacate() {
      $this->vehicle = null;
      $this->dateOccupied = null;
      }

      public function calculateFee($hours) {
      $parkingFee = new ParkingFee($this->fee);

      return $parkingFee->calculate($hours);
      }


      }

      // ParkingLot.php
      namespace AppK11Test;

      use MockeryException;

      class ParkingLot {
      private $parkingSpaces;

      public function __construct($unrestrictedCount, $handicappedCount) {
      $this->parkingSpaces = collect();

      for ($i = 0; $i < $unrestrictedCount; $i++) {
      $this->parkingSpaces->push(new UnrestrictedParkingSpace($this, ParkingFee::UNRESTRICTED, $i + 1));
      }

      for ($j = 1; $j <= $handicappedCount; $j++) {
      $this->parkingSpaces->push(new HandicappedParkingSpace($this, ParkingFee::HANDICAPPED, $i + $j));
      }
      }

      public function details() {
      $this->parkingSpaces->each(function ($parkingSpace) {

      $vNum = 0;

      if ($vehicle = $parkingSpace->getVehicle()) {
      $vNum = $vehicle->getRegistrationNumber();
      }

      Log::info('number:' . $parkingSpace->number . ', Vehicle: ' . get_class($parkingSpace) . 'Vehicle # ' . $vNum);
      });
      }

      public function exit(Vehicle $vehicle, ParkingSpacePayment $parkingSpacePayment): ParkingSpace {
      if (!$parkingSpacePayment->isPaid()) {
      throw new Exception('Balance not paid: ' . $parkingSpacePayment->balance());
      }

      $parkingSpace = self::getParkingSpace($vehicle);
      $parkingSpace->vacate();

      return $parkingSpace;
      }

      public function park(Vehicle $vehicle): ParkingSpace {
      $freeParkingSpace = self::getFreeParkingSpace($vehicle);
      $freeParkingSpace->park($vehicle);

      return $freeParkingSpace;
      }

      private function getFreeParkingSpace(Vehicle $vehicle): ParkingSpace {
      $parkingSpaces = $this->parkingSpaces;

      if ($vehicle->isHandicapped()) {
      $parkingSpaces = $this->parkingSpaces->filter(function (ParkingSpace $parkingSpace) use ($vehicle) {
      if (class_basename($parkingSpace) === 'HandicappedParkingSpace') {
      return true;
      }

      return false;
      });
      }

      $parkingSpace = $parkingSpaces->filter(function (ParkingSpace $parkingSpace) use ($vehicle) {

      return $parkingSpace->canPark($vehicle);
      })->first();

      return $parkingSpace;
      }

      public function getPayment(Vehicle $vehicle) {
      return self::getParkingSpace($vehicle)->payment();
      }

      private function getParkingSpace(Vehicle $vehicle): ParkingSpace {
      $currentParkingSpace = null;

      foreach ($this->parkingSpaces as $parkingSpace) {
      if ($parkingSpace->contain($vehicle)) {
      $currentParkingSpace = $parkingSpace;

      break;
      }
      }

      if (is_null($currentParkingSpace)) {
      throw new Exception('Vehicle is not parked in this lot. ' . $vehicle->getRegistrationNumber());
      }

      return $currentParkingSpace;
      }
      }


      // index.php
      namespace AppK11Test;

      use MockeryException;

      try {
      // Our parking lot as 10 unrestricted spaces and 5 handicapped spaces
      $parkingLot = new ParkingLot(10, 5);

      // Create two vehicles, one of each type.
      $uVehicle = new Vehicle(2255);
      $parkingLot->park($uVehicle);

      $parkingLot->details();
      $uVehiclePayment = $parkingLot->getPayment($uVehicle);
      //echo '$uVehiclePayment->getAmountDue(): ' . $uVehiclePayment->getAmountDue();
      $uVehiclePayment->pay($uVehiclePayment->getAmountDue());
      $uSpace = $parkingLot->exit($uVehicle, $uVehiclePayment);

      $parkingLot->details();

      if ($uSpace->isOccupied()) {
      echo('Still occupied!');
      } else {
      echo('Vacant slot: ' . $uSpace->getNumber());
      }

      $hVehicle = new Vehicle(2);
      $hVehicle->permits = VehiclePermit::HANDICAPPED;
      $parkingLot->park($hVehicle);
      $parkingLot->details();
      $hVehiclePayment = $parkingLot->getPayment($hVehicle);
      $hVehiclePayment->pay(15);

      $hSpace = $parkingLot->exit($hVehicle, $hVehiclePayment);

      if ($hSpace->isOccupied()) {
      echo('Still occupied!');
      } else {
      echo("n".'Vacant slot: ' . $hSpace->getNumber());
      }
      } catch (Exception $e) {
      echo $e->getMessage();
      }





      share|improve this answer








      New contributor




      San is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.





















        0












        0








        0






        if someone is interested in Greg Burghardt's code in Laravel 5.5 in PHP.



        /// ParkingFee.php
        namespace AppK11Test;


        class ParkingFee {
        const UNRESTRICTED = 20;
        const HANDICAPPED = 5;
        private $rate;

        public function __construct($rate) {
        $this->rate = $rate;
        }

        public function calculate($hours) {
        return $this->rate * $hours;
        }
        }



        // VehiclePermit.php
        namespace AppK11Test;


        class VehiclePermit {
        const HANDICAPPED = 1;
        const CELEBRITY = 2;
        }

        // HandicappedParkingSpace.php
        namespace AppK11Test;


        class HandicappedParkingSpace extends ParkingSpace {
        public function __construct($parkingLot, $fee, $number) {
        parent::__construct($parkingLot, ParkingFee::HANDICAPPED, $number);
        }

        public function canPark(Vehicle $vehicle) {
        return parent::canPark($vehicle) && $vehicle->isHandicapped();
        }
        }


        // UnrestrictedParkingSpace.php
        namespace AppK11Test;


        class UnrestrictedParkingSpace extends ParkingSpace {
        public function __construct($parkingLot, $fee, $number) {
        return parent::__construct($parkingLot, ParkingFee::UNRESTRICTED, $number);
        }
        }


        // Vehicle.php
        namespace AppK11Test;

        class Vehicle {
        private $registrationNumber;
        public $permits;

        public function __construct($registrationNumber) {
        $this->registrationNumber = $registrationNumber;
        }

        public function isHandicapped() {
        if(!is_array($this->permits)) {
        return false;
        }

        return in_array(VehiclePermit::HANDICAPPED, $this->permits);
        }

        /**
        * @return mixed
        */
        public function getRegistrationNumber() {
        return $this->registrationNumber;
        }
        }

        // ParkingSpacePayment.php
        namespace AppK11Test;

        use CarbonCarbon;

        class ParkingSpacePayment {
        private $parkingSpace;
        private $paymentDate;
        private $totalHours;
        private $amountDue;
        private $amountPaid;

        public function __construct(ParkingSpace $parkingSpace) {
        $this->parkingSpace = $parkingSpace;
        $this->paymentDate = now();
        $this->totalHours = Carbon::now()->diffInHours(Carbon::createFromTimestamp( $this->parkingSpace->dateOccupied) );
        $this->amountDue = $parkingSpace->calculateFee($this->totalHours);
        $this->amountPaid = 0;
        }

        /**
        * @return int
        */
        public function getAmountDue(): int {
        return $this->amountDue;
        }

        public function balance() {
        return $this->amountDue - $this->amountPaid;
        }

        public function isPaid() {
        return self::balance() <= 0;
        }

        public function pay($amount) {
        $this->amountPaid += $amount;
        }


        }


        // ParkingSpace.php
        namespace AppK11Test;

        use MockeryException;

        /**
        class ParkingSpace {
        private $parkingLot;
        private $fee;
        public $number;
        private $vehicle = null;
        public $dateOccupied;

        public function __construct($parkingLot, $fee, $number) {
        $this->parkingLot = $parkingLot;
        $this->fee = $fee;
        $this->number = $number;
        $this->vehicle = null;
        }

        public function payment() {
        return new ParkingSpacePayment($this);
        }

        public function canPark(Vehicle $vehicle) {
        return !self::isOccupied();
        }

        public function contain(Vehicle $vehicle) {
        return $this->vehicle == $vehicle;
        }

        public function vehicle(Vehicle $vehicle) {
        $this->vehicle = $vehicle;
        }

        /**
        * @return null
        */
        public function getVehicle() {
        return $this->vehicle;
        }

        /**
        * @param null $vehicle
        */
        public function setVehicle($vehicle) {
        $this->vehicle = $vehicle;
        }

        /**
        * @param mixed $number
        */
        public function setNumber($number) {
        $this->number = $number;
        }

        /**
        * @return mixed
        */
        public function getNumber() {
        return $this->number;
        }

        public function isOccupied() {
        return $this->vehicle !== null;
        }

        public function park(Vehicle $vehicle) {
        if (!self::canPark($vehicle)) {
        throw new Exception('Cannot park vehicle. ' . $vehicle->getRegistrationNumber());
        }

        $this->vehicle = $vehicle;

        $this->dateOccupied = strtotime("-7 hours");

        Log::info($this->dateOccupied);
        }

        public function vacate() {
        $this->vehicle = null;
        $this->dateOccupied = null;
        }

        public function calculateFee($hours) {
        $parkingFee = new ParkingFee($this->fee);

        return $parkingFee->calculate($hours);
        }


        }

        // ParkingLot.php
        namespace AppK11Test;

        use MockeryException;

        class ParkingLot {
        private $parkingSpaces;

        public function __construct($unrestrictedCount, $handicappedCount) {
        $this->parkingSpaces = collect();

        for ($i = 0; $i < $unrestrictedCount; $i++) {
        $this->parkingSpaces->push(new UnrestrictedParkingSpace($this, ParkingFee::UNRESTRICTED, $i + 1));
        }

        for ($j = 1; $j <= $handicappedCount; $j++) {
        $this->parkingSpaces->push(new HandicappedParkingSpace($this, ParkingFee::HANDICAPPED, $i + $j));
        }
        }

        public function details() {
        $this->parkingSpaces->each(function ($parkingSpace) {

        $vNum = 0;

        if ($vehicle = $parkingSpace->getVehicle()) {
        $vNum = $vehicle->getRegistrationNumber();
        }

        Log::info('number:' . $parkingSpace->number . ', Vehicle: ' . get_class($parkingSpace) . 'Vehicle # ' . $vNum);
        });
        }

        public function exit(Vehicle $vehicle, ParkingSpacePayment $parkingSpacePayment): ParkingSpace {
        if (!$parkingSpacePayment->isPaid()) {
        throw new Exception('Balance not paid: ' . $parkingSpacePayment->balance());
        }

        $parkingSpace = self::getParkingSpace($vehicle);
        $parkingSpace->vacate();

        return $parkingSpace;
        }

        public function park(Vehicle $vehicle): ParkingSpace {
        $freeParkingSpace = self::getFreeParkingSpace($vehicle);
        $freeParkingSpace->park($vehicle);

        return $freeParkingSpace;
        }

        private function getFreeParkingSpace(Vehicle $vehicle): ParkingSpace {
        $parkingSpaces = $this->parkingSpaces;

        if ($vehicle->isHandicapped()) {
        $parkingSpaces = $this->parkingSpaces->filter(function (ParkingSpace $parkingSpace) use ($vehicle) {
        if (class_basename($parkingSpace) === 'HandicappedParkingSpace') {
        return true;
        }

        return false;
        });
        }

        $parkingSpace = $parkingSpaces->filter(function (ParkingSpace $parkingSpace) use ($vehicle) {

        return $parkingSpace->canPark($vehicle);
        })->first();

        return $parkingSpace;
        }

        public function getPayment(Vehicle $vehicle) {
        return self::getParkingSpace($vehicle)->payment();
        }

        private function getParkingSpace(Vehicle $vehicle): ParkingSpace {
        $currentParkingSpace = null;

        foreach ($this->parkingSpaces as $parkingSpace) {
        if ($parkingSpace->contain($vehicle)) {
        $currentParkingSpace = $parkingSpace;

        break;
        }
        }

        if (is_null($currentParkingSpace)) {
        throw new Exception('Vehicle is not parked in this lot. ' . $vehicle->getRegistrationNumber());
        }

        return $currentParkingSpace;
        }
        }


        // index.php
        namespace AppK11Test;

        use MockeryException;

        try {
        // Our parking lot as 10 unrestricted spaces and 5 handicapped spaces
        $parkingLot = new ParkingLot(10, 5);

        // Create two vehicles, one of each type.
        $uVehicle = new Vehicle(2255);
        $parkingLot->park($uVehicle);

        $parkingLot->details();
        $uVehiclePayment = $parkingLot->getPayment($uVehicle);
        //echo '$uVehiclePayment->getAmountDue(): ' . $uVehiclePayment->getAmountDue();
        $uVehiclePayment->pay($uVehiclePayment->getAmountDue());
        $uSpace = $parkingLot->exit($uVehicle, $uVehiclePayment);

        $parkingLot->details();

        if ($uSpace->isOccupied()) {
        echo('Still occupied!');
        } else {
        echo('Vacant slot: ' . $uSpace->getNumber());
        }

        $hVehicle = new Vehicle(2);
        $hVehicle->permits = VehiclePermit::HANDICAPPED;
        $parkingLot->park($hVehicle);
        $parkingLot->details();
        $hVehiclePayment = $parkingLot->getPayment($hVehicle);
        $hVehiclePayment->pay(15);

        $hSpace = $parkingLot->exit($hVehicle, $hVehiclePayment);

        if ($hSpace->isOccupied()) {
        echo('Still occupied!');
        } else {
        echo("n".'Vacant slot: ' . $hSpace->getNumber());
        }
        } catch (Exception $e) {
        echo $e->getMessage();
        }





        share|improve this answer








        New contributor




        San is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.









        if someone is interested in Greg Burghardt's code in Laravel 5.5 in PHP.



        /// ParkingFee.php
        namespace AppK11Test;


        class ParkingFee {
        const UNRESTRICTED = 20;
        const HANDICAPPED = 5;
        private $rate;

        public function __construct($rate) {
        $this->rate = $rate;
        }

        public function calculate($hours) {
        return $this->rate * $hours;
        }
        }



        // VehiclePermit.php
        namespace AppK11Test;


        class VehiclePermit {
        const HANDICAPPED = 1;
        const CELEBRITY = 2;
        }

        // HandicappedParkingSpace.php
        namespace AppK11Test;


        class HandicappedParkingSpace extends ParkingSpace {
        public function __construct($parkingLot, $fee, $number) {
        parent::__construct($parkingLot, ParkingFee::HANDICAPPED, $number);
        }

        public function canPark(Vehicle $vehicle) {
        return parent::canPark($vehicle) && $vehicle->isHandicapped();
        }
        }


        // UnrestrictedParkingSpace.php
        namespace AppK11Test;


        class UnrestrictedParkingSpace extends ParkingSpace {
        public function __construct($parkingLot, $fee, $number) {
        return parent::__construct($parkingLot, ParkingFee::UNRESTRICTED, $number);
        }
        }


        // Vehicle.php
        namespace AppK11Test;

        class Vehicle {
        private $registrationNumber;
        public $permits;

        public function __construct($registrationNumber) {
        $this->registrationNumber = $registrationNumber;
        }

        public function isHandicapped() {
        if(!is_array($this->permits)) {
        return false;
        }

        return in_array(VehiclePermit::HANDICAPPED, $this->permits);
        }

        /**
        * @return mixed
        */
        public function getRegistrationNumber() {
        return $this->registrationNumber;
        }
        }

        // ParkingSpacePayment.php
        namespace AppK11Test;

        use CarbonCarbon;

        class ParkingSpacePayment {
        private $parkingSpace;
        private $paymentDate;
        private $totalHours;
        private $amountDue;
        private $amountPaid;

        public function __construct(ParkingSpace $parkingSpace) {
        $this->parkingSpace = $parkingSpace;
        $this->paymentDate = now();
        $this->totalHours = Carbon::now()->diffInHours(Carbon::createFromTimestamp( $this->parkingSpace->dateOccupied) );
        $this->amountDue = $parkingSpace->calculateFee($this->totalHours);
        $this->amountPaid = 0;
        }

        /**
        * @return int
        */
        public function getAmountDue(): int {
        return $this->amountDue;
        }

        public function balance() {
        return $this->amountDue - $this->amountPaid;
        }

        public function isPaid() {
        return self::balance() <= 0;
        }

        public function pay($amount) {
        $this->amountPaid += $amount;
        }


        }


        // ParkingSpace.php
        namespace AppK11Test;

        use MockeryException;

        /**
        class ParkingSpace {
        private $parkingLot;
        private $fee;
        public $number;
        private $vehicle = null;
        public $dateOccupied;

        public function __construct($parkingLot, $fee, $number) {
        $this->parkingLot = $parkingLot;
        $this->fee = $fee;
        $this->number = $number;
        $this->vehicle = null;
        }

        public function payment() {
        return new ParkingSpacePayment($this);
        }

        public function canPark(Vehicle $vehicle) {
        return !self::isOccupied();
        }

        public function contain(Vehicle $vehicle) {
        return $this->vehicle == $vehicle;
        }

        public function vehicle(Vehicle $vehicle) {
        $this->vehicle = $vehicle;
        }

        /**
        * @return null
        */
        public function getVehicle() {
        return $this->vehicle;
        }

        /**
        * @param null $vehicle
        */
        public function setVehicle($vehicle) {
        $this->vehicle = $vehicle;
        }

        /**
        * @param mixed $number
        */
        public function setNumber($number) {
        $this->number = $number;
        }

        /**
        * @return mixed
        */
        public function getNumber() {
        return $this->number;
        }

        public function isOccupied() {
        return $this->vehicle !== null;
        }

        public function park(Vehicle $vehicle) {
        if (!self::canPark($vehicle)) {
        throw new Exception('Cannot park vehicle. ' . $vehicle->getRegistrationNumber());
        }

        $this->vehicle = $vehicle;

        $this->dateOccupied = strtotime("-7 hours");

        Log::info($this->dateOccupied);
        }

        public function vacate() {
        $this->vehicle = null;
        $this->dateOccupied = null;
        }

        public function calculateFee($hours) {
        $parkingFee = new ParkingFee($this->fee);

        return $parkingFee->calculate($hours);
        }


        }

        // ParkingLot.php
        namespace AppK11Test;

        use MockeryException;

        class ParkingLot {
        private $parkingSpaces;

        public function __construct($unrestrictedCount, $handicappedCount) {
        $this->parkingSpaces = collect();

        for ($i = 0; $i < $unrestrictedCount; $i++) {
        $this->parkingSpaces->push(new UnrestrictedParkingSpace($this, ParkingFee::UNRESTRICTED, $i + 1));
        }

        for ($j = 1; $j <= $handicappedCount; $j++) {
        $this->parkingSpaces->push(new HandicappedParkingSpace($this, ParkingFee::HANDICAPPED, $i + $j));
        }
        }

        public function details() {
        $this->parkingSpaces->each(function ($parkingSpace) {

        $vNum = 0;

        if ($vehicle = $parkingSpace->getVehicle()) {
        $vNum = $vehicle->getRegistrationNumber();
        }

        Log::info('number:' . $parkingSpace->number . ', Vehicle: ' . get_class($parkingSpace) . 'Vehicle # ' . $vNum);
        });
        }

        public function exit(Vehicle $vehicle, ParkingSpacePayment $parkingSpacePayment): ParkingSpace {
        if (!$parkingSpacePayment->isPaid()) {
        throw new Exception('Balance not paid: ' . $parkingSpacePayment->balance());
        }

        $parkingSpace = self::getParkingSpace($vehicle);
        $parkingSpace->vacate();

        return $parkingSpace;
        }

        public function park(Vehicle $vehicle): ParkingSpace {
        $freeParkingSpace = self::getFreeParkingSpace($vehicle);
        $freeParkingSpace->park($vehicle);

        return $freeParkingSpace;
        }

        private function getFreeParkingSpace(Vehicle $vehicle): ParkingSpace {
        $parkingSpaces = $this->parkingSpaces;

        if ($vehicle->isHandicapped()) {
        $parkingSpaces = $this->parkingSpaces->filter(function (ParkingSpace $parkingSpace) use ($vehicle) {
        if (class_basename($parkingSpace) === 'HandicappedParkingSpace') {
        return true;
        }

        return false;
        });
        }

        $parkingSpace = $parkingSpaces->filter(function (ParkingSpace $parkingSpace) use ($vehicle) {

        return $parkingSpace->canPark($vehicle);
        })->first();

        return $parkingSpace;
        }

        public function getPayment(Vehicle $vehicle) {
        return self::getParkingSpace($vehicle)->payment();
        }

        private function getParkingSpace(Vehicle $vehicle): ParkingSpace {
        $currentParkingSpace = null;

        foreach ($this->parkingSpaces as $parkingSpace) {
        if ($parkingSpace->contain($vehicle)) {
        $currentParkingSpace = $parkingSpace;

        break;
        }
        }

        if (is_null($currentParkingSpace)) {
        throw new Exception('Vehicle is not parked in this lot. ' . $vehicle->getRegistrationNumber());
        }

        return $currentParkingSpace;
        }
        }


        // index.php
        namespace AppK11Test;

        use MockeryException;

        try {
        // Our parking lot as 10 unrestricted spaces and 5 handicapped spaces
        $parkingLot = new ParkingLot(10, 5);

        // Create two vehicles, one of each type.
        $uVehicle = new Vehicle(2255);
        $parkingLot->park($uVehicle);

        $parkingLot->details();
        $uVehiclePayment = $parkingLot->getPayment($uVehicle);
        //echo '$uVehiclePayment->getAmountDue(): ' . $uVehiclePayment->getAmountDue();
        $uVehiclePayment->pay($uVehiclePayment->getAmountDue());
        $uSpace = $parkingLot->exit($uVehicle, $uVehiclePayment);

        $parkingLot->details();

        if ($uSpace->isOccupied()) {
        echo('Still occupied!');
        } else {
        echo('Vacant slot: ' . $uSpace->getNumber());
        }

        $hVehicle = new Vehicle(2);
        $hVehicle->permits = VehiclePermit::HANDICAPPED;
        $parkingLot->park($hVehicle);
        $parkingLot->details();
        $hVehiclePayment = $parkingLot->getPayment($hVehicle);
        $hVehiclePayment->pay(15);

        $hSpace = $parkingLot->exit($hVehicle, $hVehiclePayment);

        if ($hSpace->isOccupied()) {
        echo('Still occupied!');
        } else {
        echo("n".'Vacant slot: ' . $hSpace->getNumber());
        }
        } catch (Exception $e) {
        echo $e->getMessage();
        }






        share|improve this answer








        New contributor




        San is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.









        share|improve this answer



        share|improve this answer






        New contributor




        San is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.









        answered 15 mins ago









        San

        101




        101




        New contributor




        San is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.





        New contributor





        San is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.






        San is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Code Review Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            Use MathJax to format equations. MathJax reference.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f94270%2fparking-lot-and-vehicle-classes%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Morgemoulin

            Scott Moir

            Souastre