Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 5 of 5
  1. #1
    Senior Coder
    Join Date
    Jun 2008
    Location
    New Jersey
    Posts
    2,536
    Thanks
    45
    Thanked 259 Times in 256 Posts

    OOP: "Degrading" classes

    I'm not even sure if this makes sense, but...

    Lets say I make a class called "person" and then extend it into a class called "employee", with the idea that the employee class is for someone who is currently employed. I give the employee class extra class variables and methods that are necessary to reflect/control their extra information.

    When a person loses a job, is there a way to degrade an object of type 'employee' to an object of type 'person', thus losing the extended stuff and only keeping the data from the parent class? Does it even make sense, from a programming point of view? Is this a logical concept that doesn't apply into programming?

    EDIT: by extension, can you "upgrade" an object that's already been created?
    Last edited by Keleth; 06-18-2012 at 05:39 PM.

  • #2
    God Emperor Fou-Lu's Avatar
    Join Date
    Sep 2002
    Location
    Saskatoon, Saskatchewan
    Posts
    16,987
    Thanks
    4
    Thanked 2,660 Times in 2,629 Posts
    Yes. Except in PHP this is somewhat complicated since it is datatype weak.
    In languages like Java and C#, you simply force cast it:
    PHP Code:
    public class Person
    {
        public 
    String sName;
    }

    public class 
    Employee extends Person
    {
        public static 
    void main(String... argv)
        {
            
    Employee e = new Employee();
            
    e.sName "Fou-Lu";
            
    Person p = (Person)e;
            
    System.out.println(p.sName " is of type: " p.getClass().getSimpleName());
        }

    That will show that Fou-Lu is of type Person.

    PHP on the other hand doesn't have castable object types. So what you do instead is write cast handlers:
    PHP Code:
    class Person
    {
        private 
    $sName;

        public function 
    __construct($sName "")
        {
            
    $this->sName $sName;
        }

        public static function 
    cast(Person $p)
        {
            
    $r = new self($p->getName());
            
    // Maybe do other stuff depending on complexity of object
            
    return $r;    
        }

        public function 
    getName()
        {
            return 
    $this->sName;
        }
    }

    class 
    Employee extends Person
    {
        public function 
    fire()
        {
            return 
    parent::cast($this);
        }
    }

    $e = new Employee("Fou-Lu");
    $e $e->fire();
    var_dump($e); 
    The much better approach is to use the decorator instead which effectively promotes objects of different types into a usable type, but you can unwrap as desired.

    PHP Code:
    interface IPerson
    {
        public function 
    getName();
    }

    class 
    Person implements IPerson
    {
        private 
    $sName;

        public function 
    __construct($sName "")
        {
            
    $this->sName $sName;
        }

        public function 
    getName()
        {
            return 
    $this->sName;
        }
    }

    class 
    Employee implements IPerson
    {
        private 
    $person;
        public function 
    __construct(IPerson $p)
        {
            
    $this->person $p;
        }

        public function 
    getName()
        {
            return 
    $this->person->getName();
        }
    }

    $p = new Person("Fou-Lu");
    var_dump($p);

    $e = new Employee($p);
    var_dump($e); 
    Employee and Person are both of type IPerson. The new datatype to work with for typehinting is IPerson and not Person. This way Employee and Person are distinct objects that share a common interface instead of common base (and btw, interfacing is about 10x more powerful than extending concrete classes could ever hope to be).

  • Users who have thanked Fou-Lu for this post:

    Keleth (06-18-2012)

  • #3
    Senior Coder
    Join Date
    Jun 2008
    Location
    New Jersey
    Posts
    2,536
    Thanks
    45
    Thanked 259 Times in 256 Posts
    I'm not even gonna pretend I understand that well enough to implement, but you've given me plenty to read on!

    I think I do understand the "upgrading" notion... rather then extending, and thus having a set of properties specific to that class instance, you're importing the object itself into the "child" class, and then creating the references as if it was a child class. I'm not really sure I get why interfacing is more powerful then extending though, could you elaborate? I figure since its a looser structure, its weaker, no?

    And for "degrading", the cast handler you refer to... does it have to be called cast? Is that a naming convention, or an internal thing for PHP? Is it safe to deduce that what it does is kind of like a secondary "constructor", in that by calling the parent::cast and passing the current child object, you then take the properties of the child object, create a version of the parent object that would utilize whatever properties the parent object needs, and then returns that new object? Thus why you assign it back to itself (and since, like you said, PHP is loosely typed, it just takes it and accepts that its a whole different object type).

  • #4
    God Emperor Fou-Lu's Avatar
    Join Date
    Sep 2002
    Location
    Saskatoon, Saskatchewan
    Posts
    16,987
    Thanks
    4
    Thanked 2,660 Times in 2,629 Posts
    You have it backwards. The interfaces are a tighter structure than the extends, since extends provide the same functionality of the parent should the child not override. Because of this implicit chain, sometimes the intended functionality is overlooked when an override should have been written. Interfaces contract a class and force it to implement the method, so it becomes a tighter couple to the object in question. There is a bug in the zend core though that allows you to decay the scope of a contract, and I do recall testing this in beta 6, and it was corrected (it may be corrected in an up to date version of 5.3 or 5.4 as well, last tested on a 5.2.17):
    PHP Code:
    <?php

    interface IInterface
    {
        public function 
    idostuff();
    }

    class 
    Grandfather
    {
        private function 
    idostuff()
        {
            return 
    __METHOD__;
        }
    }

    class 
    Father extends Grandfather implements IInterface
    {
        public function 
    idostuff()
        {
            return 
    __METHOD__;
        }
    }

    class 
    Son extends Father
    {
        private function 
    idostuff()
        {
            return 
    __METHOD__;
        }
    }

    $s = new Son();
    var_dump($s);
    $s->idostuff();
    Should result in:
    Code:
    Fatal error: Access level to Son::idostuff() must be public (as in class Father) on line 24
    But instead results in
    Code:
    Fatal error: Call to private method Son::idostuff() from context '' on line 34
    This is actually a major bug since the engine should be throwing this at link time and not at run time. This means you can get intermittent failures on calls that shouldn't occur.

    The cast method name is whatever you want it to be. PHP internal magics will always prepend the method with __ first. I'm still holding out for a __cast method in the future for which allows me to use (ObjectType) cast. And yeah, its written like a constructor. PHP doesn't allow signature overloads, so you can't use a real constructor without some heavy input analysis on it first. This way is just easier. It works since the Employee is already of type person, so you know that it has the variable available to retrieve it. If its not typehinted, you have to draw the object type off of it with an instanceof check first, which used to be kinda complicated with the old binding, but I think that's all been fixed up since early 5.2 or 5.3 version. Since you cannot reassign $this in an object, this is really the only way to do it.

  • Users who have thanked Fou-Lu for this post:

    Keleth (06-18-2012)

  • #5
    Senior Coder
    Join Date
    Jun 2008
    Location
    New Jersey
    Posts
    2,536
    Thanks
    45
    Thanked 259 Times in 256 Posts
    Very cool. I'll have to play and learn more.

    So far most of my projects have been pretty straight forward, so I've been at a loss for why I'd use objects, but I think I'm going to build objects even if just to do simple things, to push my learning.

    Plus, often building a query to save data that can come from a few diff places can be complex. I figure if I build a class to take the data in, then I can just write a method that will write or do the query for me.

    It actually brings up a secondary related question, but I'll post it as its own thread.


  •  

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •