Object Oriented Philosophy
ORIGINS OF OOP
C programmers have a pattern that might be called “Object-oriented class”. In this pattern, an object is an instance of a C struct.
struct st_employee_object *emp;
Or, given a suitable typedef:
EMPLOYEE emp;
Some of the struct members are function pointers. If “emp” is an object, then one calls a method on the object by looking up the appropriate function pointer and calling the pointed-to function:
emp->method(emp, args…);
Each struct definition defines a class; objects in the same class have the same member data and support the same methods. If the structure definition is defined by a header file, the layout of the structure can change; methods and fields can be added, and none of the code that uses the objects needs to know.
If you want inheritance, you let one of the structs be a prefix of another.
PRINCIPLES
There is nothing in any OOP literature I have read that says what I am doing is wrong, therefore how can it be wrong?
The language allows me to do it, therefore it cannot be wrong.
It works, therefore it cannot be wrong.
My solution is simple, therefore it adheres to the KISS principle and cannot be wrong
Having a “getter” or “setter” for each property value is a waste when you can use an associative array
Not only does this enable generic code to use common methods on any object, it also means that generic code does not even have to concern itself with the names or types of any data items which are in the inbound/outbound array. Thus even the data structure being used by the object is irrelevant. This also facilitates the principle of implementation hiding
All software I have ever written which communicates with a database table has to have some knowledge of the structure of that table within its code. Without that knowledge the software would not know which piece of data goes where. You cannot add a field to a table, or remove a field from a table without making a corresponding change to some part of your code. The database table and the software which accesses it must always be kept synchronised otherwise there will be big problems. This is not a unique failing in my design, it is a common feature of all languages.
I have never come across a case where software which communicated with a file or database table did not have knowledge of the structure of that file or table built into it. If you amend the file’s structure without amending the software’s view of that structure then you quickly run into trouble. If your software tries to write to a column that no longer exists the whole operation will fail. If you add a column without amending your software then how can it possibly supply any value for that column? This principle holds true even for software which is built around a data dictionary or application model – if you change the database without making a corresponding change in the dictionary/model you *will* have a problem.
As an application will contain multiple presentation layer components but only a single data access component, then surely it would not be considered ‘best practice’ to have that code duplicated in multiple places when it could just as easily be defined within a single place?
As far as I am concerned ‘class’ relates to ‘database table’ just as ‘class instance’ (object) relates to ‘table instance’ (record). It is not rocket science, just simple logic.
The $_POST array also contains an entry for the SUBMIT button, and if this is included in the INSERT statement you will hit a brick wall. So, I needed a generic mechanism in which items which did not belong in the table were automatically excluded from any sql statement. Instead of creating a blacklist to validate a $_POST array, have another class variable called $this->fieldlist to contain a list of all the column names that were valid for that particular table. Extend the $fieldlist array in the class constructor to indicate which field(s) in that table formed the primary key.
Primary validation will ensure that fields start_date and end_date both contain valid dates, but to check that and_date is not earlier than start_date you need an additional layer of validation which I call secondary validation. If a table is the ONE in a ONE-to-MANY relationship (sometimes known as a PARENT_CHILD relationship) there may be some rules to apply before a row can be deleted. For instance, you might want to prevent a parent row from being deleted if any child rows still exist, or you may want to allow the deletion but process the child rows in some way, perhaps by deleting them or by replacing the foreign key with nulls. This is another area that I wanted to automate in some way rather than have to hard-code it each time. Rather than waste time trying to hold these rules outside the class and having to invent some mechanism to feed them into the object at runtime I decided to create an abstract method in the processing cycle into which any and all such custom validation could be inserted.
The end result of all this work gave me a class hierarchy with two levels:
A superclass containing generic code to deal with any table of any structure in any database.
A subclass for each individual database table containing the specific details of that table.
The subclass inherits all the code from the superclass while at the same time providing the missing details which allow an object to be instantiated. These missing details are:
The database name.
The table name.
The table structure.
The identity of the primary and any additional unique keys.
Any delete constraints.
All business rules.
You should notice that my superclass matches perfectly the description given for an abstract class, and as such it is perfectly legitimate, nay obligatory, that it can only be instantiated into an object via a subclass. Accordingly I have a separate subclass for each entity/database table which contains the implementation details for that particular entity/table. When I instantiate a subclass into an object the end result is a merging of the generic code from the superclass and the specific code from the subclass.
In my implementation all generic code goes into an abstract superclass, while all non-generic code goes into subclasses, one subclass per database table. The generic code is therefore shared through inheritance. Your method, and that of your so-called ‘good designers’, of instantiating an instance of what is clearly an abstract class to create working objects does not adhere to the principles of OOP. If I added the keyword abstract to the definition of my superclass the language would physically prevent it from being instantiated into an object, therefore the method that both you and your so-called ‘good designers’ use is totally at variance with the principles of OOP. All the generic code for select, insert, update and delete exists within a single abstract superclass, which means that there is only one copy. This is inherited, not duplicated, in each table class as each table class is a subclass of my abstract table class.
Where you had a SCRIPT I now have a SUBCLASS which extends a CLASS and therefore includes its entire contents through that mechanism called inheritance. This means that I do not require any extra code to transfer details between the SCRIPT and an OBJECT instance as they are already there. The problem of secondary validation is also solved as I simply place whatever code I require in the relevant custom method which overrides the abstract method which is defined in the superclass. It is YOUR method which is wrong, not mine.
I would not create a subclass for ‘person’ unless I had to process a person with totally different attributes, properties and business rules. The reason that I create a separate subclass for my ‘customer’, ‘product’ and ‘invoice’ tables is that they are totally different entities with totally different structures and totally different business rules. The fact that they are all database tables makes them similar but not identical, and I take care of the similarities by inheriting generic methods and variables from my abstract superclass. That which is unique (non-generic) about each particular table, such as its structure and all its business rules, goes into a separate subclass for each table.
The purpose of the 3 tier architecture is to delegate tasks to particular layers so that any one layer can be changed without having any effect on the others. In this way it should be possible to switch from one database to another just by making a change to the data access layer, or to switch from one user interface to another just by making a change to the presentation layer. All the complicated code, the processing of the business rules, is maintained within the business layer which should be able to withstand any change in the other layers without missing a beat, thus preserving all the investment that went into its construction. It should even be possible to run two different user interfaces at the same time (such as client/server and web) which share the same components in the business layer.
Pagination cannot be handled in the presentation layer alone – part of it MUST be in the presentation layer, but part of it can ONLY be done in the data access layer
If I were not allowed to use the LIMIT/OFFSET clause on the SELECT statement the only other option would be to retrieve ALL possible rows and pass them ALL back to the presentation layer which would then have the task of filtering out the chunk that it wanted to display. That would be an extremely inefficient way to implement pagination, and anyone who advocates such a level of inefficiency is clearly in the wrong profession (IMHO).
In this example all the variables I am writing to are immediately processed by code within the getData method. These variables are not used by any other methods, therefore there is no harm in leaving them public.
$dbobject->numrows will return the total number of rows which satisfied the selection criteria.
$dbobject->pageno will return the current page number based on $rows_per_page.
$dbobject->lastpage will return the last page number based on $rows_per_page.
These variables are set by code within my getData method and are destined to be output, therefore there is no harm in accessing them directly instead of via a getter.
The presentation layer is limited to the interface with the user which in this case is ‘get me page X of the current result set’ with the option to ’set page size to Z rows’ if the current (or default) value for ‘Z’ needs to be changed. All pagination can be tuned off simply by setting $rows_per_page to zero.
Data is formatted or presented to the user by the presentation layer which takes whatever has been returned by the data access layer (via the business layer) and generates the necessary HTML output.
A data dictionary is a facility built into the language which allows the layout of each database table and the specifications of each field within the table to be described.
A class for a database table should encapsulate the necessary properties, methods, business rules, and persistent data store(database, XML, JSON, CSV, etc.) variables. Each class in the business layer (e.g. customer, car, product, whatever) has a create, update and delete method, but – guess what – they do not actually complete that action themselves. Instead they call a corresponding method in a Data Access Object (DAO) which handles it for them.
They pass a simple associative array to the DAO with exactly the same as the contents of the PUT request as received by PHP.
The array is passed as an argument on the create() method. It sounds daft to call the DAO’s create() method without any data, thus forcing it to perform a call-back.
There are several several arguments to the create() method:
- the data as an associative array.
- the table name.
- the database name.
- an array of field specifications (size, type, etc)
- an array which identifies the primary key field(s).
- an array which identifies any candidate key field(s).
In this way the DAO contains no hard-coded references to databases, tables, columns or keys. It is given all the information it requires on each method call. It uses this information to construct the relevant SQL query string before it is sent the database by the relevant API.
I have a different DAO for each database engine which enables me to switch from one database to another simply by instantiating my DAO object from a different class file.
If a different database engine requires that the SQL query string be constructed in a different way, then that too can be dealt with inside the class for that engine. All the other objects remain oblivious to the internal workings of the DAO. They just call it to perform a task, and it does it. How it does it is irrelevant.
By having a separate DAO for each different database engine (e.g. MySQL, PostgreSQL, Oracle, etc) it is possible to switch from one RDBMS to another by changing a single component. No business object knows anything about the internals of the DAO, it merely hands over some data and says “take care of this for me”. This is called “separation of responsibilities”.
First of all let me reiterate the fact that all OO programming languages allow 3 possible ways to feed data into an object:
By writing to the variable directly, as in $object->variable = ‘value’;
By using the variable’s set method, as in $object->setVariable(‘value’);
By supplying that variable as an argument on a method, as in $object->doSomething(‘arg1′,’arg2′,’arg3′,…);
Secondly, let me say that I do not have to choose just one of those options and stick to it religiously just to be consistent – I will use whatever option that seems appropriate for the particular circumstances.
Where a method has potentially a large number of arguments, and most of these come from the $_GET/$_POST array and may not actually be provided, using the variable’s set method is the most efficient because:
I have found this approach to have the following advantages:
I do not have to have a long string of arguments on my methods.
I do not have to change the argument list, either within the method itself or in the numerous places from which the method may be called, each time I want to add or remove a variable.
Where a variable has its own set method I can include code within that method to cleanse the input of any bad values instead of having to include the code within the method which uses that variable, as in the following:
function setPageNo ($pageno)
{
$this->pageno = abs((int)$pageno);
}
The code which performs primary validation by comparing the contents of the $_POST array with the field specifications in the $fieldspec array is automatically invoked on every insert and update operation. Each field in the database is of a particular type and size, therefore the primary validation is limited to checking that the data input by the user conforms to these specifications. As the number of data types is quite static the code I need to validate each data type is just as static, but it is flexible enough to deal with every combination of data type and size that is thrown at it.
When it comes to secondary or custom validation I have provided the following abstract methods in my superclass which are automatically called at the relevant point in the processing cycle:
_cm_validateInsert for records being inserted.
_cm_validateUpdate for records being updated.
_cm_commonValidation for records being either inserted or updated.
_cm_validateDelete for records being deleted.
In all the years that I have been programming I have never stopped looking for a better way, a quicker way, a more flexible way. Most of the time it has been down to a little change here, a little tweak there. Individually the changes may appear to be insignificant, but taken collectively there is a big difference. I regularly ignore the advice of those who consider themselves to be masters and I regularly outperform their feeble efforts.
BUG CLASSIFICATION
How serious is the problem?
It causes the whole system to crash.
It causes the program to crash.
It produces the wrong results.
It is very inefficient.
It is slightly inefficient.
How often does it occur?
Always, under all circumstances.
Always, under some circumstances.
Sometimes, under all circumstances.
Sometimes, under some circumstances.
Rarely.
What is the solution?
A major rewrite of the whole system.
A minor rewrite of the whole system.
A major rewrite of a single program.
A minor rewrite of a single program.
Programmer education and training.
BEST APPLICATIONS FOR OOP
To track large amounts of information about a complex subject, such as a person, place, or thing
To create domain specific languages that enable application owners to explain widget customization instructions to users or API usage guides to developers using easy to understand metaphors
WORST APPLICATIONS FOR OOP
For presentation and data formatting
Practical Applications of OOP Principles
Composition
When you can easily breakdown large categories into useful topics with different appeal and usage, such as dividing entertainment into sports, music, movies, and videogames
Inheritance
When multiple relationships are best described using the same word in different contexts, such as love, trust, attention, or sharing
Namespacing
When the roles between the parties in the relationship are likely to change, especially in the same activity
Encapsulation
Placing of data and the operations that perform on that data in the same class. Breaking encapsulation therefore means to NOT put the data and associated operations in the same class. It has nothing to do with variables, public or otherwise.
Polymorphism
If you plan to distribute the blueprint to your creation, a standard manual will make reproduction and maintenance easier. Different classes may have methods with the same name as other classes, but the response obtained from those methods is determined by the object instance. Same interface, different implementation, or the ability to substitute one class for another.
API coding
When you have a utility which is used for many functions, such as a bathroom
Optional OOP
package Person;
sub new
{
my $class = shift;
my $self = {
_firstName => shift,
_lastName => shift,
_ssn => shift,
};
# Print all the values just for clarification.
}
print "First Name is $self->{_firstName}\n";
print "Last Name is $self->{_lastName}\n";
print "SSN is $self->{_ssn}\n";
bless $self, $class;
return $self;
In Perl, object creation is manual. A class is declared as a package, and the functions in the package then become the methods of the class. To create an object, you make a hash table and then bless it (using the built-in function ‘bless’) to make it an object. When you call a method on an object or class, the first parameter is always the object’s reference or the class name, depending on how it is called. Using the ’shift’ function assigned the class name to the associated variable. The bless function takes two arguments: a reference and a class name. What it does is tell Perl that the given reference is actually an instance of a specific class so that, when you call a method on your object, it knows where to start looking for the method definitions.
There are three main terms, explained from the point of view of how Perl handles objects. The terms are object, class, and method.
Within Perl, an object is merely a reference to a data type that knows what class it belongs to. The object is stored as a reference in a scalar variable. Because a scalar only contains a reference to the object, the same scalar can hold different objects in different classes.
A class within Perl is a package that contains the corresponding methods required to create and manipulate objects.
A method within Perl is a subroutine, defined with the package. The first argument to the method is an object reference or a package name, depending on whether the method affects the current object or the class.
Perl provides a bless() function which is used to return a reference and which becomes an object.
References
Friends Don’t Let Friends Abuse Inheritance
Introduction to Object Oriented Programming
Encapsulation and Namespacing in Flash 5
Variable Mutation in Class-Based Languages
Reasons People Like Object-Oriented Programming
When Object-Oriented Rendering is Too Much Code
Object-Relational Mapping is the Vietnam of Computer Science
Object Interaction in PHP: Introduction to Composition
Object-Oriented Programming Myths
Object-Oriented Programming for Heretics
Building User Interfaces for Object Oriented Systems
The Basics of Object Oriented Programming
Development Speed using Strong vs Weak Typing
Implementing Real Classes in JavaScript
Blog at WordPress.com. | Theme: Pool by Borja Fernandez.
Entries and comments feeds.