SitePoint Sponsor |
|
User Tag List
Results 1 to 16 of 16
-
Nov 19, 2004, 14:20 #1
- Join Date
- May 2001
- Posts
- 193
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Interface Vs. Abstract and Inheritance
In an attempt to understand interfaces, and abstract classes I downloaded creole and checked out their use of them.
I am confused!
I took a look at the interface, abstract, and derived classes for a Prepared Statement.
PreparedStatementCommon.php which is the abstract class contains about 28 methods of which only 1 is declared as abstract escape($str)
PreparedStatement.php is the interface class which declares 27 of the methods implemented in the abstract class.
MySQLPreparedStatement.php is a derived class which extends the abstract which implements the interface and implements the method escape().
My question is why bother with the abstract class in this example, why not just add the 1 abstract method to the interface, and make PreparedStatementCommon.php into a regular class.
Im having trouble seeing the value of the abstract class here.
Thanks for any input
Also, I was reading a another post and there was a link to an article talking about "Why extends is evil", sorry can't remember the thread. But the jist of it was don't use inheritance or avoid it at all costs, and opt for Interfaces instead. I don't understand, how do you acheive what extends gives you using an interface, since you can't implement anything in the interface. There was a quote saying somethng to the affect, "80 percent of your code should be wriiten in terms of interfaces...".
-
Nov 19, 2004, 14:52 #2
- Join Date
- Dec 2003
- Location
- Arizona
- Posts
- 411
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
What I got out of the "Extends is Evil" article is that the author was an advocate of interface inheritance over implementation inheritance.
PHP Code:// Base class
class A {
function a() {
}
function b() {
}
}
// Implementation Inheritance
class B extends A {
function a() {
}
function b() {
}
function c() {
}
}
// Interface Inheritance
interface A {
function a();
function b();
}
class B implements A {
function a() {
}
function b() {
}
}
// Interface Inheritance
class C implements A {
var $b;
function C() {
$this->b = new B();
}
function a() {
$this->b->a();
}
function b() {
$this->b->b();
}
function c() {
}
}
Here is an article, also from JavaWorld, on choosing between abstract classes and interfaces:
http://www.javaworld.com/javaworld/j...-abstract.html
Thanks,
JT
-
Nov 19, 2004, 15:10 #3
- Join Date
- May 2001
- Posts
- 193
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Yes, I got the same thing from that article, but it seems to me it's not an either/or type of choice. I mean, if I have some common methods for a base class, I need to extend this base in order to inherit these methods, which would mean the base class would either need to be an abstract or regular class and not an Interface, since you can't implement anything in an interface.
So, I don't understand saying "you should avoid inheritance..." and use Interfaces, I don't see how an interface is an alternative to extending a base class, when you need some common methods of the base.
-
Nov 19, 2004, 15:12 #4
- Join Date
- May 2003
- Location
- London, On.
- Posts
- 1,127
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
My understanding goes like this...
If all the methods in your abstract class are abstract, then you have an interface. Otherwise, you have an abstract class.
-
Nov 19, 2004, 15:18 #5
- Join Date
- Dec 2003
- Location
- Arizona
- Posts
- 411
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by MikeShank
JT
-
Nov 19, 2004, 15:29 #6
- Join Date
- May 2001
- Posts
- 193
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Thanks, now Im really confused.
What is the advantage to that type of inheritance,
I mean couldnt you do the same thing with a regular class,
instead of extending just
PHP Code:class C {
var $b;
function C() {
$this->b = new B();
}
function c() {
}
}
Is it more about writing perfect code, because I often extend classes, it seems like a natural thing to do, it makes sense, and most of all it works.
The stuff I work on is simple sites, cms's and the like, and stuff like this sometimes seems like real overkill, I mean the more threads, catch phrases, pattern names, etc.. I read, the more it seems to me I spend more time trying to conform to these things than attacking the problem.
-
Nov 19, 2004, 17:37 #7
- Join Date
- Oct 2004
- Location
- downtown
- Posts
- 145
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
I guess what im getting at is, what is so bad about extending a class?
Just don't ask what Chapter, it's an indepth read
-
Nov 19, 2004, 17:42 #8
- Join Date
- Dec 2003
- Location
- Arizona
- Posts
- 411
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
I say "do what works for you". If you are using implementation inheritance and it works well for you don't worry about the "Extends is Evil" article. I would definitely not advise that you try to conform your implementation to a particular design pattern. Really what you should do is implement and if you recognize something in your code that can be solved with a pattern, then use the pattern. If anything, adapt the patterns implementation to fit your particular problem but not the other way around.
Thanks,
JT
-
Nov 19, 2004, 17:44 #9
- Join Date
- May 2001
- Posts
- 193
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Does 'Thinking In Java' translate into Thinking In PHP, i mean do the same rules apply? I assume you use inheritance, since you can't remember the reason that you shouldn't.
Sorry if I'm coming off thick here, I don't have a formal programming background and I get overwhelmed sometimes with all of the patterns, concepts, etc..
thanks for your help
-
Nov 19, 2004, 18:36 #10
- Join Date
- Nov 2001
- Location
- Bath, UK
- Posts
- 2,498
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by seratonin
If you read the comments at the bottom, you'll see lots of voices against the "is evil" line - and taking such an extreme view probably isn't very clever. If you want to extend an implementation, then extends is the thing to use. If you want to extend an interface, but have the freedom to change the implementation, then that's what this article is talking about.
One of the ideas is that it is better to reuse a class as a black box rather than to try and make the black box bigger. If the black box is small to start with (say, an abstract class) then this argment isn't very strong.
If you have a class which does xy, and you want it to do z too, then you may as well extend it to do xyz. If, later, you wanted to replace xy with ab, you'd be stuck. So, one solution is to have z accept a class which could do ab or xy depending on what object is passed to it. That's where "extends is evil" because you'd probably have to duplicate z into xy and ab.
Or, you could turn the whole thing on its head, and pass z to ab or xy, and they can work out what to do with their new features themselves. Then you could pass in q instead of z, making abq and xyq without having to tell ab or xy what they are doing
Look up the GoF Composite and Stratagey patterns for more ideas.
DouglasHello World
-
Nov 19, 2004, 18:41 #11
- Join Date
- Nov 2001
- Location
- Bath, UK
- Posts
- 2,498
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by MikeShank
DouglasHello World
-
Nov 19, 2004, 19:36 #12
- Join Date
- Jul 2004
- Location
- Gerodieville Central, UK
- Posts
- 446
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Personally, I don't see extends as "evil", but like any technique in programming, it can be misused and called "evil" because many use it incorrectly (look at goto, that is evil, but there are case in languages like C++ where using a forward goto is cleaner and more readable than a load of flagging varaibles/tests to break out of loops).
The big issue with extending classes is that if you are not careful, you will nearly always use inheritance. This is particularlly bad in bigger projects as you will end up with the classes that are on the leaves of your class trees extending a hell of a lot of classes. If you follow that class up to the root base class in your tree, you need to look and compare the two classes. In bigger projects if you are extending a lot, then many times you will find that extending classes leaves you in a position where the leaf classes often end up not having much in common with your root most base class. However that class will still need to implement a lot of the methods from the base class, etc etc. In these situation the leaf classes are probably not using half the the methods that is has inherited, and if they are then, most of those methods are completely reimplemented in the extended classes, and thus your root / base class in the class tree has little in common with the leaf classes, even though if you comapre a class and it's parents there will still be a strong relationship.
The other issue with inheritance is that in the real world, many objects will implement a number of interfaces. Look at a car, that has electrical and mechanical properties and a novice C++ programmer might see the easy way to model this is by making a electrical and mechanical as classes, and then making the car extend both of these classes. In this situation, use of inheritance is a possible solution that works, even if it's not the most suitable. We can get away with multiple inheritance here as we know exactly what classes we are extending from.
However there are times were this is not possible. Take a look at a person. We might need to deal with two types of people who have different skills. One might be a a Web Designer, and the other might be a programmer...
class WebDesigner extends Person {}
class Programmer extends Person {}
Now in the real world, people don't just have one skill like illistrated above. Take me for example, I am both a web designer and computer programmer... With multiple inheritance I could say ....
class Jason extends WebDesigner, Programmer {}
but PHP won't let me. The other thing, unlike the car where the classes we extend from is predictable, when it comes to the person example, things change. A person during it's lifetime will learn new skills. For instance I might learn how to drive, or a might learn how to snowboard or something. These new skills can not be added to me at runtime via inheritance. As a result inheritance fails. Instead of inheriting, it becomes better to use aggregation. A good example of this is the Decorator pattern. I could demonstrate this by doing....
$Jason = new Snowboarder(new Driver(new Programmer(new WebDesigner(new Person())));
Now Snowboarders, Drivers, Programmers and Web Designers are classes that know and work with people. So they might implement the Person interface.
e.g.
interface IPerson {
function hasSkills();
function gender();
}
from this Person interface we can make a concrete class called Person. This is abstract as the class is incomplete (a person must have a concreate gender)
abstract class Person implements IPerson {
function hasSkills () {
return array('talk', 'walk', 'eat'); //Skills all normal people have ;-)
}
abstract function gender() {
}
}
Let make out concrete classes for a Person
class Male extends person() {
function gener() {
return 'Male';
}
}
class Male extends person() {
function gener() {
return 'Stupid Female';
}
}
now classes like WebDesigner, Programmer, etc know a bit about a Person but they only deal with certain features relating to a Person, and they are not aware of other Skills and as classes do not deal anything relating to a person such as their gender or other skills that person may have. This means we can make a skelton class for this, this will be our decorator class.... (Note this is abstract as is it can not be instanced as an object as it not "complete")
abstract class PersonDecorator implements IPerson {
public $wrapper;
function __construct(IPerson &$person) {
$this->wrapper = $person;
}
function gender() {
$this->wrapper->gender();
}
function hasSkills() {
$this->wrapper->hasSkills();
}
}
now from this class we can create skills....
class WebDesigner extends PersonDecorator {
function hasSkills() {
$skills = parent::hasSkills();
$skills[] = 'Web Design';
return $skills;
}
}
class Programmer extends PersonDecorator {
function hasSkills() {
$skills = parent::hasSkills();
$skills[] = 'Programmer';
return $skills;
}
}
... etc.
now with this, you could model someone like me like this....
$Jason = new WebDesigner(new Programmer(new Male()));
Interfaces are needed to state what behaviours are common to a number of classes. However behaviours differ majorly ...
for example
interface IOpenAble {
function Open();
}
class Door implements IOpenAble {}
class File implements IOpenAble {}
Open is a behaviour common to Door and File, but you woudln't have an abstract base class (ABC) here as both these methods are implmented totally differently when it comes to their algorithm.
The ABC is used in may example for classes like Decorators where there is behaviour that is shared. It is quite common to mix interfaces and ABC's like I had IPerson and Person.
Inheritance was also used in my example. However my example is designed in such a way that inheritance is doesn't create big class trees. If you inherit classes more than 3 or 4 times that is a bad code smell and therefore is *normally* avoidable.
The nice thing is that I can creat an object, such as $Jason, myself, and I can have the properties of Male, WebDesigner and Programmer without using multiple inheritance. Likewise if I was to develop new skills or for some reason, it doesn't cause problems in my design...Let say I took up snow boarding
$Jason = new SnowBoarder($Jason);
No problem.
The nice thing is that the Skill classes do not need to bound to a specific person class, as long as the object has implements IPerson. This means if our model was to be extended in the future, it would easily accomodate this. We could easily add something stupid like a Transvestite Gender, without having to affect the Skill classes.
The issue comes when Web Designer has extra methods and Programmer has extra methods as these are not callable if I take up snow boarding. In Java I think DynamicProxies get round this. In PHP5 you can use
function __call($func, $params)
{
return call_user_func_array(array($this->wrapper, $func), $params);
}
in the PersonDecorator class.
Look at the SPL in PHP5. It shows off the use of interfaces and decorators very well. The other thing to look at for interfaces is the DOM API. DOM uses interface and factories a lot. The idea of the interface plays a big part here where the composite pattern is used heavily. Interfaces and factories in the DOM API allow for it to be easy to create different implementions of the DOM (i.e. so it can be used with CSS, and HTML etc as well as XML).
-
Nov 19, 2004, 23:36 #13
- Join Date
- Dec 2003
- Location
- Arizona
- Posts
- 411
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by DougBTX
Originally Posted by MikeShank
JT
-
Nov 23, 2004, 14:55 #14
- Join Date
- Jul 2004
- Location
- office.bh.mg.br
- Posts
- 23
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Hi,
MiiJaySung, your post was really helpful and lightened some shadows here, that would be nice if I could just do somenthing like:
PHP Code:$alex = new OOPMaster( $alex )
What would be the advantages of your implementation using the Decorator opposed to mine in this case ... If you ask me to read more about OO and patterns I'll understand youAlex Brina
"...sempre q eu tirar a cabeça fora d'agua eu dou um alô..." JC
-
Nov 23, 2004, 15:31 #15
- Join Date
- Jul 2004
- Location
- Gerodieville Central, UK
- Posts
- 446
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
$alex = new OOPMaster( $alex )
Should work as you want it as the parameter $alex will be passed to the constructor of OOPMaster, and you would do something like $this->something = &$alex; which means the value of alex will referenced by both $this->something and $alex (otherwords both these variables are pointing to the same thing and is $alex changes, $this->something will change with it). When the constructor exits, $alex get told to point to the new object you've pointed. $this->something however should still point to the old $alex object. In order how to understand the way variables get controlled you need to understand references well. Whenever I teach this to other people in person, I often use the example of a files system where you have file data and a file name. The filename is like a variable name, and the contents of the file is like the variable value. What links them together is that the file name is linked specificly to those file contents. Another words the filename is referencing some file contents. This is done on the file system my holding where the file physically exists. This concept is exactly the same with varibables. When you make hard links on the UNIX file system (where two files names point to the same file contents), this is like having two variables referencing the same value / location in memory. The main concept behind referencing is the notation of a named item having a pointer (anotherwords it describes a location / address in some way).
Personally I would stick to different variable names to remove this confusing notation though ....
$new_alex = &new OOPMaster($alex);
.... is easier to see this distiction.
What would be the advantages of your implementation using the Decorator opposed to mine in this case
-
Nov 24, 2004, 05:21 #16
- Join Date
- Jul 2004
- Location
- office.bh.mg.br
- Posts
- 23
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
ok, you wrote in your post...
$Jason = new SnowBoarder($Jason);
No problem.
PS: maybe u did not get what I meant due to my poor english, if so, excuse me (getting better each time I come here)
My implementation was poor explained in my post. What I would do to represent that a Person has Skills would be:
PHP Code:class Skill{}
class OopKnowledge extends Skill{}
class Person {
var $skills = array();
function addSkill( &$skill ) {
$this->skills[] =& $skill;
}
}
$person =& new Person();
$person->addSkill( new OopKnowledge() );
PHP Code:abstract class PersonDecorator implements IPerson {
public $wrapper;
function __construct(IPerson &$person) {
$this->wrapper = $person;
}
function gender() {
$this->wrapper->gender();
}
function hasSkills() {
$this->wrapper->hasSkills();
}
}
Alex Brina
"...sempre q eu tirar a cabeça fora d'agua eu dou um alô..." JC
Bookmarks