Академический Документы
Профессиональный Документы
Культура Документы
What is so special about it? Why should I use it? How do I use it?. The person asking this type of question usually has experience of non-OO programming and wants to know the benefits of making the switch. Unfortunately most of the replies I have seen have been long on words but short on substance, full of airyfairy, wishy-washy, meaningless phrases which are absolutely no use at all to man or beast. Having created 1000's of programs using non-OO languages, and another 500+ using the OO features of PHP I feel more than qualified to add my own contribution to the melting pot. According to some OO 'purists' I am not qualified at all as I was not taught to do things 'their' way and I refuse to follow 'their' methods. My response to that accusation is that there is no such thing as 'only one true way' with OOP just as there is no such thing as 'only one true way' with religion. People tell me that my methods are wrong, but they are making a classic mistake. My methods cannot be wrong for the simple reason that they work, and anybody with more than two brain cells to rub together will tell you that something that works cannot be wrong just as something that does not work cannot be right. My methods are not wrong, they are simply different, and sometimes it is a willingness to adopt a different approach that separates the code monkeys from the engineers. One reason why some people give totally useless answers is that it was what they were taught, and they do not have the intelligence to look beyond what they were taught. Another reason is that some of the explanations about OO are rather vague and can be interpreted in several ways, and if something is open to interpretation it is also open to a great deal of mis-interpretation. If you do not believe that there is widespread confusion as to what OO is and is not then take a look at Nobody Agrees On What OO Is. Even some of the basic terminology can mean different things to different people, as explained in Abstraction, Encapsulation, and Information Hiding. If these people cannot agree on the basic concepts of OOP, then how can they possibly agree on how those concepts may be implemented.
package supplied us with a series of pre-compiled subroutines which we could call from our own code. We never saw the source code to any of these subroutines, we merely had a list of APIs and a description of what each API required as input and returned as output. How's that for implementation hiding?
The result of each invocation is exactly the same - the caller is suspended while control is passed to the callee, and control is not returned to the caller until the callee has finished. I have worked with messaging software in the past and I can tell you quite categorically that they are completely different:
In the first place they allow messages to be passed from one process to another, not one module to another in the same process. The only exception I have seen to this was in a language which supported the creation of non-modal forms in which it was possible for one non-modal form to send a message to another non-modal form within the same application instance. However, it could only do this by using a separate sendMessage() function, and the receiving module had to have code in its receiveMessage trigger to deal with any incoming messages. The only way to respond to such a message was for the receiver to send its own message back to the sender. In the second place their behaviour is totally different: o They are asynchronous, which means that after the caller drops a message into the message queue the caller can continue processing and does not have to wait until the callee returns control. E-mail is a classic example of such a messaging system. o The message queue may be able to contain any number of messages from any number of processes, and the receiving process picks out one message from the front of the queue, processes it, then looks for the next message. o The message may signify that an acknowledgement be sent back to the caller as soon as it has been received, or it may require a more elaborate result set to be returned after it has been processed. o In either case the message originator must contain the necessary code to deal with the acknowledgement and/or the result, which is in addition to the code which sends the message.
As you can see the mechanics of activating a method in an object is exactly the same as activating a non-OO function and nothing like passing a message in a messaging system.
separate any responsibilities you must first identify what those responsibilities are, and this is a design decision which is totally separate from the language in which the design is ultimately implemented. In all my many years of experience the only project that I have ever been involved in which failed to be implemented due to "technical difficulties" was one where the system architects were OO "experts" who knew everything there was to know (or so they thought) about this "separation of responsibilities". They designed a system around design patterns which had a different module for each responsibility, and this resulted in a design with at least ten layers of code between the UI and the database. This made the creation of new components far more complicated and convoluted than it need be, and it made testing and debugging an absolute nightmare. The result was far too expensive for the client, both in time and money, so he pulled the plug on the whole project and cut his losses. A pair of components which took 10 days to build using these "new fangled" OO techniques took me less than an hour to build using my "old fashioned" non-OO methods. So much for the superiority of OO. Besides, any software which consists of multiple classes/modules automatically has "separation of concerns" as each class/module can be considered to be "concerned" with a particular entity. The critical factor is how well each class/module deals with the requirements of its entity.
'Late' refers to the fact that the binding decisions (which binary to load, which function to call) are deferred as long as possible, often until just before the function is called, rather than having the binding decisions made at compile time (early). Rubbish. Whether such binding takes place early or late does not separate OOP from non-OOP. It is possible to have a non-OO language which offers late binding, but that does not magically turn it into OO. Conversely, a language which supports classes, encapsulation, inheritance and polymorphism is suddenly not OO simply because it only offers early binding.
As you can see, the above descriptions are either too vague or not specific to OOP, so they cannot be used as distinguishing features.
Same interface, different implementation. The ability to substitute one class for another. This means that different classes may contain the same method names, but the result which Polymorphism is returned by each method will be different as the code behind each method (the implementation) is different in each class. A class defines (encapsulates) both the properties (data) of an entity and the methods (functions or operations) which may act upon those properties. Neither properties nor methods which can be applied to that entity should exist outside of that class definition.
What OOP is
This is a lot simpler than some people would like you to believe. They like to use the more complicated definitions because it makes them sound more intelligent than they really are. Here is the real definition: Object Oriented Programming is programming which is oriented around objects, thus taking advantage of Encapsulation, Polymorphism, and Inheritance to increase code reuse and decrease code maintenance. To do OO programming you need an OO language, and a language can only be said to be object oriented if it supports encapsulation (classes and objects), inheritance and polymorphism. It may support other features, but those are the bare minimum. That is not just my personal opinion, it is also the opinion of the man who invented the term. In addition, Bjarne Stroustrup (who designed and implemented the C++ programming language), provides this broad definition of the term "Object Oriented" in section 3 of his paper called Why C++ is not just an Object Oriented Programming Language: A language or technique is object-oriented if and only if it directly supports: 1. Abstraction - providing some form of classes and objects.
2. Inheritance - providing the ability to build new abstractions out of existing ones. 3. Runtime polymorphism - providing some form of runtime binding. Later versions of various OO languages have added more features, and some people seem to think that it is these additional features which decide if a language is OO or not. I totally disagree. It would be like saying that a car is not a car unless it has climate control and satnav. Those are optional extras, not the distinguishing features. It would also be incorrect to say that a car is a car because it has wheels. Having wheels does not make something a car - a pram has wheels, but that does not make it a car, so having wheels is not a distinguishing feature. That is why I say that such things as "modularity", "reusability" and "messaging" are not features which distinguish an OO language from a non-OO language for the simple reason that they already exist in some non-OO languages.
A class method is defined within the boundaries of a class definition. Each class name "cName" must be unique within the application. Each class may contain any number of functions (also known as "methods"), and the function name "fName" must be unique within the class but need not be unique within the application. In fact, the ability for different classes to share common function/method names is a requirement of polymorphism.
class cName { function fName ($arg1, $arg2) // function description { .... return $result; } // fName } // cName
Calling a class method is not so straightforward. First it is necessary to create an instance of the class (an object), then to access the function (method) name through the object. The object name must be unique within the application.
$object = new cName; $result = $object->fName($arg1, $arg2);
Although it is possible to access a static method without first creating an object, this is no better than accessing a non-class function. As it is not actually using an object it cannot be considered part of object oriented programming.
Each time this function is called it will return a value that is one greater than the previous call. Without the keyword static it would always return the value '1'. Class variables which need to persist outside of a function (method) are declared at class level, as follows:
class calculator { // define class properties (member variables) var $value; // define class methods function setValue ($value)
{ $this->value = $value; return; } // setValue function getValue () // function description { return $this->value; } // setValue function add ($value) // function description { $this->value = $this->value + $value; return $this->value; } // setValue function subtract ($value) // function description { $this->value = $this->value - $value; return $this->value; } // setValue } // cName
Note that all class/object variables are referenced with the prefix $this-> as in $this->varname. Any variable which is referenced without this keyword, as in $varname, is treated as a local variable. Note also that each instance of the class (object) maintains its own set of variables, so the contents of one object are totally independent of the contents of another object, even it is from the same class.
Practical Examples
Here are some practical examples which demonstrate Encapsulation, Inheritance and Polymorphism.
Encapsulation
Encapsulation The act of placing data and the operations that perform on that data in the same class. The class then becomes the 'capsule' or container for the data and operations.
Every application deals with a number of different entities or "things", such as "customer" "product" and "invoice", so it is common practice to create a different class for each of these entities. At runtime the software will create one or more objects from each class definition, and when it wants to do something with one of these entities it will do so by calling the relevant method on the relevant object. The data held within each object at runtime cannot remain in memory for ever, so it is written out to a persistent data store (a database) with a separate table for each entity. There are only four basic operations which can be performed on a database table (Create, Read, Update, Delete) so I shall start by creating a method for each one.
class entity1 { // class properties var $dbname; // database name var $errors = array(); // array of error messages, indexed by field name
// // // // //
associative array of name=value pairs array of field specifications number of database rows affected array of field names which make up the primary key table name
= 'entity1'; = 'foobar';
$this->fieldlist = array('column1', 'column2', 'column3', 'column4'); $this->primary_key = array('column1'); } // entity1 // class methods function getData ($where) // read data from the database which satisfies the selection criteria in $where { .... return $this->fieldarray; } // getData function insertRecord ($fieldarray) // create a database record using the contents of $fieldarray { .... return $this->fieldarray; } // insertRecord function updateRecord ($fieldarray) // update a database record using the contents of $fieldarray { .... return $this->fieldarray; } // updateRecord function deleteRecord ($fieldarray) // delete a database record identified in $fieldarray { .... return $this->fieldarray; } // deleteRecord } // entity1
The class constructor identifies the physical characteristics of this database table, that which makes it unique from all other database tables. The contents of the class constructor are performed automatically when the class is instantiated into an object. All the table data is held in a single array of fields rather than a separate variable for each field. For a detailed explanation as to why I choose this method please read Why don't you use GETTERS and SETTERS? I have not bothered including the actual code within each method as it there is too much of it. It you really want to see the code that I use then you can download it, either for my small sample application, or my full development framework. There are actually many more methods than the four mentioned, but these are enough to begin with.
Each of these classes therefore acts as a 'capsule' which contains both the data for an entity and the operations which can be performed upon that data. This is 'encapsulation'.
Inheritance
Inheritance The reuse of base classes (superclasses) to form derived classes (subclasses). Methods and properties defined in the superclass are automatically shared by any subclass.
After writing and testing a class to deal with 'entity1' I copied it and made it work for 'entity2'. I then compared the two classes to see what code was common and could be shared, and what code was unique and could not be shared. I then transferred all the common code into a separate class known as a 'superclass'. Firstly, to create the superclass, I changed the class name and the constructor to the following:
class default { // class properties .... // class methods function default () // constructor { $this->tablename $this->dbname
= 'unknown'; = 'unknown';
$this->fieldlist = array(); $this->primary_key = array(); } // default // class methods function getData ($where) .... function insertRecord ($fieldarray) .... function updateRecord ($fieldarray) .... function deleteRecord ($fieldarray) .... } // default
This class cannot be used to instantiate a working object as it does not refer to a database table which actually exists, so it is what is known as an 'abstract' class. Secondly, I altered each table class to remove the common methods and properties, and included the keyword extends to force inheritance from the superclass.
include 'default.class.inc'; class entity1 extends default { function entity1 () // constructor { $this->tablename = 'entity1'; $this->dbname = 'foobar'; $this->fieldlist = array('column1', 'column2', 'column3', 'column4'); $this->primary_key = array('column1');
} // entity1 } // entity1
When a subclass is instantiated into an object that object will contain all the properties and methods of the superclass as well as those of the subclass. If anything has been defined in both the superclass and the subclass, then the definition from the subclass will be used. In my current development environment the superclass contains several thousand lines of code, but there is only one copy of this code which is inherited by several dozen table classes. Inheritance is therefore a powerful mechanism for making one copy of common code accessible to many objects instead of having multiple copies of that common code.
Polymorphism
Same interface, different implementation. The ability to substitute one class for another. This means that different classes may contain the same method names, but the result which Polymorphism is returned by each method will be different as the code behind each method (the implementation) is different in each class. Polymorphism can only be employed where the same method names exist in several classes. The code within the method may be inherited from a parent class, or it may be totally different. This means that the same method can be used on different objects, but the results will be different. For example, take a series of classes called 'Customer', 'Product' and 'Invoice'. One practice I have seen which makes polymorphism impossible is to incorporate the entity name into the method name, as in: 1. getCustomer(), insertCustomer(), updateCustomer() deleteCustomer() 2. getProduct(), insertProduct(), updateProduct() deleteProduct() 3. getInvoice(), insertInvoice(), updateInvoice() deleteInvoice() The problem with this approach is that the object (the controller in MVC) which communicates with each table object (the model in MVC) needs to know the method name before it can open up that channel of communication. If each model has a unique set of method names then it must have a unique set of controllers to communicate with it. My approach is to use a standard set of method names for standard operations, as in: 1. getData(), insertRecord(), updateRecord() deleteRecord() This is made easier as these methods are defined in the superclass and made available to each subclass through inheritance. The advantage of this is that I can have one standard controller for each standard function, and this controller can work with any table class in the system. This is far better than having a separate set of controllers for each table class. Here is some example code from one of my controllers:
.... include "$table.class.inc"; $object = new $table; $data = $object->getData($where); ....
The contents of $table and $where are made available at runtime. The significant point is that the name of the class (database table) is not hard-coded into the controller, it is passed as an argument at runtime. Only the method names are hard-coded, but as these method names exist within every table class by being inherited from the superclass they will always work. So, if the class name
is 'Customer' the controller will obtain data from the 'Customer' table, if it is 'Product' it will obtain data from the 'Product' table, and so on.
Conclusion
Many people use different words to describe what OOP is supposed to mean, but the problem with words is that they are slippery. Like Humpty Dumpty proclaimed in Lewis Carroll's Through the Looking Glass: When I use a word, it means just what I choose it to mean -- neither more nor less. If you take the words used by the originators of OOP and apply different meanings to those words, then others take your words and apply different meanings to them, then you can end up with something which is nothing like the original, as immortalised in that children's game called Chinese Whispers. There are only three features which really differentiate an Object Oriented language from a non-OO language, and these are Encapsulation, Inheritance and Polymorphism. Everything else is either bullshit or hype. Object Oriented Programming is therefore the use of these features in a programming language. High reusability and low maintainability cannot be guaranteed - that depends entirely on how these features are implemented. Some people accuse me of having a view of OOP which is too simplistic, but instead of saying that my view is "more simple than it need be" surely it can also mean that their view is "more complex than it need be"? As a long-time follower of the KISS principle I know which view I prefer, and I also know which view is easier to teach to others.