Click to See Complete Forum and Search --> : [RESOLVED] Sharing info between objects
cnperry
11-26-2005, 01:35 AM
Hi,
I'm pretty new to PHP5/OOP, and I have this particular question about how to share information between two classes I have. This is my first project using OOP, so I'm not really sure where to go, and unfortunately my Wrox Pro PHP5 book isn't helping as much as I hoped it would.
One class is a SystemComponent class which, for now, just reads in my app's configuration file. Another class I'm working on is a database class (called DbConnector). The goal is to take the information read-in by SystemComponent, and supply DbConnector with the correct connection parameters.
I've come up with something that works, but I don't know if it's good/proper coding practice (which, ultimately is more important to me than just being able to hack something together). Am I doing this right?
Three files: index.php, SystemComponent.class.php, and dbConnector.class.php.
Below is all the relevant code.
index.php
require_once('includes/SystemComponent.class.php');
require_once('includes/dbConnector.class.php');
//reads config file upon construct
$system = new SystemComponent;
/**This is where the main point of my question is: is this the correct way to do this?
** or would I do better to just feed it something like $system->getSettings() ?**/
$DB = new DbConnecto($system);
SystemComponent.class.php
class SystemComponent {
private $_settings;
function __construct()
{
$this->setSettings();
}
function getSettings($var=NULL)
{
// if $var==NULL return ALL settings
//else return the value for the parameter $var.
}
function setSettings($var=NULL)
{
//basically, if it's NULL, set default settings, as supplied by config file
//else, change/add setting parameter.
}
dbConnector.class.php
/**I'm pretty sure I should put the "extends SystemComponent", correct? **/
class DbConnector extends SystemComponent {
private $_settings;
function __construct($system)
{
/**is this the proper way to pass info?**/
$this->_settings=$system->getSettings();
}
Thanks so much for your help! I really appreciate it! :cool:
Take care, everyone.
~Cameron
stephaneey
11-26-2005, 07:38 AM
It's not exactly what I would do. I would either use inheritance without passing $system to the constructor of dbconnector, either not using inheritance but passing $system.
Here you make a mix of inheritance and the fact that you pass $system to the constructor. It looks a bit redundant to me. I would do it that way,
class System
{
protected get_settings()
{
....
}
}
class dbConnector extends System
{
public __construct()
{
$this->get_settings(); //you call the super class method get_settings
}
}
$db=new dbConnector();
The other approach would be to completely separate those classes and to pass some information got from the system class to the dbConnector one by the instance. Well, it's only my opinion :))
cnperry
11-26-2005, 07:37 PM
Thanks! I think I understand the how/why of this. All I can say is that I've read so much things get lost and forgotten. I'll play around with what you suggested.
Take care,
~Cameron
cnperry
11-28-2005, 04:28 AM
Ok, I tried it, and found out some interesting things, and I want to make sure this is "how it goes," so to speak.
Sample code:
class A{
private $variable;
public function __construct(){
$this->variable="now THIS is a variable";
}
public function getVar(){
return $this->variable;
}
}
class B extends A{
public function __construct(){
parent::__construct();
echo"<b>From B:</b> ";
print ($this->getVar());
echo"<br>";
}
}
$classA = new A;
echo "<b> From A:</b> ";
print($classA->getVar());
echo "<br>";
$varB = new B;
In this example, and others like it, it seems that the only way for B to see the variable set by the constructor in A is to call parent::__construct(), unless that variable is set outside of function scope,
EGclass A{
public $variable="some value";
I guess i learned is that the parent __construct() isn't automatically called just by typing class B extends A...
Though I assume so, is this sort-of the way of doing this sort of thing?
Thanks!
Cameron
Shrike
11-28-2005, 05:29 AM
Yes you are right, in PHP parent constructors are not automatically called, whereas in Java (which PHP 5 borrows heavily from) they are. One notable exception is that if you omit the constructor from the derived class then the parent constructor is called in it's place, if there is one.
cnperry
11-28-2005, 07:14 AM
After working with some ideas and the way the code is currently structured, it seems like creating two separate classes where the DB object doesn't extend the SystemComponent is going to be the simplest route. Here's why:
1) It's not possible to refer to the parent class's variables to the child (or at least have them stick around to even be used). Below is my code concept that doesn't work as intended.
class SystemComponent{
function __construct(){
setSettings();
}
function setSettings(){
$this->_settings=$array;
}
getSettings() {
return $this->_settings;
}
}
class DB extends SystemComponent {
function __construct(){
parent::__construct;
print_r($this->_settings); //won't print the array (it's not set).
}
}
2) The easiest (and most flexible) is to create the two classes and pass the SystemComponent to the DB class, similar to what I'd done in my first post, just minus the "extends... " stuff:
class DB {
function __construct($system) {
$this->_settings=$system->getSettings(); //or ...=&$system->getSettings();
}
}
$SystemClass= new SystemComponent;
$DB = new DB($SystemClass);
/* Do a variety of tasks with the $DB class, and even change parameters/settings
part-way through the script, thus changing the behavior from them on, if need be */
At least in this scenario this scheme makes more sense than trying to share things between a parent/child.
Thanks,
Cameron
Shrike
11-28-2005, 07:33 AM
I think you need to consider the intended use of inheritance (http://en.wikipedia.org/wiki/Inheritance_%28computer_science%29). One of the key uses is specialisation - reimplementing parts of a class to change it's purpose somehow. It also crops up alot during refactoring - it makes sense to have common code in a base class and specialised code in a derived class. Whether you should use inheritance comes down to the is-a relationship. In your case you need to ask yourself 'is DB a SystemComponent'. To me it seems that the answer is yes, and so inheritance should be used. If there is no discernable relationship between class A and class B then you should look at composition (passing class B as a parameter to class A).
It's also important to realise that when you create a derived class, all of the public and protected methods and variables are automatically available in the derived classes' context. If you override a method or variable in the derived context (by redefining it) then the parent methods and variables can be accessed using the parent keyword.
class Foo
{
public $foo = "A public variable in the parent context";
protected function bar()
{
echo "Using a parent method in the derived context";
}
}
// This class has no methods or variables, but I can still call the parent
// methods and variables from within context of Bar
class Bar extends Foo {}
$bar = new Bar();
echo $bar->foo;
$bar->bar();
bubblenut
11-28-2005, 09:51 AM
whereas in Java they are.
Are you sure? If you've got an overloaded constructor how would Java know which parent to call? You may be building what the parent contructor requires within the child contructor.
ie.
public class FileParent {
FileParent( String path ) {
// do stuff with path
}
}
public class FileChild extends FileParent {
FileStuff( String filekey ) {
if( filekey.equalsIgnoreCase( 'myfile' ) ) {
super( '/some/other/path' );
}
}
}
Also, I'd like to provide an allternative view with regard to using inheritance. Building system-wide base objects can be dangerous. You're loosing orthogonality by tying all your classes to the architecture of the current project. If you decide you want database access in another project you have to port the entire heirachy. This isn't too bad now (and may not become too bad in a small system) but what happens when when your SystemComponent class starts doing other System related thigs which aren't related to DB? What happens when you add other data access components, should you change where DB inherits from?
Although it's just a barely disguised global variable, I would be inclined to use an unrelated Singleton class for settings (probably accessing it from within the class as show below). The advantage of this method is that it is more resilliant to change and it is more portable. You still need to port both the DB and the Settings classes to use the DB class in another project but you don't have to port the architecture so you can be using a completely different overall design and just plug in your DB class.
Example PHP5
class Settings {
private
$instance = null,
$settings = array();
private function __construct() {
}
public function instance() {
if( !$this->instance ) $this->instance = new Settings();
return $this->instance;
}
public function set( $key, $value ) {
$this->settings[$key] = $value;
}
public function get( $key ) {
return $this->settigns[$key];
}
}
class DB {
public function __construct( ) {
$settings = Settings::instance();
$this->db_set_one = $settings->get('db_set_one');
$this->db_set_two = $settings->get('db_set_two');
// .. or however you want to implement Settings
}
}
Shrike
11-28-2005, 10:45 AM
Are you sure? If you've got an overloaded constructor how would Java know which parent to call?
Pretty sure, try running the following:
class Tester
{
Tester()
{
System.out.println( "Base constructor" );
}
public static void main( String[] args )
{
TestChaining chain = new TestChaining();
}
}
class TestChaining extends Tester
{
TestChaining()
{
System.out.println( "Derived constructor" );
}
}
Interesting to note that the constructors are called from the inside-out. There is something in my Thinking in Java book about chaining overloaded constructors but I can't find it :)
I do agree with you with regard to the PHP code. To my mind a SystemComponent object is not simply a container for settings, rather a base class for system components. Semantics eh ;)
bubblenut
11-28-2005, 10:56 AM
Ahh, right, I see. I've just remembered that my example wouldn't work anyway. Calls to super have to be the first line in the child constructor, which makes sense now you say about the constructors being called inside out (I got very frustrated a little while ago because it wouldn't work). That's interesting, cheers.
Shrike
11-28-2005, 11:22 AM
I think we are both right: if the class constructor has arguments with no default values then you have to call super( args ) or you'll get a compiler error. Otherwise you can omit the line as the compiler can find the constructor without help. With regard to the overloading question - you can only overload constructors with the argument list as theres no return value - hence why it's not an issue with no-arg constructors.
Bruce Eckle says:
If your class doesn’t have default arguments, or if you want to call a base-class constructor that has an argument, you must explicitly write the calls to the base-class constructor using the super keyword and the appropriate argument list.
Anyway enough of this Java malarkey! :evilgrin:
cnperry
11-28-2005, 05:29 PM
**swish** <- that's the sound of a lot of things flying over my head! :)
I've received a lot of insight, nonetheless, and I'm starting to really figure out how all these things play together nicely. Thanks shrike and bubblenut! I think it definitely makes most sense to keep DB as a child of SystemComponent - now that I'm getting a better for it. I'll try another set of test classes and will post them later today.
Thanks!
Cameron
Shrike
11-28-2005, 06:47 PM
I would pay attention to what Bubblenut says - if SystemComponent is really just a container for system settings then your database class doesn't really have any relationship to it :)
cnperry
11-28-2005, 07:41 PM
Shrike, that makes sense. I haven't planned-out exactly what SystemComponent (or better, its children) will do, but it looks like it could do more than just be a container for variables. Some other ideas I have for it: user validation, input filtering, and maybe a few other things like file i/o). Honestly, though, I'm too new to OOP to really know all the reasons why you' d choose one method over another (though both of you are definitely helping!), and I'm not sure what something like a "SystemComponent" would really do outside reading/changing settings and the few things I mentioned above. What I do know is that it seems like several tutorials out there on creating CMSs and their ilk all seem to have one form of SystemComponent or another (including the link in your sig, Shrike).
Considering a SystemComponent that does more than just work with settings variables: it would make sense for the children to be related to each other in some way also. E.g., parent class Automobile would have children sedan, pickup, convertable, etc. Those children are similar in one way or another - all autos. As for the things I've listed the SystemComponent doing, is DB access, file io, user validation, input filter, etc, all related? Only vaguely, but probably not enough to warrant them extending the same parent class. It would make much more sense to make all those their own classes (or maybe make file io and db access children of the same parent). Now that I think of it, I'm seeing several potential problems of keeping the SystemComponent as the parent of all those - what happens as the project grows and increasing specialization may be needed, but becomes increasingly difficult as the same methods and properties affect very different children classes? Headaches. At least with more componentized classes upkeep and portability are minimized and increased, respectively. Seems to me like a light bulb above my head is starting to glow!
Thanks, again! Your patience and insights are extremely invaluable to me, and I really appreciate it.
~Cameron
PHP Builder
Copyright WebMediaBrands Inc. All Rights Reserved.