what's the best practice here...
It depends on your use case, how is the user going to use the system?. Would it be a Lab being "blowed" by a Person
? or the use case of the system is to have some Person
blow up Labs
?
or maybe it doesn't even matter :S
At the end the result is the same, but the important thing here is the semantic of the code. If sounds silly to have Labs being blowed by people, then don't do it.
So the golden rule, as BobTurbo mention, is to find out the "information expert" ( see: GRASP ) in the system and give the control to that object.
You usually define a user history or narrative on how the system would be used, if, for instance, the narrative is:
When a person does something everyone in the lab has to be notified.
Then, to me it means that a Person
works in a Lab
, when it is created, that person may receive the lab he works on, and register himself to be notified of what happens in that perticula lab.
Since the lab has the list of the persons to notify, it makes sense to be the lab who performs the notification ( Person gives control to Lab in this case )
Then probably the Person
could be defined as:
labs.Person {
- name: String
- lab : Lab
+ Person( withLab: Lab , andName: String ) {
self.lab = withLab
self.name = andName
self.lab.subscribe( self ) // want to know what happens
}
+ blowUpLab() {
lab.boom!(blownBy:self)
}
// receive a lab even notification
// performed by "someone"
+ labEvent( lab:Lab, by: Person ) {
// if is my lab and it wasn't me?
if( self.labg .== lab .&& self .!= by ) {
// ok it was someone else....
}
}
}
So, the person does something in the lab, in this case the public method blowUpLab
which just blows up the person's lab by invoking the Lab's boom!
method.
In turn the Lab
perform the method actions and notify all its subscribers at the end:
labs.Lab {
- labName:String
- subscribers: Person[0..*]
+ subscribe( to: Person ) {
subscribers.add( to )
}
+ boom!( blowedBy: Person ) {
// do blow up the lab
....
// and then notify:
subscriber.forEach( person: Person ) {
person.labEvent( self, blowedBy )
}
}
}
This is the observer pattern.
Finally your main app will create persons and labs and execute the use case:
labs.MainApp {
_ main() {
blackMesaLab = Lab("BlackMesa")
gordon = Person( withLab: blackMesaLab, andName: "Gordon Freeman")
scott = Person( withLab: blackMesaLab, andName: "Scott Tiger")
james = Person( withLab: Lab("SO Labs"), andName:" James Hetfield");
persons = Array( gordon, scott, james )
....
while( true ) {
// every now and then, have someone blowing up it's lab
if ( randomNumber() .== 42 ) {
person.at( randomPosition ).blowUpLab()
}
}
}
}
This main app, will create three person, with some lab, only two of them are related ( scott and gordon )
Randomly one of them will receive the blowUpLab
message and will perform the method. The lab in turn will notify all the subscribers of that lab.
So, when James Hetfield, blow its lab, no one will be notified :)
The point is Do describe your use case, and identify the information expert there; give the control to that object, and let that object rely the control to other object, but only according to your use case
I hope it makes sense.