Page 1 of 2

OO Design issues

Posted: Fri Jun 01, 2007 7:11 am
by vigge89
I just ran into a couple of problems while starting to practice with OOP, consider the following code:

Code: Select all

class db {
    function query($sql) { ... }
}

class second_class {
    var $third_class;
    function second_class() {
        $this->third_class= new third_class();
    }
    function someFunction ($vars) {
        // do some queries
        $db->query("UPDATE ....");
   }
}

class third_class{
    function doSomething($something) {
        // lines of code
        if (is_file($something))
                $data = file_get_contents ($something)
        else
                $query = $db->query("SELECT * FROM table WHERE id='$something'");
                $data = $query->fetch();
        // lines of code
    }
}

$db = new db();
$class = new second_class();
$class->third_class->doSomething();
First, how am I supposed to create a relation between the db class and the second_class (an instance of db will be required for second_class to operate, and will often be used by third_class), should I create an instance of db inside the second_class or outside it in the global scope, and how would I call it "correctly" from both the second and third classes? In the third_class, should I check if $db has been instanced and proceed with querying it if it has, and if so, how would I do this?

I've been looking around the web and on PHPDN but have had a hard time finding resources (articles/guides/etc.), so if anyone of you have some good links, I'd be happy to take a look.

Posted: Fri Jun 01, 2007 7:18 am
by superdezign

Code: Select all

class FirstClass
{
    function Query($sql);
}

class SecondClass
{
    var $pFirstClass;

    function SecondClass($pFirstClass)
    {
        $this->pFirstClass = $pFirstClass;
    }
}

$first = new FirstClass();
$second = new SecondClass($first);

Posted: Fri Jun 01, 2007 7:25 am
by feyd
One of the easy ways to determine how to generally couple two classes is to use the "is a" versus "has a."

An example:

"Is class xyz an instance of class abc?" or "Does class xyz have a instance of class abc?"
If the answer is true for the former, it angles toward inheritance. If it's the latter, it points toward composition and usage.

Posted: Tue Jun 05, 2007 10:37 am
by staar2
I did not want to make new topic so i few question about PHP 5. What for are interface and abstract? How can i use them and what is their point?

Posted: Tue Jun 05, 2007 11:43 am
by Christopher
staar2 wrote:What for are interface and abstract?
Interface define what methods a class must implement. Interfaces contain no code. If the methods are not implemented then you will get an error message, so they inforce a specified interface.

Abstract classes contain code, but cannot be instantiated. They must be extended to create a normal class that can then be instantiated. Usually they contain base functionality, but leave some methods that must be implemented or overridden to create a complete class. So abstract classes force the programmer to finish the class.
staar2 wrote:How can i use them and what is their point?
I would recommend that you do not use them until you find a problem that cannot be solved without them.

Posted: Tue Jun 05, 2007 1:35 pm
by timvw
I've got an issue with abstract class.. In order to use them you have to inherit from to implement the abstract functions...

If you define all the abstract methods in an interface you can implement a regular class that accepts an instance that implements the interface and use that instance in all the places where you need it... This way your implementation can be independant of the class...

eg: (Don't focus on the syntax... I'm getting rusty :p)

Code: Select all

abstract class Foo
{
 abstract something();
}
vs

Code: Select all

class Foo
{
 private ISomething something;

 public Foo(ISomething $something)
 {
  $this->something = $something;
 }
}

interface ISomething
{
 something();
}

Posted: Tue Jun 05, 2007 1:57 pm
by TheMoose
timvw wrote:I've got an issue with abstract class.. In order to use them you have to inherit from to implement the abstract functions...

If you define all the abstract methods in an interface you can implement a regular class that accepts an instance that implements the interface and use that instance in all the places where you need it... This way your implementation can be independant of the class...
The point of an abstract class it to provide a class of common functions along with customizable functions that vary dependent upon the child class's constraints. Interfaces provide a common guideline of functions that the class must implement, but each implementation, although it may have the same function, requires different code to accomplish.

IE:

Code: Select all

abstract class Drink {
   function pour() {
      return "Man that was a tasty beverage!";
   }
   abstract function Open();
}
public class SodaCan extends Drink {
   function Open() {
      $this->tab->pull();
      return "You've popped the tab on me!";
   }
}
public class WineBottle extends Drink {
   function Open() {
      $this->cork->twist()->pop();
      return "You've popped my cork!";
   }
}
public class myDrink {
    function __construct($drink) {
      echo $drink->Open();
      echo $drink->Pour();
    }
}

$myDrink = new myDrink(new WineBottle());
$myDrink = new myDrink(new SodaCan());
Both WineBottle and SodaCan have a common function that only needs a single codebase to implement, Pour(), which is why it is in the abstract declaration. Open() requires different code to obtain the same result, hence it being declared abstract and needing separate code.

Interface:

Code: Select all

public interface IDrink {
   public function Open();
   public function Pour();
}
public class SodaCan implements IDrink {
   public function Open() {
       $this->tab->pop();
       return "I've been opened!"
   }
   public function Pour() {
       $this->can->grab()->chug();
   }
}
public class WineBottle implements IDrink {
   public function Open() {
       $this->cork->twist()->pull();
       return "I've been popped!";
   }
   public function Pour() {
       $this->bottle->tip();
   }
}
public class myDrink {
    function __construct($drink) {
      echo $drink->Open();
      echo $drink->Pour();
    }
}

$drink = new myDrink(new SodaCan());
$drink = new myDrink(new WineBottle());

Posted: Tue Jun 05, 2007 1:58 pm
by feyd
Yeah, I prefer interfaces too, but have used abstracts quite successfully in several occasions.

Posted: Tue Jun 05, 2007 2:26 pm
by Christopher
I have only found one or two cases where either interfaces or abstracts actually solved a problem better than not using them. The are really just enforcers and I find that they are very overused. I see programmers create an interface reflexively without ever having a problem enforcing an interface. Call them a Premature De-optimization.

Posted: Tue Jun 05, 2007 2:34 pm
by RobertGonzalez
Man, I am glad someone asked this question. I too was looking at the difference and use cases for interfaces and abstracts. I have been looking at various frameworks and codebases and I see some using them almost everywhere and others using them sparingly.

In my case, I was looking at their place in the View component of MVC and the abstraction layer of DB interaction. But I thought myself a little weird for not using them as much in the framework I am building. Though now, not so much.

I need to hang around T&D more often. These OOP discussions are getting really good as of late.

Posted: Tue Jun 05, 2007 2:42 pm
by TheMoose
In the central portion of MVC they're not really all that useful. There's not too much of a need to abstract classes outside of views and possibly the DB layer. Unless you plan on switching DB engines constantly, you don't even really need it for that. As for Views, I'd say it's readily useful for creating a common dataset for the view to use, regardless of where you got the data from, either file, logic, or DB.

Posted: Tue Jun 05, 2007 2:43 pm
by timvw
TheMoose wrote:The point of an abstract class it to provide a class of common functions along with customizable functions that vary dependent upon the child class's constraints.
- A major advantage (for me) is the fact that the implementation of the interface does not have to live with a 'IS-A' (name of abstract class).
- The only functional advantage of the abstract class i can imagine is the fact that the concrete implementation can access protected methods.

Eg: As in your example you can have drinks... And in order to consume a Drink a drink would need to know how to open itself... By doing this via abstract methods you implement something that can open drinks and that is a drink itself...

Using interface you can implement a class that is a DrinkOpener (and doesn't have to be a drink itself).

Code: Select all

// about opening a drink
interface IDrinkOpener
{
 function Open();
}

// a drink doesn't have to know how to open itself... That's the task of an IOpener
class Drink 
{
 IDrinkOpener $drinkOpener;

 function Drink(IDrinkOpener $drinkOpener) { $this->drinkOpener = $drinkOpener; }

 // implement functions that use the functionality of a DrinkOpener...
}

// Notice, this class is about the opening of SodaCans... NOT drinks 
class SodaCanOpener : IDrinkOpener
{
 function Open()
 {
  return "You've popped the tab on me'!";
 }
}

Posted: Tue Jun 05, 2007 2:51 pm
by timvw
arborint wrote:I have only found one or two cases where either interfaces or abstracts actually solved a problem better than not using them.
I can imagine that in the context of an average php project they're not really useful (but we already agree on that very long ago ;))

Last year i've been in a situation with teams that actually 'consume services' from each other, and in that situation i've really started to appreciate the advantages of programming against an interface instead of a concrete implementation :)

Posted: Tue Jun 05, 2007 2:53 pm
by RobertGonzalez
@timvw: I think you have been in C# too long. ;)

Posted: Tue Jun 05, 2007 3:08 pm
by TheMoose
Yes, he's been in the C world a bit ;)

@timvw
The only problem with the IDrinkOpener is that it adds an extra step to handle the opening of the drink. I could see why it could be necessary, as some drinks might implement the same style of opening (say a soda can, and a beer can, or a Heineken mini-keg, etc), vs a wine bottle and a non-alcohol corked bottle, or whatnot.

But with the method you added, you cannot create separate drinks because your Drink class is a single instantiation with concrete methods of its own. You'd have to either make it the parent class and extend children from it if you wanted separate types, ie a Wine Bottle might have different properties and methods than a Soda can does.