By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
457,759 Members | 1,242 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 457,759 IT Pros & Developers. It's quick & easy.

Listing objects

P: n/a
Hi,

Suppose we have a hierarchical class structure that looks something like
this:

Object
|
+-- Main
|
+-- Object1
| |
| +-- Object11
| |
| +-- Object 12
|
+-- Object2
|
+-- Object3
|
+-- Object4
|
+-- Object41
| |
| +-- Object 411
|
+-- Object 42

Now, the application looks like this:

<?php
$app = new Main(...);
$app->doSomething();
?>

Class Main() is instantiated only once, however it works with all other
classes, which may be instantiated a number of times.

Now suppose there is a scenario:

Main creates Object11.
Object11 calls Object1::foo(), which creates Object2.
Object2 may then create Object1, in which case
Object1 calls its foo(), which creates Object2.
Object2 may then create Object1
..etc. until Object2 decides not to create Object1..

So, basically we'd get:

Main
Object11->foo()
Object2
Object1->foo()
Object2
...

Now, when Object2 instantiates, it assigns a value to its variable, say,
Object2::var, which should be unique. So, after assigning the value,
Object2 has to check it against any other Object2::var.
However, Object2 is not necessarily instantiated under Object1, so,
let's say we have

Main
$obj11 = Object11
$obj2 = Object2
$obj1 = Object1
$obj2 = Object2
$obj3 = Object3
$obj2 = Object2
$obj411 = Object411
$obj42 = Object42
$obj1 = Object1
$obj2 = Object2
Assume $app->obj11->obj2->obj1->obj2::var should be checked against

$app->obj11->obj2::var
$app->obj3->obj2::var
$app->obj411->obj42->obj1->obj2::var

I assumed the easiest way would be to have a global array of all objects
descending from Object and update it in Object's constructor:

function Object() {
global $objects;
$objects[count($objects)] = &$this;
...
}

Then we could simply iterate through $objects, and if object's class is
Object2, compare the var's.
However, this does not work, even through the object is passed by
reference - if the object's variables are updated (by the object itself
or by another object), the global array apparently has a copy of the
initial object state:

$obj2 = new Object2();
results in
$obj2 = Object2( var => 1 )
$objects[0] = Object2( var => 1 )

$obj2->var++;
results in
$obj2 = Object2( var => 2 )
$objects[0] = Object2( var => 1 )

So, I guess, some other mechanism should be used.
Any ideas?

Thanks,
luph
Dec 30 '05 #1
Share this Question
Share on Google+
8 Replies


P: n/a
Following on from Lüpher Cypher's message. . .
Hi,

Suppose we have a hierarchical class structure that looks something like
this:

Sorry, I lost the plot half way through.

Your objects can be
1 - nesting : object has a 'what are my children' and 'who is my
parent' functionality.

2 - pointing : eg object has a 'these are my siblings' or
'previous/next' functionality

3 - dictionary entries : object in some container is discoverable by
name

Variations and combinations of course apply. BUT every object exists in
some context. I /think/ you might be trying to get A's to point-to/own
B's where more than one A could be the parent of a B. Eg
"Sally is daughter of Jean"
"Geoffrey is son of Teddy"
"Sally is daughter of Teddy" // Only one Sally! J+T are married.

In this case[1] create an array of children and point to elements in
that array from your parent objects.

eg $child['Sally'] = new ChildObj(.....);
then $Jean->AddChild('Sally') // just a key name or index
or $Jean->AddChild($child['Sally']) // inside the routine do $myKids[]
= & $NewChild
where $NewChild is the function argument.

[1] If in this example children can be parents then you should be
looking at a single universal 'person' class with the necessary links.


--
PETER FOX Not the same since the adhesive company came unstuck
pe******@eminent.demon.co.uk.not.this.bit.no.html
2 Tees Close, Witham, Essex.
Gravity beer in Essex <http://www.eminent.demon.co.uk>
Dec 30 '05 #2

P: n/a


Lüpher Cypher wrote:
Hi,

Suppose we have a hierarchical class structure that looks something like
this:

Object
|
+-- Main
|
+-- Object1
| |
| +-- Object11
| |
| +-- Object 12
|
+-- Object2
|
+-- Object3
|
+-- Object4
|
+-- Object41
| |
| +-- Object 411
|
+-- Object 42

Now, the application looks like this:

<?php
$app = new Main(...);
$app->doSomething();
?>

Class Main() is instantiated only once, however it works with all other
classes, which may be instantiated a number of times.

Now suppose there is a scenario:

Main creates Object11.
Object11 calls Object1::foo(), which creates Object2.
Object2 may then create Object1, in which case
Object1 calls its foo(), which creates Object2.
Object2 may then create Object1
..etc. until Object2 decides not to create Object1..

So, basically we'd get:

Main
Object11->foo()
Object2
Object1->foo()
Object2
...

Now, when Object2 instantiates, it assigns a value to its variable, say,
Object2::var, which should be unique. So, after assigning the value,
Object2 has to check it against any other Object2::var.
However, Object2 is not necessarily instantiated under Object1, so,
let's say we have

Main
$obj11 = Object11
$obj2 = Object2
$obj1 = Object1
$obj2 = Object2
$obj3 = Object3
$obj2 = Object2
$obj411 = Object411
$obj42 = Object42
$obj1 = Object1
$obj2 = Object2
Assume $app->obj11->obj2->obj1->obj2::var should be checked against

$app->obj11->obj2::var
$app->obj3->obj2::var
$app->obj411->obj42->obj1->obj2::var

I assumed the easiest way would be to have a global array of all objects
descending from Object and update it in Object's constructor:

function Object() {
global $objects;
$objects[count($objects)] = &$this;
...
}

Then we could simply iterate through $objects, and if object's class is
Object2, compare the var's.
However, this does not work, even through the object is passed by
reference - if the object's variables are updated (by the object itself
or by another object), the global array apparently has a copy of the
initial object state:

$obj2 = new Object2();
results in
$obj2 = Object2( var => 1 )
$objects[0] = Object2( var => 1 )

$obj2->var++;
results in
$obj2 = Object2( var => 2 )
$objects[0] = Object2( var => 1 )

So, I guess, some other mechanism should be used.
Any ideas?

Thanks,
luph


This is a textbook case of a badly designed set of classes. Their
relationships are so complicated that even you the architect, are having
problems explaining yourself. I think you'll need to go back to the
drawing board on this one.

Extract the common features into a set of interfaces which should help
clarify the class relationships and clean up the design. Also wherever
possible, try to use (design) patterns, that way, your code will be
reusable (not to mention easir to maintain). Incidentally, your Main
class looks like a Singleton pattern, but only you the archiect, can
know that for sure since your description given here is not very lucid.

hope that helps

Dec 30 '05 #3

P: n/a
As Peter Fox has pointed out, put the tasks where they belong. I have
the feeling that you are playing the role of a conductor who has to
learn each musician how to play as well.

Use clear names for objects. If your model represents companies,
departments, employees, tasks and subtasks, just call your classes those
descriptive names.

If you have to compare different instances that might have the same
state (value of internal variables), you could give the class an
IsEqualTo method to do the comparison. Even better, a lazy collection
can be used to ensure that equal objects are actually the same instance.
You could then just use the reference equality operator (===).

Oh, and just delegate. Conductors trust their musicians in their skills.
At least in a concert. In software, use unit tests as a "musician's exam".
For instance, if each object in your structure has some value and you
want to calculate the total of it, don't traverse the structure
yourself. Trust your directly known objects and ask them to calculate
THEIR total and add these.

It is not always bad to repeat methods:
Employee->Name()
May be defined as
Employee->EmployeeRecordWrapper->Name()

The short verion is clearer AND independent of the underlying structure,
giving you more architectural flexibility.

If you fancy some technical stuff, google for the "Law of Demeter". It
is about what objects should know each other and what objects should not.

Best regards
Dec 30 '05 #4

P: n/a
Dikkie Dik wrote:
As Peter Fox has pointed out, put the tasks where they belong. I have
the feeling that you are playing the role of a conductor who has to
learn each musician how to play as well.

Use clear names for objects. If your model represents companies,
departments, employees, tasks and subtasks, just call your classes those
descriptive names.
I figured that much :)

If you have to compare different instances that might have the same
state (value of internal variables), you could give the class an
IsEqualTo method to do the comparison. Even better, a lazy collection
can be used to ensure that equal objects are actually the same instance.
You could then just use the reference equality operator (===).


Hmm.. $a === $b will return true if $a == $b (i.e. their properties have
the same values) and they are of the same class. That doesn't mean they
are the same object, though..
Ok, let me try a clearer version :)

Object
|
+--- App
| ::$page
|
+--- Control
| ::$parent
| ::$data
| ::$object
|
+--- Data
| ::$id
|
+--- Template
| ::owner
| ::control
|
+--- Page

In the main script I have

$app = new App();

In constructors I have

function App() {
...
$null = null;
$this->page = new Page($null);
}

function Page(&$owner) {
parent::Template(&$owner);
}

function Template(&$owner) {
$this->owner = &$owner;
...
$this->control = new Control(&$this);
}

function Control(&$parent) {
$this->parent = &$parent;
...
$this->data = new Data();
...
if (already_exists($this->data->id)) {
echo "Duplicate ID";
return;
}
...
if ($some_condition) {
$object = new $CustomSubclassOfTemplate($this->parent);
}
}

function Data() {
...
$this->id = $some_retrieved_id;
}

Basically, the interesting part is:

When Template is instantiated (initially Page in App constructor), it
instantiates Control, which, in turn, instantiates Data. At this point,
$data->id will be set.
At the end of Control's constructor, if some condition is satisfied, a
subclass of Template will be instantiated and stored in Control's $object.
Thus, we can have:
$app->$page->$control->$object->$control->$object->...->$control->null($object)
We can also pretty much go bottom-up through Template objects via $owner
and we can get the Template object from Control via $parent.

Now, in the middle of Control's constructor there is a check that
$data-id does not already exist.

One way would be to go through Templates:

$temp = $this->parent;
while (isset($temp)) {
if ($this->data->id == $temp->control->data->id) {
echo "error";
return;
}
}

Apparently this does not work, even though owner is passed by reference.

Another way would be to have a global array and save all objects there
by reference. This is actually more appealing to me, since then I could
access any object at any time from anywhere. Say, if I have an array of
objects under Template and each of them has an associative array
key=>value, then to "compile" an associative array from all those
arrays, I'd simply iterate through the global array, check object's
class, and take in the key/value pairs in the resulting array, rather
than traverse a tree of Templates and those objects. Anyways, what I
tried is something like this:

global $objects;
$objects = array();

class Object() {
global $objects;
$objects[count($objects)] = &$this;
}

However, if a property of an object is changed, the object in $objects
stays the same.. But then, it must be a copy?
luph
Dec 30 '05 #5

P: n/a
Oh, forgot to update $temp here:

$temp = $this->parent;
while (isset($temp)) {
if ($this->data->id == $temp->control->data->id) {
echo "error";
return;
}
$temp = $temp->owner;
}

Also, it does not work because $temp->control->data is null. However, if
$this->data is accessed from $temp->control, it is instantiated..

luph
Dec 30 '05 #6

P: n/a
Peter Fox wrote:
Following on from Lüpher Cypher's message. . .
Hi,

Suppose we have a hierarchical class structure that looks something like
this: Sorry, I lost the plot half way through.

Your objects can be
1 - nesting : object has a 'what are my children' and 'who is my
parent' functionality.

2 - pointing : eg object has a 'these are my siblings' or
'previous/next' functionality

3 - dictionary entries : object in some container is discoverable by name


It is more of a [1] and [3] :)
[1] for I do have a tree-like structure of objects. I have $parent (for
all objects) and $owner (for objects that are of subclasses of some
class) properties so, I can go bottom-up. I also do have children array
for objects that have owner so, I can go top-down.
[3] for I am trying to have a global associative array that has unqiue
object names as keys and object references as values.
Variations and combinations of course apply. BUT every object exists in
some context. I /think/ you might be trying to get A's to point-to/own
B's where more than one A could be the parent of a B. Eg
"Sally is daughter of Jean"
"Geoffrey is son of Teddy"
"Sally is daughter of Teddy" // Only one Sally! J+T are married.

In this case[1] create an array of children and point to elements in
that array from your parent objects.

eg $child['Sally'] = new ChildObj(.....);
then $Jean->AddChild('Sally') // just a key name or index
or $Jean->AddChild($child['Sally']) // inside the routine do $myKids[]
= & $NewChild
where $NewChild is the function argument.

[1] If in this example children can be parents then you should be
looking at a single universal 'person' class with the necessary links.


I do have a universal class :)
Say,

class Object
var $name;
var $owner;
var $children[];
Now,

class A extends Object
class B extends Object

A's can be owned by A's and/or B's, B's can have many A's and/or B's
B's can be owned by A's and/or B's and can have many A's and/or B's as well.

One of the things I am trying to do is to have global references to all
Objects:

global $objects;
$objects = array();

function Object() {
global $objects;
... assign name ...
$objects[$this->name] = &$this;
}

However, if a property of some Object changes, $objects[$object_name]
still has the "old" object. I assume $objects[$object_name] does not
reference the object then, rather has a copy of it. So far I was unable
to solve this.. By the way, this will work:

$obj = new Object();
$objects[$obj->name] = &$obj;

if it is executed outside any function and any class..
luph
Dec 30 '05 #7

P: n/a
> Hmm.. $a === $b will return true if $a == $b (i.e. their properties have
the same values) and they are of the same class. That doesn't mean they
are the same object, though..
Sorry, I'm probably confusing with other languages.
Ok, let me try a clearer version :)

Object
|
+--- App
| ::$page
|
+--- Control
| ::$parent
| ::$data
| ::$object
|
+--- Data
| ::$id
|
+--- Template
| ::owner
| ::control
|
+--- Page
This is the inheritance hiearchy, I assume that you also have an
association hierachy. App will be the root, and then...
I would think that an App creates a page and creates a data channel, or
asks a Control to do that. Within my perception, a page has a template
and date (gets it from App). So App knows Control and Page, and also
knows that Control has Data. All these objects do not have to know App
as far as I can see.

In the main script I have

$app = new App();

In constructors I have

function App() {
...
$null = null;
$this->page = new Page($null);
}

function Page(&$owner) {
parent::Template(&$owner);
}

function Template(&$owner) {
$this->owner = &$owner;
...
$this->control = new Control(&$this);
}

function Control(&$parent) {
$this->parent = &$parent;
...
$this->data = new Data();
...
if (already_exists($this->data->id)) {
echo "Duplicate ID";
return;
}
If the problem is here, let's keep it here. I have written my own
DatabaseID class with its own IsEqualTo method (and an IsNew method).
For the very simple reason: A data-object might be constructed with
database data (known ID) or be a new record that was not yet stored in
the database. With this DatabaseID class, I can give EVERY data-object
an ID. If the DatabaseID instance is not contructed with a database ID,
the IsEqualTo method will always return FALSE. Two new records are per
definition different in my application. Only if both compared objects
were constructed with an ID from the database, an actual by-value
comparison if done.
...
if ($some_condition) {
$object = new $CustomSubclassOfTemplate($this->parent);
}
}
What is template doing in Control? If Control is responsible for both
templates and data, it is a clear signal that this class wants to be
split into two classes.

function Data() {
...
$this->id = $some_retrieved_id;
}

Basically, the interesting part is:

When Template is instantiated (initially Page in App constructor), it
instantiates Control, which, in turn, instantiates Data. At this point,
$data->id will be set.
At the end of Control's constructor, if some condition is satisfied, a
subclass of Template will be instantiated and stored in Control's $object.
Thus, we can have:
$app->$page->$control->$object->$control->$object->...->$control->null($object)
Is that bad? Frankly I don't care how deep a structure goes, as long as
it makes sense and is finite.

We can also pretty much go bottom-up through Template objects via $owner
and we can get the Template object from Control via $parent.

Now, in the middle of Control's constructor there is a check that
$data-id does not already exist.

One way would be to go through Templates:

$temp = $this->parent;
while (isset($temp)) {
if ($this->data->id == $temp->control->data->id) {
echo "error";
return;
}
}

Apparently this does not work, even though owner is passed by reference.

Another way would be to have a global array and save all objects there
by reference. This is actually more appealing to me, since then I could
access any object at any time from anywhere.
Yikes!
One of the main features of object-oriented programming is that you can
nicely shield off what others should not access. The contents of a page
should only be accessible by asking the $page object, which is only
accessible by asking $app. Why? Because it allows $page to decide how
and when to fill in the data, and to throw an exception if no data was
passed. That is $page's reponsibility, not $app's, and not even yours.
If you want to peek inside of $page's internals, write a temporary
method or echo statement, during development only.
Not even your responsibility? No. You can program it, write a unit test
for it, and when it is finished, it can live on its own. It is sad, but
the only thing left for you to do is typing the URL...
Say, if I have an array of
objects under Template and each of them has an associative array
key=>value, then to "compile" an associative array from all those
arrays, I'd simply iterate through the global array, check object's
class, and take in the key/value pairs in the resulting array, rather
than traverse a tree of Templates and those objects. Anyways, what I
tried is something like this:

global $objects;
$objects = array();

class Object() {
global $objects;
$objects[count($objects)] = &$this;
}

However, if a property of an object is changed, the object in $objects
stays the same.. But then, it must be a copy?
luph

Dec 30 '05 #8

P: n/a
Dikkie Dik wrote:
Ok, let me try a clearer version :)

Object
|
+--- App
| ::$page
|
+--- Control
| ::$parent
| ::$data
| ::$object
|
+--- Data
| ::$id
|
+--- Template
| ::owner
| ::control
|
+--- Page
This is the inheritance hiearchy, I assume that you also have an
association hierachy. App will be the root, and then...
I would think that an App creates a page and creates a data channel, or
asks a Control to do that. Within my perception, a page has a template
and date (gets it from App). So App knows Control and Page, and also
knows that Control has Data. All these objects do not have to know App
as far as I can see.


Well, App only creates and knows Page (Template).
Template creates Controller, which creates Data. So, Template knows
Control, and Control knows Data. However, depending on the Data, Control
may instantiate another Template (at which point the process repeats).
The original Template has access to Control's Template.
Right now it's like this:

$app = new App(
$page = new Page(
$ctl = new Control(
$data = new Data()
if (<another template>) {
$temp = new TemplateSubclass(
$ctl = new Control(
...
)
)
}
)
)
)
$app->render(
$page->render(
if ($ctl->temp exists) {
$out = $ctl->temp->render(
if ($ctl->temp exists) {
$out = $ctl->temp->render(
...
)
}
return $out.<custom output>;
)
}
return $out.<custom output>;
)
echo <results>;
)

Some of these do call App's methods, though, for instance to get a
global application parameter..
In the main script I have

$app = new App();

In constructors I have

function App() {
...
$null = null;
$this->page = new Page($null);
}

function Page(&$owner) {
parent::Template(&$owner);
}

function Template(&$owner) {
$this->owner = &$owner;
...
$this->control = new Control(&$this);
}

function Control(&$parent) {
$this->parent = &$parent;
...
$this->data = new Data();
...
if (already_exists($this->data->id)) {
echo "Duplicate ID";
return;
}
If the problem is here, let's keep it here. I have written my own
DatabaseID class with its own IsEqualTo method (and an IsNew method).
For the very simple reason: A data-object might be constructed with
database data (known ID) or be a new record that was not yet stored in
the database. With this DatabaseID class, I can give EVERY data-object
an ID. If the DatabaseID instance is not contructed with a database ID,
the IsEqualTo method will always return FALSE. Two new records are per
definition different in my application. Only if both compared objects
were constructed with an ID from the database, an actual by-value
comparison if done.


Well, all I need is just checking whether an ID exists. In this case,
Data loads an XML file and sets its ID value to a string. This ID should
be unique. So, after Controller instantiates Data, it should check that
the loaded ID wasn't already assigned in some other Controller. I figure
the easiest way is to iterate an array of objects, see if an object has
Control as its class, and compare the IDs in Datas. :)
...
if ($some_condition) {
$object = new $CustomSubclassOfTemplate($this->parent);
}
}
What is template doing in Control? If Control is responsible for both
templates and data, it is a clear signal that this class wants to be
split into two classes.


Well, Template is presentation class so, it's all about *how* the data
should be displayed and has no other functionality. It knows its Control
so that it can present dynamic data. Control relies on Data to choose
*what* should be displayed.
Suppose Data consists of 3 classes. Control chooses one of them and
instantiates it. Template simply outputs whatever it is, inserting the
output of the class Control chose along way. This way Data is only about
data, Control is about what should be output, and Template is only about
how it should be output, no matter what the actual content is. I figure
instantiating the chosen class in Template would mean that Template is
also about data, and instantiating it in Data would mean that data is
dynamic.. :)

function Data() {
...
$this->id = $some_retrieved_id;
}

Basically, the interesting part is:

When Template is instantiated (initially Page in App constructor), it
instantiates Control, which, in turn, instantiates Data. At this
point, $data->id will be set.
At the end of Control's constructor, if some condition is satisfied, a
subclass of Template will be instantiated and stored in Control's
$object.
Thus, we can have:
$app->$page->$control->$object->$control->$object->...->$control->null($object)

Is that bad? Frankly I don't care how deep a structure goes, as long as
it makes sense and is finite.


Well, it's actually not all that bad, since there is no need to write
such long references :) It is finite, otherwise the page will never load :)

We can also pretty much go bottom-up through Template objects via
$owner and we can get the Template object from Control via $parent.

Now, in the middle of Control's constructor there is a check that
$data-id does not already exist.

One way would be to go through Templates:

$temp = $this->parent;
while (isset($temp)) {
if ($this->data->id == $temp->control->data->id) {
echo "error";
return;
}
}

Apparently this does not work, even though owner is passed by reference.
Found where the mistake is :) Apparently $a = new A() means that $a is a
copy of the actually instantiated class A() ;-o All I had to do was to
change all instantiations to form $a = &new A();

Another way would be to have a global array and save all objects there
by reference. This is actually more appealing to me, since then I
could access any object at any time from anywhere.


Yikes!
One of the main features of object-oriented programming is that you can
nicely shield off what others should not access. The contents of a page
should only be accessible by asking the $page object, which is only
accessible by asking $app. Why? Because it allows $page to decide how
and when to fill in the data, and to throw an exception if no data was
passed. That is $page's reponsibility, not $app's, and not even yours.
If you want to peek inside of $page's internals, write a temporary
method or echo statement, during development only.
Not even your responsibility? No. You can program it, write a unit test
for it, and when it is finished, it can live on its own. It is sad, but
the only thing left for you to do is typing the URL...


Well, echo's are what I usually use to debug things :)
In this case, however, I could either traverse a tree, meaning that I
would start in a leaf, and would have to traverse all nodes and leaves,
bottom-up, check if a node/leaf is of appropriate class, and then check
whether ID is the same or not.
With array, I can simply use foreach:

foreach($objects as $name=>$obj) {
if (!is_a($obj,"Control")) { continue; }
if ($name == $this->getName()) { continue; }
if ($this->getId() == $obj->getId()) { echo "error"; return; }
}

Where each object has a unique name :) That's what I actually use, now
that the references work in the global array.
It is pretty much a shortcut to doing some things :)
luph
Dec 30 '05 #9

This discussion thread is closed

Replies have been disabled for this discussion.