SitePoint Sponsor

User Tag List

Results 1 to 6 of 6
  1. #1
    SitePoint Enthusiast Remy's Avatar
    Join Date
    Oct 2002
    Location
    Amsterdam
    Posts
    47
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Question How to change a object dynamicly

    The problem that I have is that I have a terminal (that belongs to a merchant) but the terminal can change to a different kind of terminal.

    The base of the terminal is always the same (its the same hardware and software), but typeB can have different or added functions (or has methods that are implented differently) as typeA.

    I thought of to ways to build the classes for a terminal-object. Just create a abstract terminal-class and extend this with the needed subclasses\types of terminals. The other solution I came up with is a simplefied decorator-pattern, like this:
    PHP Code:
    // "Base Terminalclass (partly abstract)"
      
    class TerminalBase
      
    {
          
    // Methods
          
    function TerminalBase($terminalId) {..some code...}
          function 
    deleteProduct($productId) {..some code...}
          function 
    getType() {...};
          function 
    setType($type) {...};
          function 
    addProduct($productId)
          {
              die(
    'Method <b>addProduct</b> is not implemented.');
          }
      }
      
      
    // "abstract Decorator for Terminal"
      
    class Decorator extends TerminalBase
      
    {
          var 
    $terminal;
      
          function 
    Decorator(&$Terminal)
          {
              
    $this->terminal =& $Terminal;
          }
          
          function 
    addProduct($productId)
          {
              
    $this->terminal->addProduct($productId);
          }
          
          function 
    deleteProduct($productId)
          {
              
    $this->terminal->deleteProduct($productId)
          }
      
          function 
    getType()
          {
              
    $this->terminal->getType();
          }
          
          function 
    setType($type)
          {
              
    $this->terminal->setType($type);
          }
      }
      
      
    // "ConcreteDecoratorA"
      
    class TerminalTypeA extends Decorator
      
    {
          function 
    TerminalTypeA(&$Terminal)
          {
              
    parent::Decorator($Terminal);
          }
      
          function 
    addProduct($productId)
          {
              
    //...some override code...
          
    }
      }
      
      
    // "ConcreteDecoratorB"
      
    class TerminalTypeB extends Decorator
      
    {
          function 
    TerminalTypeB(&$Terminal)
          {
              
    parent::Decorator($Terminal);
          }
      
          function 
    addProduct($productId)
          {
              
    //...some override code...
          
    }
          
          function 
    newMethod() {..some code...}
      } 
    What do you think is better? The default abstract-extends one or the simplefied (if thats alowed ) decorator-pattern? Or are there other ways to do it?

    But this is not my real problem, The problem is how do I change it... What I am looking for is a API like this:
    PHP Code:
    $terminal = new Terminal(122); //type is A
      
    $terminal->doSomething(); //do something on a typeA terminal
      
    $terminal->setType('TypeB'); // change to typeB
      
    $terminal->doSomething(); // do something on a typeB terminal 
    So the object changes the class its based on...

    The only thing I can think of is to let another object to change the terminaltype, for example in a merchant-class:
    PHP Code:
    class Merchant
      
    {
          function &
    getTerminal($terminalId)
          {
              
    $terminal =& new TerminalBase($terminalId);
              switch (
    $terminal->getType())
              {
                  case 
    'TypeA':
                     return new 
    TerminalTypeA($terminal);
                      break;
                  case 
    'TypeB':
                     return new 
    TerminalTypeA($terminal);
                      break;
                }
          }
          function &
    changeTerminalType(&$terminal$type)
          {
              switch (
    $type)
              {
                  case 
    'TypeA':
                      
    $terminal->setType('TypeA');
                     return new 
    TerminalTypeA($terminal);
                      break;
                  case 
    'TypeB':
                      
    $terminal->setType('TypeB');
                     return new 
    TerminalTypeB($terminal);
                      break;
                }
          }
          
    // ....more code....
      

    and using it like this:
    PHP Code:
    $terminal =& $merchant::getTerminal(1223);
      echo 
    $terminal->getType(); // echo's TypeA
      
    $terminal->addProduct(15); // add product using ConcreteDecoratorA
      
      
    $terminal =& $merchant::changeTerminalType($terminal'TypeA');
      echo 
    $terminal->getType(); // echo's TypeB
     
    $terminal->addProduct(16); // add product using ConcreteDecoratorB 
    but it isn't the API I am looking for... (notice that a terminal-object can find out by itself which type it is)

    I am now thinking of making a one terminal-class with switch-statements in some methods like:
    PHP Code:
        function addProduct ($productId) {
              switch(
    $this->getType())
              {
                  case 
    'typeA':
                      
    // add product for typeA
                      
    break;
                  case 
    'typeB':
                      
    // add product for typeB
                      
    break;
                }
          } 
    There are now 4 terminaltypes, but in the future this can become possible more and the switch-statement is going to grow rapidly, so this is not a good solution...

    Are there other better solutions to solve this?
    Last edited by Remy; Jan 5, 2004 at 04:12.

  2. #2
    SitePoint Wizard holmescreek's Avatar
    Join Date
    Mar 2001
    Location
    Northwest Florida
    Posts
    1,707
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think you would be better off creating the base class for Terminal X, then creating abstract classes as you stated. This would eliminate having to perform a select on each class type. In addition, you could create an additional class called MACHINE as a container to hold and deal with all the instances of TerminalX that you create. Thus, in theory you could create a method(s) in MACHINE for instance TermTypeCtoA that would create a new instance of TermTypeA, copy the data from instance ot TermTypeC to TermType A, then kill the original instance of TermTypeC. Pobibly using an array, within MACHINE, to manage the instances created of all terminal types.




    Quote Originally Posted by Remy
    The problem that I have is that I have a terminal (that belongs to a merchant) but the terminal can change to a different kind of terminal.

    The base of the terminal is always the same (its the same hardware and software), but typeB can have different or added functions (or has methods that are implented differently) as typeA.

  3. #3
    eschew sesquipedalians silver trophy sweatje's Avatar
    Join Date
    Jun 2003
    Location
    Iowa, USA
    Posts
    3,749
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Sounds like perhaps a strategy pattern to represent the varius terminal types?
    Jason Sweat ZCE - jsweat_php@yahoo.com
    Book: PHP Patterns
    Good Stuff: SimpleTest PHPUnit FireFox ADOdb YUI
    Detestable (adjective): software that isn't testable.

  4. #4
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Problem: You need to add a new method to a hierarchy of classes, but the act of adding it
    will be painful or damaging to the design.


    This is a common problem. For example, suppose you have a hierarchy of Modem
    objects. The base class has the generic methods common to all modems. The derivatives
    represent the drivers for many different modem manufacturers and types. Suppose also
    that you have a requirement to add a new method, named configureForUnix, to the
    hierarchy. This method will configure the modem to work with the UNIX operating system.
    It will do something different in each modem derivative, because each different
    modem has its own particular idiosyncrasies for setting its configuration, and dealing with
    UNIX.
    Unfortunately adding configureForUnix begs a terrible set of questions. What
    aboutWindows, what about MacOs, what about Linux? Must we really add a new method
    to the Modem hierarchy for every new operating system that we use? Clearly this is ugly.
    We’ll never be able to close the Modem interface. Every time a new operating system
    comes along we’ll have to change that interface and redeploy all the modem software.
    A quotation from a PDF I have from a Java perspective, though I'm now thinking that the 'Visitor' Pattern as quoted above would help in this case folks ?

    Umm...

    For those interested I've attached the said PDF file to this post, hope it helps anyway

  5. #5
    SitePoint Enthusiast Remy's Avatar
    Join Date
    Oct 2002
    Location
    Amsterdam
    Posts
    47
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for the reply's!
    Quote Originally Posted by holmescreek
    I think you would be better off creating the base class for Terminal X, then creating abstract classes as you stated. This would eliminate having to perform a select on each class type.
    I don't understand what you mean by this . Which select on each class type do you mean? Keep in mind that I don't know the type of a terminal, the terminal itself knows this (the implentation of getType() is for al types the same.)
    Quote Originally Posted by holmescreek
    In addition, you could create an additional class called MACHINE as a container to hold and deal with all the instances of TerminalX that you create. Thus, in theory you could create a method(s) in MACHINE for instance TermTypeCtoA that would create a new instance of TermTypeA, copy the data from instance ot TermTypeC to TermType A, then kill the original instance of TermTypeC. Pobibly using an array, within MACHINE, to manage the instances created of all terminal types.
    That's a bit like mine example with the decorator-pattern. The merchant-class is the something like the MACHINE, but I only want one instance of a Terminal. I think mine example of the decorator is a much better solution (but I don't fully understand you solution, so I could be wrong.)
    Quote Originally Posted by Dr Livingston
    I'm now thinking that the 'Visitor' Pattern as quoted above would help in this case folks ?
    It think it's not as flexible solution for this problem. I tried it out, but it doesn't feel 'right'. Then I take a look at the definition:
    Quote Originally Posted by GoF
    Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.

    and the consquences:
    Quote Originally Posted by GoF
    Quote Originally Posted by GoF
    1. Visitor makes adding new operations easy;
    2. A visitor gathers related operations and separates unrelated ones;
    3. Adding new ConcreteElement classes is hard.
    As you can see the Visitor-problem is al about adding new (related) operation(methods) and thats not the problem (it's only a part small part of the problem). Consquence 3 is exactly the oppisite i want to accomplish. So I think it's not wise to use these pattern to solve this problem. Thanks for the pdf btw!

    Quote Originally Posted by sweatje
    Sounds like perhaps a strategy pattern to represent the varius terminal types?
    Yes, that sounds good to me too . After much thinking about it, I think the State pattern (looks alot like a Strategy pattern) sounds even better, looking at the definition:
    Quote Originally Posted by GoF
    Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
    Seeing a terminaltype change as a state. And when the state(aka terminaltype) changes, the class which the object is based on changes. I think this is the solution. What do you think?

    -Rémy

  6. #6
    eschew sesquipedalians silver trophy sweatje's Avatar
    Join Date
    Jun 2003
    Location
    Iowa, USA
    Posts
    3,749
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Remy
    Yes, that sounds good to me too . After much thinking about it, I think the State pattern (looks alot like a Strategy pattern) sounds even better, looking at the definition:
    Seeing a terminaltype change as a state. And when the state(aka terminaltype) changes, the class which the object is based on changes. I think this is the solution. What do you think?
    Sounds like State should work even better for you. I am not as familiar with the State pattern, but I have used Strategy before. To a man with a hammer...


Bookmarks

Posting Permissions

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