Jump to content

Class Extending


ether

Recommended Posts

I'm trying to access a child class from a child class via a parent class, if that makes sense :P

 

Here's what I've got:

 

index.php

require(ROOT_DIR . "ifc0nfig.php");
$ifc0nfig =& new ifc0nfig;

 

ifc0nfig.php

class ifc0nfig
{
public $func = NULL;
public $smarty = NULL;
public $MySQL = NULL;
public $navi = NULL;

function ifc0nfig( )
{
	require( ROOT_DIR . "functions.php" );
	$this->func =& new func( );

	require( ROOT_DIR . "mysql.php" );
	$this->MySQL =& new MySQL( "localhost", "12345", "12345", "12345" );

	require( ROOT_DIR . "navi.php" ) ;
	$this->navi =& new navi( );

	// etc, etc
}
}

 

From inside the Navi class I want to access the MySQL class, I've got

class navi extends ifc0nfig {}

(same with the MySQL class) and tried accessing the MySQL class differant ways but I just can't seem to access it. If I

print_r( $this ); print_r( get_class_methods( $this ) );

I get

navi Object

(

    [func] =>

    [smarty] =>

    [MySQL] =>

    [navi] =>

)

 

Array

(

    [0] => navi

    [1] => build

    [2] => ifc0nfig

)

 

You can see the variables are there, but they're just empty and I cant access them outside the ifc0nfig class.

 

I'm not sure how to go about it, can anyone help? Thanks! :)

Link to comment
Share on other sites

Your member data is never being initialized.  In PHP5 you name the constructor __construct (as opposed to giving it the same name as the class as you would in PHP4 or Java).  Give this code a shot:

 

<?php
class ifc0nfig
{
public $func = NULL;
public $smarty = NULL;
public $MySQL = NULL;
public $navi = NULL;

function __construct( )
{
	require( ROOT_DIR . "functions.php" );
	$this->func = new func( );

	require( ROOT_DIR . "mysql.php" );
	$this->MySQL = new MySQL( "localhost", "12345", "12345", "12345" );

	require( ROOT_DIR . "navi.php" ) ;
	$this->navi = new navi( );

	// etc, etc
}
}
?>

 

Best,

 

Patrick

Link to comment
Share on other sites

After taking a closer look I noticed that you have a super class (ifc0nfig) composed of a derived class (navi).  I don't think that this is what you originally set out to do.  Think of the relationship between a super class and a derived class as an "is a" relationship.  A dog is a mammal, a strawberry is a fruit, David Hasselhoff is a... also a fruit.  The composition relationship can be thought of as a "has a" relationship.  A dog has a tail, a strawberry has a stem, David Hasselhoff has... a Germany.  It doesn't usually make sense for a parent class to "have a" subclass (there are exceptions).  A dog has a mammal?  A strawberry has a fruit?  Perhaps you could explain how the classes you are trying to define relate to each other and I or someone else can help you with parlaying these relationships into code.

 

Best,

 

Patrick     

Link to comment
Share on other sites

I'm trying to keep everything manageable through multiple classes of which I can access all of them through one object ($ifc0nfig).

 

mysql ------@
	          |
            ifc0nfig
	      |      |
other classes--@----@----- navi

 

If the navi class wants to run a MySQL query it'll have to go through the ifc0nfig class to get there, if you get my drift? ($ifc0nfig->MySQL->some_method( );).

Link to comment
Share on other sites

That's not the way to it then... Read utexas_pjm's post about class relationships again.

 

This will do it:

<?php
class MySQL
{
public function some_method()
{

}
}

class ifc0nfig
{
public $MySQL;

public function __construct()
{
	$this->MySQL = new MySQL;
}
}

$ifc0nfig = new ifc0nfig();
$ifc0nfig->MySQL->some_method();
?>

Link to comment
Share on other sites

There's no reason to extend one with the other in this case, they are not interrelated in such a way to make it viable. Instead, use the mysql/navi object for what it is, an object.

 

<?php

class ifc0nfig // what's with the 0 instead of o?
{
    private $_mysql;

    public function __construct (navi $db)
    {
        $this->_mysql = $db;
    }

    public function doSomethingWithMySQL()
    {
        $this->_mysql->doSomething();
    }
}

Link to comment
Share on other sites

Daniel0: that works fine if I'm accessing $ifc0nfig->MySQL outside of a class, but if I'm in another class (navi) I cant access the MySQL object or even the ifc0nfig object (without class extends). The classes are in differant files (shouldn't matter?).

 

Jenk: Not sure if I follow you here but making $_mysql private isn't going to help at all? I want other classes to be able to access it.

Link to comment
Share on other sites

Conceptually you should think of objects as if they were real tangible objects.  People tend to make OO programming more difficult than it is (it was after all introduced to simplify development, not complicate it) because they think of objects in a convoluted abstract sense. 

 

Consider a case where we want to model a car.  We want our car be able to start, stop, honk, and go (turning is overrated).  Our car will also have a color.  In our simplistic world we will say that all vehicles can start, stop and go.  A car "is a" vehicle so this is a great opportunity for us to define a super class.  Since a vehicle is inherently abstract we'll define it as so:

 

<?php
<?php
abstract class Vehicle
{
private $color;
private $isStarted;

public function __construct($color)
{
	$this->color 		= $color;
	$this->isStarted 	= false;
	$this->isGoing 		= false;
}

public function start()
{
	if($this->isStarted){
		print "We are already started...";
	}else{
		print "We are starting...";
		$this->isStarted = true;
	}
}

public function stop()
{
	if($this->isGoing){
		print "We are stopping...";
		$this->isGoing = false;
	}else{
		print "We are already stopped...";
	}
}

public function go()
{
	if(!$this->isStarted){
		print "The vehicle is not started...";
		return;
	}

	if($this->isGoing){
		print "We are already going...";
	}else{
		print "We are going...";
		$this->isGoing = true;
	}
}

public function getColor()
{
	return $this->color;
}
}

class Car extends Vehicle
{
private $horn;
private $hornInstalled = false;

public function installHorn($horn)
{
	$this->horn = $horn;
	$this->hornInstalled = true;
}

public function honkHorn()
{
	if($this->hornInstalled){
		$this->horn->honk();
	}else{
		print "There is no horn installed...";
	}
}
}

class Horn
{
public function honk()
{
	print "Beep...";
}
}

$car_1 = new Car('Green');
$car_1->go();
// The vehicle is not started.  Ooops.
$car_1->start();
// Now we're started.
$car_1->go();
// Now we're going.
$car_1->honkHorn();
// Uh oh, there's not horn installed on this car (must be a Hyundai).
$car_1->stop();
// (sigh) We better stop and build one.
// Let's build a horn (What, are you going to you act like you don't carry horn
building supplies with you?).
$horn = new Horn();
// let's test it.
$horn->honk();
// Cool, it works.  Now let's install it.
$car_1->installHorn($horn);
// Now let's test in the car
$car_1->honkHorn();
// Works here too.  We can be back on our way...
$car_1->go();
$car_1->honkHorn();
$car_1->honkHorn();
?>

 

In the above example I've touched on a few important things. 

 

1) Inheretance:

 

By having the Car class extend the vehicle we are saying that a car "is a" vehicle.  Think about that in concrete terms, if something A "is a" something B then it makes sense that something A can do the things that something B can and that something A shares the attributres of this something A.  This is true in our example.  A vehicle can start, stop and go.  A car therefore can start, stop, and go.  A vehicle has a color.  A car has a color.  Notice that a car can honk, but a vehicle cannot.  This logically makes holds ture, as a submarine is a vehicle and it cannot honk.  Therefore it makes sense for this behavior to be included only in the car class.

 

2) Composition *:

 

Our Car class wants to use some functionality defined in the Horn class.  It doesn't make sense extend the Horn class, the car is definitely not a horn, the car "has a" horn.  So what do we do?  We say that the car is composed of a horn.  By passing the horn object to the car object we are able to use the horn inside of the car without blurring the lines which clearly exist between the two objects.

 

 

Now let's look at what you're trying to do in concrete terms:

 

<?php
class ifc0nfig
{
public $func = NULL;
public $smarty = NULL;
public $MySQL = NULL;
public $navi = NULL;

function __construct( )
{
	require( ROOT_DIR . "functions.php" );
	$this->func = new func( );

	require( ROOT_DIR . "mysql.php" );
	$this->MySQL = new MySQL( "localhost", "12345", "12345", "12345" );

	require( ROOT_DIR . "navi.php" ) ;
	$this->navi = new navi( );

	// etc, etc
}
}
?>

 

 

You have an ifc0nfig class that is composed of a MySQL object and a navi object.  So the ifc0nfig "has a" MySQL connection and "has a" navi.  Then you say the navi class extends the ifco0nfig class so you assert that the navi "is a" ifc0nfig... hmmm.  This doesn't quite make sense.  You want to be able to use the MySQL class from within the navi class.  So the navi class should also "have a" MySQL object.  Let's swap some code.

 

 

<?php
class ifc0nfig
{
private $func = NULL;
private $smarty = NULL;
private $MySQL = NULL;

function __construct($MySQL)
{
	$this->MySQLL = $MySQL; 
        require( ROOT_DIR . "functions.php" );
	$this->func = new func( );
	// etc, etc
}
}

class navi
{
   private $MySQL = NULL;
   function __construct($MySQL)
   {
      $this->$MySQL = $MySQL;
   }
}

require( ROOT_DIR . "mysql.php" );
$MySQL = new MySQL( "localhost", "12345", "12345", "12345" );

// Create new ifc0nfig class which "has a" MySQL object.
$foo = new ifc0nfig($MySQL);

// now create a new navi class which "has a" MySQL object.
$bar = new navi($MySQL);

?>

 

By switching around the code a bit, we have allowed the two classes navi and ifc0nfig to be separate as they should be, but still able to act of the MySQL connection independently.  I hope this was of some help... and not too long winded...

 

Best,

 

Patrick

 

* (Disclaimer: I'm using composition loosely here, as a blanket term, as contextually the horn - car relationship is an example of aggregation)

 

 

Link to comment
Share on other sites

Daniel0: that works fine if I'm accessing $ifc0nfig->MySQL outside of a class, but if I'm in another class (navi) I cant access the MySQL object or even the ifc0nfig object (without class extends). The classes are in differant files (shouldn't matter?).

 

Jenk: Not sure if I follow you here but making $_mysql private isn't going to help at all? I want other classes to be able to access it.

You've missed the point entirely.

 

1. The navi object is constructed outside of your ifc0nfig class, thus not breaking encapsulation.

2. It is not an extension, it is a parameter.

3. You are free to pass around the navi object to other objects.

Link to comment
Share on other sites

utexas_pjm: thank you! Your post helped me alot :)

 

That code is fine, but say I want the navi class to access other classes, such as "session" and "logger". I'd have to initialize the classes and then do:

$navi = new navi( $MySQL , $session , $logger );

right? This could get a bit messy if my "session" class also wants to access the "MySQL" and "logger" class, and so on.

 

I've came up with a solution that works:

 

class ifc0nfig
{
var $func = null;
var $smarty = null;
var $MySQL = null;
var $navi = null;
}

 

class navi
{
var $ifc0nfig = null;

function __construct( &$ifc0nfig )
{
	$this->ifc0nfig =& $ifc0nfig;
}
}

 

and I initialize like so;

 

$ifc0nfig = new ifc0nfig( );
$ifc0nfig->func = new func( &$ifc0nfig );
$ifc0nfig->smarty = new Smarty( );
$ifc0nfig->MySQL = new MySQL( params );
$ifc0nfig->navi = new navi( &$ifc0nfig );

 

and then within a class, lets say the navi class, I can access the MySQL class with

$this->ifc0nfig->MySQL->some_method( );

 

Can anyone see anything wrong with this, any bad points etc?

Link to comment
Share on other sites

utexas_pjm: thank you! Your post helped me alot :)

 

That code is fine, but say I want the navi class to access other classes, such as "session" and "logger". I'd have to initialize the classes and then do:

$navi = new navi( $MySQL , $session , $logger );

right? This could get a bit messy if my "session" class also wants to access the "MySQL" and "logger" class, and so on.

 

I've came up with a solution that works:

 

class ifc0nfig
{
var $func = null;
var $smarty = null;
var $MySQL = null;
var $navi = null;
}

 

class navi
{
var $ifc0nfig = null;

function __construct( &$ifc0nfig )
{
	$this->ifc0nfig =& $ifc0nfig;
}
}

 

and I initialize like so;

 

$ifc0nfig = new ifc0nfig( );
$ifc0nfig->func = new func( &$ifc0nfig );
$ifc0nfig->smarty = new Smarty( );
$ifc0nfig->MySQL = new MySQL( params );
$ifc0nfig->navi = new navi( &$ifc0nfig );

 

and then within a class, lets say the navi class, I can access the MySQL class with

$this->ifc0nfig->MySQL->some_method( );

 

Can anyone see anything wrong with this, any bad points etc?

 

Better (IMO), less "messy":

 

 

index.php:

<?php
// Load registry
require_once 'registry.php';
$Registry =& Registry::get_instance();

// Require files
require_once 'navi.php';
require_once 'session.php';
require_once 'mysql.php';
//...

// Make objects
$Session	= new Session();
$MySQL		= new MySQL();
//...

// Register objects
$Registry->set('session', $Session);
$Registry->set('mysql', $MySQL);
//...

// Run...
$Navi = new Navi();
?>

 

registry.php:

<?php
class Registry
{
var $_objects = array();

function &get_instance()
{
	static $me;

	if(is_object($me))
	{
		return $me;
	}

	$me = new Registry();
	return $me;
}

function set($name, &$object)
{
	$this->_objects[$name] = $object;
}

function &get($name)
{
	return $this->_objects[$name];
}
}
?>

 

navi.php:

<?php
class Navi
{
var $Session;
var $MySQL;

function Navi()
{
	$Registry	=& Registry::get_instance();

	$this->Session	=& $Registry->get('session');
	$this->MySQL	=& $Registry->get('mysql');
}
}
?>

 

mysql.php, session.php, ...: Whatever you need in them.

 

 

Using the registry pattern is really great IMO. You don't need to pass objects around via arguments and you don't need to make objects global using the global keyword.

Link to comment
Share on other sites

Without trying to start up this cliché discussion, IMO, Registry is one of the examples where a Singleton is warranted. I agree that one should avoid having a multitude of Singletons (or statically - globally available methods in general) because it is just to tempting to use them scattered throughout your system. Which can create high interdependencies between the components in your system.

 

I feel that one should NOT pass around data through otherwise unrelated classes or layers, just to avoid using globally available data. In fact, IMHO that just shifts (or rather spreads out - like peanut butter on a sandwich) responsibilities.

 

If there is data that object A needs as well as object G, would you rather muddle responsibilities of objects between them by having them act as a relay for this data, or have the data globally available? I've once read an accurate handle for this practice: creating tramp data.

 

Having objects passed by other objects that don't need them could create what could be considered Spaghetti Code, implied by constructors' calling routine or not.

 

I would argue that some data simply NEEDS to be globally available, denying this fact can create the very thing you are trying avoid; high dependency between components in your system.

 

But as I said when I started this post, I agree that Singletons should be applied with caution. In fact, I only use one: the Registry. For other objects that I want to have only one one instance of; I have them implement a Registrable interface, declare their constructor private, and ensure they can not be instantiated directly without having them added to Registry.

 

I know, adding objects to a global Registry still makes them globally available (which is the point of a global Registry), put at least this ensures that they cannot be accessed, other than from the controlled confines of the Registry.

Link to comment
Share on other sites

Still solved by passing a registry instead..

 

<?php

$registry = new Registry;
// add stuff to $registry here

$controller = new Controller($registry);

// now within the controller..
public function doSomething()
{
    // add more stuff to the registry here, before using the following
    $this->_child = new SomeChildClass($this->_registry)
    $this->_child->run();
}

//now within the child..
public function run()
{
    $this->_view = $this->_registry->at('view');
    $this->_view->setModel($this->_registy->at('model'));
    $this->_view->display();
}
?>

crappy example, but explains the concept clearly.

Link to comment
Share on other sites

Like I said, you have a fault in your design if that is the case. Why are objects calling on objects that don't need parameters, yet the object below them need parameters? Remove the middle man if that is the case.

 

You can be witty (and trying to be insulting) and draw all the pictures you want, singleton's cause problems. Encapsulation is broken with any form of global. Why use objects if you are not going to use their interface? Might as well go back to procedural programming.

Link to comment
Share on other sites

Chill out, nobody's trying to be insulting. I just thought it would be more entertaining to illustrate the problem this way. Sorry you can't appreciate that.

 

There's a looong way to go from using a global Registry back to procedural. And nobody's denying that Singletons cause problems either (they can tempt you to firmly root a component into it's system, making it less reusable).

 

To avoid global data is a good guideline, nobody's arguing with that. Having said that, there are some considerations to be made. First of all, passing a Registry can be considered a responsibility. Requiring intermediators to pass this Registry gives them an extra responsibility. I know I don't have to tell you about the SRP. Furthermore, the receiving party becomes dependent on the passing party to an extra degree, namely on the fact that it correctly passes the registry (this goes right back to the SRP). Last but definitely not least, passing a Registry exposes all classes that come into contact with it to it's contents in the same degree that a global Registry would, nullifying your argument of broken encapsulation for the better part of the Model.

 

I don't quite agree with your top-down parameter story either. The fact is (pardon the absolutism) that more often than not you need to keep the objects in the higher layers unaware of details. Saying otherwise is turning the world up side down. A User object does not need to know nor care what DB object is used. A User- Data Mapper or DAO does care, since it needs it to be able serve data to User.

 

In my personal opinion, the only benefit to passing a Registry as an argument is that it is transparent by it's appearance in the calling routine of a constructor, but I don't have to tell you that is no substitution for proper documentation.

 

Now don't get mad at me for telling it how I see it, there is not one right way unless you're a Christian and quoting the bible. Maybe you should be a little less caught up with OOD heuristics and start thinking for yourself.

 

btw:

 

Like I said, you have a fault in your design if that is the case.

 

You never said anything of the sort.

Link to comment
Share on other sites

If I may chime in...

 

I Typically try not to pass a registry to my domain objects or have them make calls to global registry.  My philosophy is that domain objects should be able to function regardless of the application layer (which I consider the registry to be part of).  My registry is populated and passed in as an argument to the Controller super class and contained as member data.  When a derived controller needs to instantiate a domain object it passes the data from the registry to the object, instead of the registry all together.

 

<?php
abstract class Controller
{
  protected $registry;
  public function __construct(Registry $reg)
  {
    $this->registry = $reg;
  }
}

class SomeController extends Controller
{
   public function processSomeRequest()
   {
       $someObj = new Foo($this->registry->get('foo'), $this->registry->get('bar'));
        $this->doSomethingWith($someObj);
   }
}
?>

 

Best,

 

Patrick

 

P.S.

 

448191 -- best example ever.  haha.

Link to comment
Share on other sites

The point goes beyond the Domain Model. After posting that I realized that it was probably not the best example example of the problem. In fact, I was a bit disappointed in Jenk for not noticing and banging me down for it.  :P

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.