OK, here's a question that should get you thinking. It's been bothering me for quite some time. It's not urgent, but it's definitely worth 500 points for a good answer!
In a typical abstracted set of objects, it's common to have many instances grouped under some other instance, for example passengers on a flight, subscribers in a mailing list etc. These are each represented by independent objects that can look after their own data. So my passenger class might be something like (all pseudocode):
class passenger {
var name;
var seatnum; //etc
var flightid;
function passenger($id=0) { //constructor
if (id > 0 ) $this->get($id);
}
function get($id) {
query(select * from passengers where id = $id) //Query database to get passenger details
}
function update() {
//Write altered passenger data back to db
}
}
and my flight might be:
class flight {
var $start
var $destination
var $time
var $passengers
function flight() { //constructor
$this->passengers = array();
}
function getpassengers() {
$passengers = querydatabase(select id from passengers where flightid = $this->id)
foreach ($passengers as $passengerid)
$this->passengers[] = new passenger($passengerid); //get all the passenger objects for this flight
}
//flight storage skipped
}
this is all nice and encapsulated - passengers and flight are self-sufficient in terms of data storage, neither needs to know about the other's internals.
Now the problem arises. Say I have 100 passengers - I'm going to be doing 101 database queries to get their data - a serious performance problem.
Getting all the data could be done in 1 query, but where to put it? A passenger shouldn't have to know about other passengers, and a flight shouldn't have to know all the details of each passenger. I've seen suggestions to use a factory class that creates multiple passengers, but I can't see how to do that in an object-clean way. This would work:
class passengerfactory {
static function makepassengers($flightid) {
$passengerarray = array();
$passengers = query(SELECT * from passenger where flightid = $flightid)
foreach($passengers as $passenger) //pretend that $passenger contains a complete record
$p = new passenger;
$p->flightid = $flightid;
$p->name = $passenger['name'];
$p->seatno = $passenger['seatno'];
}
return $passengerarray;
}
but it means that this class has unnecessary inside knowledge of the passenger class and its database table, and if I change the passenger class then I have to update this one too, breaking my encapsulation. That's not a big deal in this simple example, but it can be in bigger hierarchies, and it's the principle of the thing I'm trying to nail down.
If I made the factory class a subclass of passenger then it would at least keep the data private by inheriting it, but it's still not a clean definition - the list of passengers is not a specialisation of a passenger (as for example flight crew might be).
I'm sure that a good answer to this is in a design patterns book somewhere, but I've not been able to find one. Any ideas?
I've got another question like this too, but that can wait...
Start Free Trial