Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Designing classes & functions for object interaction [closed]

I've been trying to learn OOP techniques and design patterns for a little while and my approach has certainly improved, but I don't quite click when we're talking about objects of different classes interacting. I mostly work in PHP but I'd accept a general answer for the sake of learning. And apologies in advance if I use terms wrong, I'm self-taught here. Here's an example:

Suppose I want to model people moving around by car. There are 3 classes:

class Car {}
class Driver {}
class Road {}

and I want the Car class to have a function

public function Drive($time) {}

whereby driving should update the location of the driver, dependent on the speed limit of the road. My question is what's the best way to structure Drive so that Car interacts with the other objects. I can see 3 possibilities off the top of my head:

public function Drive($time, $Driver, $Road) {}

The advantage here is that the function itself tells me what arguments I need. The drawback is I can end up with lots of arguments passing around if I have a lot of similar actions, call private functions along the way, add passenger objects, etc.

$Car->setDriver($Driver);
$Car->setRoad($Road);
$Car->Drive($time);

The advantage is that I can call Drive very simply and it has access to whatever it needs. The drawback is that I have to remember to set the driver and road first because the function definition doesn't tell me.

class Car() {
  public function Drive($time) {
    $Driver = getDriver($this->DriverID);
    $Road = getRoad($this->RoadID);
  }
}
function getDriver($DriverID) {
  static $DriverArray;
  // check isset(), create object and place in array if not
  return $DriverArray[$DriverID]; 
}
function getRoad($RoadID) {} // assume similar

The advantage is that all the logic is internal, with DriverID and RoadID existing because they're foreign keys from the db. (Or they can be manually set beforehand, but that wouldn't be a memory issue because it'd be an intentional assignment.) The drawback is that all Driver operations would have to go through the same getDriver lest I end up with separate instances of the same Driver.

Just for completeness my fourth approach would be SELECT <columns> FROM Car_tbl INNER JOIN Driver_tbl on DriverID INNER JOIN Road_tbl on RoadID, which I'm pretty sure is bad OO design but is what I used to do because it performs fewer queries.

I'm leaning toward approach 3 but suspect that I'm missing something on a large level. Feel free to answer PHP specific or just how this is generally considered. Thanks.


edit: Appreciate answers so far, including links to further reading. Rather than overcomplicate the example, let's assume simplest possible UseCase now but most complex possible UseCase later. So e.g. Speed is right now function of getRoadSpeedLimit(), but later is limited by getMaxCarSpeed() and getMaxDriverSpeed() and the car's need for gas and the driver's need for food. And the road's speed limit adjusts if we have too many Driver objects using the same road, etc.

What Gordon and UmlCat both seem to be driving at (so to speak) is using an encapsulating class, is that right?

class RoadTrip_aka_Universe {
  // Operates on instances of Car, Driver, Route (which may contain roads)
}

But doesn't that still leave the question, are $Car, $Driver and $Route added in the constructor, applied by setters, pulled in from static externals? Whichever class we decide should have the methods, how does it get access to those other objects?

Also my gut reaction is that if everything just goes in a larger class, doesn't the inside of that class start to resemble a big classless structure with global variables? Don't take that as criticism, I'm just trying to understand because it feels like the letter of OOP but not the spirit of it.

Again, thanks.

like image 767
Amarsir Avatar asked Feb 04 '12 16:02

Amarsir


People also ask

What is a design class?

Definition. A design class represents an abstraction of one or several classes in the system's implementation; exactly what it corresponds to depends on the implementation language. For example, in an object-oriented language such as C++, a class can correspond to a plain class.

How many years is a designing course?

The duration for a Bachelor of Fashion Design course is 3 - 4 years. The duration of a BSc Fashion Designing is the same and is spread over 6 semesters. The students with a BSc in Fashion Designing can expand to multiple fronts of jobs like accessory designing, footwear designing, jewelry as well as textiles.


2 Answers

First, altought, seems unnecesary, you may add a single class that contains all other objects, like "universe" or "world". This, will help you model the real world, into an application.

class CarClass
{

} // class CarClass

class DriverClass
{

} // class DriverClass

class RoadClass
{

} // class RoadClass

class WorldClass
{
  /* public CarClass */ $Car;
  /* public DriverClass */ $Driver;
  /* public RoadClass */ $Road;
} // class WorldClass

And, an small code example. Could be something, like these:

 /* void */ function Example()
 {
    WorldClass $MyWorld = new WorldClass();

    // ...
 } // void function Example

Next. What are each objects "fields" , "operations", "members" ? You mention, that "Speed Limit" depends on the road.

class RoadClass
{
   /* public int */ $SpeedLimit; 
} // class RoadClass

/* void */ function Example()
{
   WorldClass $MyWorld = new WorldClass();

   $MyWorld->Road->SpeedLimit = "50" // ouch, "cow country"
    // ...
} // void function Example

How objects interacts. Well, here there is a problem. Sometimes objects depend on others, like been part of others, some times, objects are independent, they just related.

In this example, most objects depends of "World" object, but, they are only related to each other. Lets add a small comment to check that.

class WorldClass
{
  /* public depends CarClass */ $Car;
  /* public depends DriverClass */ $Driver;
  /* public depends RoadClass */ $Road;

  function __construct() {
     // parent::__construct();

     // "World" "contains" these objects
     $this->Car = new CarClass();
     $this->Driver = new DriverClass();
     $this->Road = new RoadClass();
  } // function __construct()

} // class WorldClass

The Car has a register machine that records how much distance has move, if starts, moves, and stops. Its a simple value, its not an object.

class CarClass
{
   /* public int */ $Distance; 
} // class RoadClass

But, there is an interaction with "outer" objects:

class CarClass
{
   /* public int */ $Distance; 
   /* public DriverClass related */ $Driver; 
   /* public RoadClass related */ $Road; 


  function __construct() {
     // parent::__construct();

     // "Car" only relates these objects
     $this->Driver = null;
     $this->Road = null;
  } // function __construct()
} // class RoadClass

/* void */ function Example()
{
   WorldClass $MyWorld = new WorldClass();
   $MyWorld->Road->SpeedLimit = "50"; // ouch, "cow country"

   $MyWorld->Car->Road = $MyWorld->Road; // a reference only
   $MyWorld->Car->Driver = $MyWorld->Driver; // a reference only

    // ...
} // void function Example

Since the distance of the car depends on the road speed limit, and changes, when the car drives then:

class CarClass
{
   /* public int */ $Distance; 
   /* public DriverClass related */ $Driver; 
   /* public RoadClass related */ $Road; 

  function __construct() {
     // parent::__construct();

     // "Car" only relates these objects
     $this->Driver = null;
     $this->Road = null;
  } // function __construct()


  /* void */ function Drive($time) {

    // general idea of formula:

    $this->distance = ($this->SpeedLimit * $time * $this->Road->SpeedLimit);
  } // void function Drive(...)

} // class RoadClass

/* void */ function Example()
{
   WorldClass $MyWorld = new WorldClass();
   MyWorld->Road->SpeedLimit = "50"; // ouch, "cow country"

   $MyWorld->Car->Road = $MyWorld->Road; // a reference only
   $MyWorld->Car->Driver = $MyWorld->Driver; // a reference only

   /* int */ $time = 60; // 1 hours in minutes

   $MyWorld->Car->Drive($time);
} // void function Example

Cheers.

like image 123
umlcat Avatar answered Sep 21 '22 21:09

umlcat


You usually put the method onto the class that has the most information to fulfill a certain task. So in your case, ask yourself which class that is. Also, you should try to keep dependencies between classes small and manageable, so you can achieve low coupling and high cohesion.

See GRASP (object-oriented_design)

As for your specific UseCase, I think you should first take a step back from writing concrete code. Instead try to put what you want to achieve into plain english first. What you said so far is:

  • people moving around by car
  • driving should update the location of the driver
  • new Location is dependent on the speed limit of the road.

If you think about this, you could end up with an (incomplete) UseCase like this:

UseCase UC1: Drive Car

  • Scope: DriveSim
  • Level: Primary Task
  • Actors: Driver
  • Priority: high
  • Preconditions: Driver has a Starting Location
  • Postconditions: Driver's Location is updated
  • Happy Path:
    • Driver selects a Car
    • Driver selects Road to travel
    • Driver sets total time to drive
    • System calculates total mileage
    • System updated Location of Driver
    • Driver exits Car

And this should already tell you, that having Driver, Car and Road is insufficient to transform this UseCase into code. As you can see there is something like a Location as well. However, there is no information whatsoever about what Location is. Is it GPSCoordindates? StreetAdresses? Or merely DistanceTraveled?

We can also argue that you do not need the Driver at all, because it is the main user of the application and all actions it takes are made through the UI anyway. And if so, maybe there should be another Domain concept: Roadtrip. And that collects the used Car, and the Road or rather the Route. And that can hold the StartingLocation and many Roads, each having a possible SpeedLimit.

What the UseCase doesnt say is whether the Driver will always drive at maximum allowed speed or whether the Car has a fuel tank that can run out of gas, and so on. Right now, Car doesnt provide anything to the Roadtrip. Do we really need it? So before diving into the code, make sure you understand the Problem Domain you are modelling.

like image 22
Gordon Avatar answered Sep 20 '22 21:09

Gordon