1

I am trying to inherit a set of different parent class and even not inherit any class but as per some condition. For example, Here is what I would like to do

$choice = 2;
switch($choice) {
     case 1: 
         class child extends parent1
         break;
     case 2: 
         class child extends parent2
         break;
     default:
         class child 
         //extend nothing
         break;
    }

I think you can figure out what I am trying achieve here.

parent classes

class car { }

Child classes

class ferari extends car { }
class ford extends car { }

grandchild classes

class automaticcar { }
class manualcar { }

Now this grandclasses need to interit a parent as per the value sent from the form using post. Something like this

$brand = $_POST['brand'];

if(isset($brand) && !empty($brand)) {
  class automaticcar extends $brand 
}
else {
  class automaticcar extends car  //or even nothing
}

//And then I wish to write the remaining portion of the class

mrN
  • 3,734
  • 15
  • 58
  • 82
  • 3
    You should not need to do something like this. Can you describe what you are actually trying to model and maybe we can figure out a better solution than 'conditional inheritance' – Rohith Sep 20 '10 at 07:54
  • I simply want to be able to inherit different parent classes, according to some condition. – mrN Sep 20 '10 at 07:59
  • Without knowing your problem this is hard to solve. There will be a design pattern that will help you out here. Without knowing what you are doing it is hard to say what patten will help you – Jake N Sep 20 '10 at 08:01
  • ...and why do you want to do that? What is the goal you're trying to achieve with "inherit different parent classes, according to some condition"? – VolkerK Sep 20 '10 at 08:01
  • Ok, I will try to explain you the case, see the update – mrN Sep 20 '10 at 08:05
  • 2
    An actual, concrete example would be better. The example you've posted could e.g. be solved by having a class `Car` that _has_ a `Gear` property which then reveals itself as either an instance of `ManualGear` or an instance of `AutomaticGear`, thus eliminating the `manualcar`/`automaticcar` inheritance level. – VolkerK Sep 20 '10 at 08:15
  • 1
    You might be interested in the [AbstractFactory](http://sourcemaking.com/design_patterns/abstract_factory) and [Builder](http://sourcemaking.com/design_patterns/builder) patterns. Also, I doubt a Ferrari should extend Car. Subclassing means specializing and Ferrari is nothing but a manufacturer. A Ferrari has *different values* for car attributes, but it doesnt have *other* attributes. – Gordon Sep 20 '10 at 08:49

5 Answers5

5

The kind of inheritance you are trying to obtain is not attainable using a language which inheritance is based on classes.

The closer solution you can obtain is using a sort of decorator pattern with a bit of magic methods. something like this:

$choice = 2;
switch($choice) {
     case 1: 
         $obj = new child(new parent1());
         break;
     case 2: 
         $obj = new child(new  parent2());
         break;
     default:
         //extend nothing
         $obj = new child();
         break;
    }

with child being similar to this:

class child {
    function __construct($composeObj) {
        $this->core = $composeObj;
        // wathever you need to iniyialize
    }
    function __call($name, $params) {
        return call_user_func(array($sthis->core, $name), $parameters);
    }
    // other functions
} // eo child

This solution have some caveats but if you can cope with them (the object does not belongs to the family of the composited object, call_user_func does not pass the parameters by reference) this is a suitable solution.

A better solution is the factory approach suggested by sandeepan but you already refused it.

Eineki
  • 14,773
  • 6
  • 50
  • 59
1

A Ferrari is not different to a Ford in the properties or methods it supplies. They are both still cars. They just have different values for their attributes. Creating a spezialized subclass shouldn't be necessary for that. Instead try this route:

class Car
{
    protected $_manufacturer;
    protected $_engine;
    protected $_gear;
    protected $_price;
    protected $_color;

    public function __construct($manufacturer, $engine, $gear, $price, $color)
    {
        // setting properties
    }
}

class CarFactory
{
    public static function createFerrari()
    {
        $engine = new Engine(500); // PS
        $gear = new Gear(6, 'manual');
        return new Car('Ferrari', $engine, $gear, '250000', 'red');
    }

    public static function createFord()
    {
        $engine = new Engine(90); // PS
        $gear = new Gear(5, 'automatic');
        return new Car('Ford', $engine, $gear, '50000', 'black');
    }

    // other car creation methods ...
}

If you extend a class, you are creating an is-a relationship. The subclass is a specialized parent class. Gear and Engine is nothing a Car is, but something a car has. Whenever you can describe a class to have something, it's a candidate for it's own class or for just being an attribute. Whether it should be it's own class depends on whether the thing encapsulated own unique state and responsibiliy.

Gordon
  • 312,688
  • 75
  • 539
  • 559
  • That was a real life example, trying to express what I need. I just want to be able to inherit different but single parent classes as per conditions. – mrN Sep 20 '10 at 09:39
  • @MrNepal that doesnt sound feasible. Either a subclass *is-a* specialization of parent class or it's not. A Ferrari doesn't stop being a Car because of some condition. That's illogical. Please try to update your question with more details. – Gordon Sep 20 '10 at 10:01
  • @mrNepal: "I just want" - so that's the only reason? ;-) Seriously, there isn't a keyword or language-construct that let's you do what you've explained and the example is better solved differently. – VolkerK Sep 20 '10 at 10:05
0

Is your question "Condition based inheritance good?" Then yes it looks necessary in many cases. But I think it would be better to initiate objects conditionally instead of defining extended classes inside condition.

Updates
As far as I understand you want to have different attributes/functions of the child class depending on condition. I faced a similar need/problem in my project. There it was relating to view logic. You may check How is my approach to reuse view logic in my project?

If I understand your problem correctly, then you should have the child classes ready beforehand like this in separate php files:-

child1 class

class1 child extends parent1

child2 class

class2 child extends parent2

And in the condition part do something like:-

$choice = 2;
switch($choice) {
     case 1: 
         include /path/to/child1;
         $obj = new child1();
         break;
     case 2: 
         include /path/to/child2;
         $obj = new child2();
         break;
     default:
         include /path/to/child;
         $obj = new child();
         //extend nothing
         break;
    }
Community
  • 1
  • 1
Sandeepan Nath
  • 9,966
  • 17
  • 86
  • 144
  • no no, I want same child to inherit different parent according to cases. – mrN Sep 20 '10 at 07:58
  • Are you using a MVC architecture? In what context are you using this class? What code is there inside your child and parent classes? – Sandeepan Nath Sep 20 '10 at 08:03
0

First off, I really don't think you understand object oriented principles well enough to ask for this functionality. It's not really needed with the style of OOP that PHP implements.

You are requesting something like conditional mix-ins. It's possible to implement it, but it is a huge kludge and should be avoided. Here's something I put together a while ago when I was just testing some concepts:

<?php
    class Mixin
    {
        private $objects = array();
        private $funcs = array();

        public function addMixin(Mixable $object)
        {
            $exported_vars = $object->exportVars();
            foreach ($exported_vars as $key => &$ref)
                $this->$key = &$ref;

            $vars = array();
            foreach (array_keys(get_object_vars($this)) as $key)
                $vars[$key] = &$this->$key;
            $object->importVars($vars);

            $this->objects[] = $object;
        }

        public function __call($method, $args)
        {
            if (!isset($this->funcs[$method]))
            {
                $found = false;
                foreach ($this->objects as $obj)
                {
                    if (method_exists($obj, $method))
                    {
                        $found = true;
                        $this->funcs[$method] = array($obj, $method);
                        break;
                    }
                }

                if (!$found)
                    throw new Exception("method doesn't exist");
            }

            return call_user_func_array($this->funcs[$method], $args);
        }
    }

    class Mixable
    {
        public function exportVars()
        {
            $vars = array();

            foreach (array_keys(get_object_vars($this)) as $key)
            {
                $vars[$key] = &$this->$key;
            }

            return $vars;
        }

        public function importVars($vars)
        {
            foreach ($vars as $key => &$ref)
            {
                $this->$key = &$ref;
            }
        }
    }
?>

You would use it like:

<?php    
    class Parent1 extends Mixable
    {
        protected $name = 'Parent 1';

        public function p1()
        {
            print "P1\n";
        }
    }

    class Parent2 extends Mixable
    {
        protected $name = 'Parent 2';

        public function p2()
        {
            print "P2\n";
        }
    }

    class Child1 extends Mixin
    {
        public function whoAmI()
        {
            print $this->name."\n";
        }
    }

    $foo = new Child1();
    if (mt_rand(1, 2) == 1)
    {
        $foo->addMixin(new Parent1());
        $foo->p1();
    }
    else
    {
        $foo->addMixin(new Parent2());
        $foo->p2();
    }

    $foo->whoAmI();
?>

Please do not try to use the code! Even if it were production ready, it's a terrible concept. I put it here to show you how it would work.

I think what you really should be doing is something more like a Factory pattern: build a CarFactory class that returns a properly subclassed Car. (Or you could create a factory method within a Car class.)

It could be something like Car::get($_POST['brand']).

Matthew
  • 47,584
  • 11
  • 86
  • 98
0

I know I'm almost SIX years late. But just in case someone lands here again, I decided to put it here.

Please note that I do not assume the implicit understanding that the class names Car, Ferrari, Ford etc are vehicles. So as to be able to apply this pattern in a generic fashion.

I'll use either of the following approaches in the decreasing order of preference:

  • Class alias PHP Manual (PHP 5 >= 5.3.0, PHP 7)

    // Assuming $my_brand is the required brand 
    if (!class_exists($my_brand))
    {
        // Raise suitable warning here and exit
        exit('Brand ' . $my_brand . 'not found.');
    }
    
    class_alias($my_brand, 'MyBrand');
    
    class AutomaticCar extends MyBrand
    {
    }
    
  • Conditional stacking of inheritance (Too much work, but useful in a larger size project)

    // File: lib/MyBrand/Ferrari.php
    class MyBrand extends Ferrari
    {
    }
    

    // File: lib/MyBrand/Ford.php
    class MyBrand extends Ford
    {
    }
    

    // Then in your main code, assuming $my_brand is the required brand 
    if (!file_exists('lib/MyBrand/' . $my_brand . '.php'))
    {
        // Raise suitable warning here and exit
        exit('Brand ' . $my_brand . 'not found.');
    }
    
    class AutomaticCar extends MyBrand
    {
    }
    

Other patterns that I can right now (such as Factory Design and Decorator Pattern) think of would take different routes which doesn't comply with the exact topic. So thats all for now.

Izhar Aazmi
  • 915
  • 12
  • 25