Reputation: 711
Would somebody out there have any suggestions as to how I could rearrange this code to get the desired output working properly? I'd like child classes to be able to update any parent class variables so that the next child class within the loop has the updated variable.
class Foo {
protected $x = 0;
public function __construct() {
}
public function run() {
foreach (array(1, 2) as $id) {
$class_name = 'Bar' . $id;
$b = new $class_name();
$b->add();
}
}
public function getX() {
return $this->x;
}
}
class Bar1 extends Foo {
public function __construct() {
parent::__construct();
}
public function add() {
$this->x++;
}
}
class Bar2 extends Foo {
public function __construct() {
parent::__construct();
}
public function add() {
$this->x++;
}
}
$f = new Foo();
$f->run();
echo $f->getX(); # desired output should be 2
UPDATE
I'm sorry for this confusing question but I'm having troubles even figuring out how to ask it.. My real issue is that I have a very very large, ever growing, class that I would like to break each method out into its own class. Here is a small sample of the classes that I'm currently working with. The parent class does not need to have access to the updated variable, just the child classes should and in this example they do. But that's not what I'm trying to achieve. Well I am, but just with individual child classes.
class Foo {
protected $x = 0;
public function __construct() {
}
public function run() {
$b = new Bar();
foreach (array(1, 2) as $id) {
$method_name = 'Bar' . $id;
$b->$method_name();
}
echo $b->getX(); # outputs 2
}
public function getX() {
return $this->x;
}
}
class Bar extends Foo {
public function __construct() {
parent::__construct();
}
public function Bar1() {
$this->x++;
}
public function Bar2() {
$this->x++;
}
# thousands of lines more and ever growing... 7,500+
}
UPDATE 2
Here's another version that does work, but just seems strange to me.. Would this be better than the static version?
class Foo {
public $x = 0;
public function __construct() {
}
public function run() {
foreach (array(1, 2) as $id) {
$class_name = 'Bar' . $id;
$c = new $class_name($this);
$c->add();
}
}
public function getX() {
return $this->x;
}
}
class Bar1 extends Foo {
private $foo;
public function __construct($foo) {
$this->foo = $foo;
}
public function add() {
$this->foo->x++;
}
}
class Bar2 extends Foo {
private $foo;
public function __construct($foo) {
$this->foo = $foo;
}
public function add() {
$this->foo->x++;
}
}
$f = new Foo();
$f->run();
echo $f->getX(); # outputs 2
Upvotes: 0
Views: 52
Reputation: 7485
I'm guessing that you have many different ways to do some kind of calculation.
How about having one object that does the calculations for each calculator?
<?php
interface Calculator {
public function getTotal();
public function calculate();
}
class PlusOne implements Calculator
{
protected $x;
public function __construct($x)
{
$this->x = $x;
}
public function getTotal()
{
return $this->x;
}
public function calculate()
{
$this->x++;
}
}
class PlusTwo extends PlusOne
{
public function calculate()
{
$this->x = $this->x + 2;
}
}
class ItemsCalculator implements Calculator
{
protected $x = 0;
protected $items;
public function __construct($items)
{
$this->items = $items;
}
public function getTotal()
{
return $this->x;
}
public function calculate()
{
foreach($this->items as $item) {
$item->calculate();
$this->x = $this->x + $item->getTotal();
}
}
}
$itemsCalculator = new ItemsCalculator([new PlusOne(0), new PlusTwo(0)]);
$itemsCalculator->calculate();
echo $itemsCalculator->getTotal();
Outputs:
3
Alternatively you could pass a total instance to each calculator:
class Total
{
public $x;
}
class Plus1
{
protected $total;
public function __construct(Total $total)
{
$this->total = $total;
}
public function calculate()
{
$this->total->x++;
}
}
class Plus2 extends Plus1
{
public function calculate()
{
$this->total->x = $this->total->x + 2;
}
}
Upvotes: 0
Reputation: 76408
The thing you're missing is simple: a class is a blueprint, properties you declare in a class don't exist as part of the class, but rather: they exist inside the individual instances you create.
the $x
property that exists in Foo
is completely independent of any number of properties that exist in Bar
, Bar2
or any other instance of Foo
that might exist at any given time.
If you want to share state, you need something that isn't tied to the lifecycle of an instance. Something that exists before an instance is created, and doesn't disappear after an instance is GC'ed. Basically, you're looking for a static
property.
class Foo
{
protected static $x = 0;
public function run()
{
foreach (array(1, 2) as $id) {
$class_name = 'Bar' . $id;
$b = new $class_name();
$b->add();
}
}
public function getX()
{
return static::$x;
}
}
class Bar1 extends Foo
{
public function add()
{
static::$x++;
}
}
class Bar2 extends Bar1
{}//no need to re-implement add...
That will work
Mind you, relying on statics is generally considered bad practice. Statics introduce global state, and in a case like this, they'll almost always cause you grief in the end... consider this:
function runFoo($times = 1)
{
$foo = new Foo;
while ($times-- > 0) {
$foo->run();
}
return $foo->getX();
}
$first = runFoo();
$second = runFoo(2);
$third = runFoo(123);
Now without running this, try and predict the output...
Upvotes: 1
Reputation: 383
Maybe I didn't understand your question, but you cannot increment the $x from the main class by instanciating children, because they have their own $x.
I mean, each child you create starts with $this->x == 0, and end up with $this->x == 1 (for one call to add() ), and will never change the attributes of the OBJECT which creates them in the loop.
Upvotes: 0