This question will probably confuse a lot of novice programmers as they do not realise that Object Oriented Programming (OOP) had any aims in the first place. It's just a style of programming, right? Surely the only aim to programming is the writing of programs, the creation of software, right? While those answers are partially correct they miss an important point - the production of software on its own is not a measure of success, it is how effective, particularly cost effective it is viewed as by the end customer, the user, the one who pays the bills. Different programming languages are created with different features and syntax in an attempt to offer something which is better than the alternatives. As explained in What is OOP? Object Oriented languages have features which provide the ability to
increase code reuse and decrease code maintenance. Why is reusable code so important? The more reusable code you have at your disposal then the less code you have to write, and the less code you have to write to get the job done then the less time it will take to get the job done. Programmers are charged by the hour, so the ability to get the job done in less time than your rivals will directly equate to a lower cost, and lower costs are more likely to resonate with paying customers than the so-called "purity" of the code. Reusable code will also take less time to document and less time to test. All in all it will make you more productive. How do you write code which is reusable? A starting point would be to read Designing Reusable Classes which was published in 1988 by Ralph E. Johnson & Brian Foote.
In the culinary world there is a well-known saying:
The proof of the pudding is in the eating.
This means that it does not matter what ingredients were used or what recipe was followed, if the end result looks like crap or tastes like crap then the entire effort has been a failure. Instead of producing a gourmet meal the cook has actually created a dog's dinner, something which is unfit for human consumption and will only be appreciated by a canine companion who will eat practically anything put in front of him.
In the software world the same rules apply - it does not matter what ingredients (programming language, software libraries, DBMS) you use, or what recipe (programming rules or "best practices") you follow, it is the end result as viewed by the paying customer that really counts. Different programmers have different ideas of what the "rules" of OOP are, but to my way of thinking most of them are not rules at all but simply guidelines or personal preferences. In my long experience of designing and building enterprise applications using a mixture of non-OOP and OOP languages, as well as a mixture of non-relational and relational databases, there is only a small number of rules which can genuinely be described as universally applicable, and these are identified below.
Note that these rules need not apply if you are a lone programmer who writes code only for personal consumption. However, if you are writing code which will be shared and maintained by other people then it is vitally important that you write code which those other people can read and understand. If you are the only person who can understand and maintain the code that you write then you are a bad programmer.
Programs must be written for people to read, and only incidentally for machines to execute.Martin Fowler, the author of Patterns of Enterprise Application Architecture (PoEAA) put it another way:
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.Here is another variation of that saying:
Any idiot can write code that only a genius can understand. A true genius can write code that any idiot can understand.
There are some people who seem to think that PHP is too verbose and seek to "improve" the situation by introducing a more compact syntax. In my humble opinion this would be a backward step. There is a word for the act of replacing proper human-readable words (plain English) with symbols, and that word is obfuscation.
This topic is discussed further in the following:
Try to achieve complex tasks using simple code, not simple tasks using complex code.
This is in complete contrast to most of today's programmers who seem to prefer the KICK principle.
Smart data structures and dumb code works a lot better than the other way around.This is the opposite view to that of Robert C. Martin (Uncle Bob) who, in his article NO DB wrote the following:
The database is just a detail that you don't need to figure out right away.I prefer to ignore Uncle Bob and follow the principles identified in Jackson Structured Programming where it says
start with the data structures of the files that a program must read as input and produce as output, and then produce a program design based on those data structures, so that the program control structure handles those data structures in a natural and intuitive way.
All the other "rules" or "best practices" which I have encountered are mainly based on one of the above, but merely expressed in a greater level of detail. You try and get a bunch of programmers to define what "readable" code means and all you will do is start a never-ending debate on when to use uppercase or lowercase, when to use camel case, snake case or even Studly caps. The only common description of "bad code" which is offered by most programmers is
code not written by me, which means that the topic is purely subjective and there will never be universal agreement.
My critics fail to understand that sometimes I don't follow a particular practice/principle/rule simply because it is not appropriate for the type of applications which I develop. This could either make that practice completely redundant, and thus a violation of YAGNI, or produce a result which is not as optimum as it could be. If I can find a way that is better than your "best" then surely I should be applauded and not admonished.
Note that the sole measurement for judging what is "best" should be "that which produces the best results". This means that the software should be cost-effective so that where several pieces of software have the same effect the one with the lowest cost should always be regarded as being better. Note that "cheaper but less effective" does not qualify. With software development it is the cost of the developers time which is a crucial factor, and the best way to reduce the amount of time taken for a developer to write code is to write less code, which in turn can be achieved by utilising as much pre-written and reusable code as possible. Since the stated aim of OOP is
to increase code reuse and decrease code maintenance then any practice which encourages the production of reusable code should be regarded as being better than any practice which does not. I encourage you to read Designing Reusable Classes which was published in 1988 by Ralph E. Johnson & Brian Foote for ideas on how this can be achieved.
Simply following a series of rules or common/standard practices is not enough on its own. Following rules blindly in a dogmatic fashion and assuming that the results you achieve will be the same as those achieved by others is the path to becoming nothing more than a Cargo Cult Programmer. You have to analyse a problem before you can design a solution, then you need to decide which practices to apply and how to apply them in order to achieve the best results. By "which practices" I mean that some rules or practices may be inappropriate for various reasons. Trying to build a modern web-based database application using practices which were created by people who had little or no experience with such applications is unlikely to set you on the path to success. Most of the rules, practices and principles which I have encountered were written in the 1980s by academics using the Smalltalk language, or something similar, but how many of these people used these languages to write enterprise applications with hundreds of tables and thousands of screens? Also, practices designed for programs using bit-mapped displays are not relevant for modern programs which use HTML forms.
Below are some the rules, principles and practices which I consider to be inappropriate, so I ignore them:
favour composition over inheritancewas devised by someone who didn't know how to use inheritance properly, which creates problems. The solution is to avoid deep inheritance hierarchies and to only inherit from an abstract class. Refer to Composite Reuse Principle for more of my thoughts on this matter.
program to the interface, not the implementationis meaningless as you cannot simply call an interface, you must call a method on an object which actually implements that interface. I have yet to see a code sample which proves that this idea has merit, and until I do I will dismiss it as bogus. If this is supposed to mean calling a method on an unknown object where the identity of that object is not provided until runtime, then as a description of how to use polymorphism is is pretty pathetic.
software entities should be open for extension, but closed for modificationimplies that, once deployed, you should not modify an object but extend it (using "extends" to create a subclass?), which sounds like it creates more problems than it solves. In the life of my framework I have performed numerous refactorings, and if I was forced to put each update in a separate subclass I would also have to change all references of the original class to the updated subclass. Refer to Open/Closed Principle for more of my thoughts on this matter.
depend upon abstractions and not concretionsis vague because there are so many different interpretations of the word abstraction. It took me years to realise that what this meant was to define methods in an abstract class which could then be inherited by numerous subclasses, thus providing polymorphism, which then gives you the ability to swap from one subclass to another at run time. But what if I don't have multiple subclasses? Refer to Dependency Inversion Principle for more of my thoughts on this matter.
each software module should have one and only one reason to change, but "reason to change" was found to be so inadequate and confusing that Uncle Bob had to produce a follow-up article to explain that what he meant was the separation of GUI logic, business logic and database logic. This is, in fact, the same thing as the 3-Tier Architecture which is less confusing to implement as it has a more precise and less ambiguous definition.
It should be obvious to every OO programmer that shared method names offer polymorphism while unique method names do not. Polymorphism provides the opportunity for more reusable code as it enables Dependency Injection.
it should not be possible for an object to exist in an inconsistent statewhere the word "state" is mistakenly taken to mean the data within an object when it actually means the condition of an object. The ONLY absolute rule regarding constructors is that after being executed the constructor should leave the object in a condition which will allow any of its public methods to be called. My full response to this schoolboy mistake can be found in Re: Objects should be constructed in one go.
PHP was created to make it easy to create dynamic web applications, those which have HTML at the front end and an SQL database at the back end. I was involved in writing enterprise applications (database applications for commercial organisations) for 20 years before I switched to using PHP, so I knew how such applications worked. I had even created frameworks in two of those languages. All I had to do was to convert my latest framework to use PHP and the OO features which it offered in order to create as much reusable software as possible. The structure of my RADICORE framework can be pictured in Figure 1 below:
Figure 1 - A combination of the 3-Tier Architecture plus MVC
There is also a more detailed version available. This shows that the RADICORE framework uses a combination of the 3 Tier Architecture, with its separate Presentation layer, Business layer and Data Access layer, and the Model-View-Controller (MVC) design pattern. The following amounts of reusability are achieved:
Note also that any Controller can be used with any Model (and conversely any Model can be used with any Controller) because every method call made by a Controller on a Model is defined as a Template Method in the abstract class which is inherited by every Model. This means that if I have 40 Controllers and 450 Models this produces 40 x 450 = 18,000 (yes, EIGHTEEN THOUSAND) opportunities for polymorphism and therefore Dependency Injection.
I was able to produce a single View module which can produce the HTML output for any transaction as a result of my choice to use XSL Transformations and a collection of reusable XSL Stylesheets. This is coupled with the fact that I can extract all the data from a Model with a single call to $object->getFieldArray() instead of being forced to use a separate getter for each column, as discussed in Getters and Setter are EVIL.
I worked with several non-OO languages for over 20 years writing enterprise applications before I switched to using PHP in 2002 with its OO capabilities, and the first thing I needed to do was to find out what OOP actually meant and why it was supposed to be better than previous programming paradigms. The initial definition that I found at that time was roughly as follows:
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.
These four characteristics can be described as follows:
|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. This binds together the data and functions that manipulate the data.
More details can be found in What is OOP? - encapsulation
|The reuse of base classes (superclasses) to form derived classes (subclasses). Methods and properties defined in the superclass are automatically shared by any subclass. A subclass may override any of the methods in the superclass, or may introduce new methods of its own.
More details can be found in What is OOP? - inheritance
|Same interface, different implementation. The ability to substitute one class for another. This means that different classes may contain the same method signature, but the result which is returned by calling that method on a different object will be different as the code behind that method (the implementation) is different in each object.
More details can be found in What is OOP? - polymorphism
|The process of separating the abstract from the concrete, the general from the specific, by examining a group of objects looking for both similarities and differences. The similarities can be defined in an abstract superclass so that they can be shared by all members of that group while the differences can be defined in separate concrete subclasses.
More details can be found in What is OOP? - abstraction
Some people seem to think that encapsulation and abstraction mean the same thing - information hiding - which is a big mistake. Encapsulation is about placing data and the operations which act upon that data in a "capsule" called a "class" while abstraction is about refactoring classes in order to share common code by inheriting it from an abstract class.
I noticed in the PHP manual that these capabilities were added to the language without removing the ability to write procedural code, so it was possible to have a mixture of procedural and OO code in the same program, thus leaving it up to the individual programmer to decide which style was best in a particular set of circumstances. This lead me to the conclusion, as documented in What is the difference between Procedural and OO programming? that:
Object Oriented programming is exactly the same as Procedural programming except for the addition of encapsulation, inheritance and polymorphism. They are both designed around the idea of writing imperative statements which are executed in a linear fashion. The commands are the same, it is only the way they are packaged which is different. While both allow the developer to write modular instead of monolithic programs, OOP provides the opportunity to write better modules.
It was quite clear to me that the objective of using OOP was to take advantage of the additional features in such a way as to increase the amount of reusable or sharable code within your application. An application consists of a number of different components (my ERP application currently has over 3,700) so it would not be unusual to find identical or similar pieces of code being used by more than one component.
Why would increasing the amount of reusable/sharable code be of any benefit? If you have a piece of logic which is common to several components the novice's method of implementing this would be to duplicate that logic, using the copy-and-paste method, in each component. This produces multiple copies of the same piece of logic in multiple places, so if a bug is ever found in that logic, or it needs to be updated, you have to find every copy of that logic in order to update it. All you have to do is miss one copy and the result could be at best unexpected and at worst unpleasant. The correct way, as practiced by experienced programmers, is to follow the DRY principle and define that logic in a single place, such as a function or a method, and then reference that function or method whenever you wish to execute that logic. This provides two enormous benefits:
It should therefore be obvious that the more reusable code you have then the less code you have to write. The less code you have to write then the less time it takes to complete a component. The less code you have to write then the less code you have to test as the reusable code will (should?) have already been tested. The less code you have to write in order to produce a component also means that you save time by not having to write as much code, and as time is money this also leads to lower costs. The ability to produce software in less time and at less cost than your rivals will always be a major factor in a competitive market.
When I came to start building my first PHP components I already had an architecture in mind which I had encountered in my previous language and which I saw could provide a solid basis for all future development. This was the 3-Tier Architecture with its separate Presentation, Business and Data Access layers. I had also encountered Extensible Markup Language (XML) and The Extensible Stylesheet Language Family (XSL), and I decided that I would create all my HTML pages using XSL Transformations. This meant that I had split my Presentation layer into two separate components thus producing an architecture that included the popular Model-View-Controller (MVC) Design Pattern. This combined architecture is shown in Figure 1:
Figure 1 - The MVC and 3-Tier architectures combined
In the following paragraphs I shall refer to various types of component using the names in the above diagram - Models, Views, Controllers and Data Access Objects (DAO). An application will contain a number of each of these component types which can be used in a variety of combinations in order to achieve different results. For example, a single Model may be referenced by any number of Controllers, and the DAO may be referenced by any number of Models. The most important layer is the Business layer, which is also known as the Domain layer, as this contains all the entities/objects and their individual business rules which are relevant to the application. The other components - the Controllers, Views and DAOs - are there as services which support the execution of the business rules.
Note here that I have introduced two categories of object. In his article How to write testable code the author identifies the following categories:
The PHP language does not have value objects, so I shall ignore them.
The components shown in Figure 1 above have been implemented as follows:
All business rules for an application exist in and only in the Business/Model layer, with a different Model component for each entity which needs to be referenced by the application. There could be hundreds of different Models in a large application.
All the service components are application-agnostic which means that they do not contain any logic or other knowledge which is specific to any application. This means that they can be used with the Model components of any application without any modification. The framework contains sets of pre-written and reusable components for these services as follows:
You must first create classes before you can make use of inheritance and polymorphism, so what you do here has a direct bearing on how much potential for reusability you will eventually create. Get it wrong and you will have limited potential. Get it right and that potential could be enormous.
Encapsulation is the act of creating a class for something which has data (state) as well as procedures (behaviour) which can operate on that data. The class then acts as the "capsule" for that data and those procedures. In OOP the data is implemented as "properties" and the procedures are implemented as "methods". Please try to avoid falling into the trap of creating anemic objects which contain state but very little behaviour. This is contrary to the basic idea of object-oriented design which is to combine data and process together.
The biggest challenge for the novice programmer is to identify which parts of an application, the "things", which need to be represented as classes, and what methods to build into each of those classes.
Those new to OOP are so dazzled by the idea that OOP "lets you model the real world" that they try to model those objects which they perceive as existing within the real world. When designing something like an e-commerce application which deals with things called PRODUCTS, CUSTOMERS and SALES ORDERS they think it would be a good idea by designing classes for each of those three objects. They are told to leave the database design till later as it is less important, a mere "implementation detail".
It is a well known fact to every experienced database designer that sometimes the data for a single "thing" in the outside world will actually need to be split across more than one table in the database. This is a result of the process called Data Normalisation. For example, a sales order may initially be regarded as a single entity, but in a database it could require multiple tables such as ORDER_HEADER, ORDER_ITEM, ORDER_ADJUSTMENT and ORDER_ITEM_ADJUSTMENT. An experienced programmer would create a separate class for each of these tables whereas a novice would create an aggregate/compound class called ORDER which would handle every table associated with an order. Having a single class which is responsible for more than one database table surely breaks the Single Responsibility Principle (SRP), which is one of the reasons why I avoid that idea like the plague.
Inheritance is the ability to reuse the contents of a base class (superclass) to create a derived class (subclass). This leads to three important questions regarding the amount of code which can be reused through inheritance:
The method taught to novice OO programmers to identify places where inheritance may be used is to carry out the IS-A test. Unfortunately the poor dears get this completely wrong. They look at an entity called CUSTOMER and say "A customer is-a person, so I must create a PERSON class and then extend this to create a CUSTOMER class". Likewise they say "We sell widgets, and as a widget is-a product I must first create a PRODUCT class and then extend this to create a WIDGET class".
If you follow the same path you will end up with a number of superclasses (PERSON and PRODUCT in the above example) and a number of subclasses (CUSTOMER and WIDGET in the above example). This to me is wrong on so many levels:
The end result of this approach would be a limited amount of inheritance, which in my opinion is a sign of failure. Not only that, problems can be caused by mis-using inheritance by extending one concrete class to create another concrete class, or by creating deep inheritance hierarchies. The solution to these problems was provided in the book Design Patterns - Elements of Reusable Object-Oriented Software which was first published in October 1994 where it says:
One cure for this is to inherit only from abstract classes since they usually provide little or no implementation.
Any experienced OO programmer should know that when looking to create an abstract class you first look for a group of business/domain entities which share a common set of characteristics. A programmer experienced with SQL will be able to immediately point to the following list of characteristics which are common to all database tables:
Note that a table may be related to itself, in which case it is both the parent and the child.
Only a novice programmer would fail to see the benefit of placing the code to deal with all those common characteristics in an abstract class which can then be inherited by any number of concrete classes. This also passes the IS-A test as it it quite plain to see that every object in the domain/business layer is-a database table. The abstract class deals with the common characteristics while the concrete class provides the details which are unique to a particular table. But how much code can I put into the abstract class? Quite a lot, actually. This answers a criticism of my approach which was given as long ago as 2003 where someone known as Jochen Daum said the following:
This means you write the same code for each table - select, insert, update, delete again and again. But basically its always the same.
This person was obviously a novice to OO as he failed to understand that instead of repeating the code to deal with the SQL queries for select, insert, update and delete in each concrete table class you can define it just once in an abstract table class and then share it with every concrete table class by using inheritance. In my ERP application I currently have over 400 database tables, so if each of those 400 table classes inherits from the same abstract class then that is a lot of code sharing.
I disagree with the notion, as contained in the above quote from the Gang of Four book, that abstract classes usually provide little or no implementation. If you have been writing database applications for as long as I have then you may actually find that the code used to communicate with a database table could be quite large. You may wish to intersperse the standard logic which constructs SQL queries with custom logic to handle the business rules, in which case a solution for this is given in the same Gang of Four book in the form of the Template Method Pattern which is described as follows:
Defines the skeleton of an algorithm in an operation, deferring some steps to subclasses. It lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
This is where an algorithm/operation requires a series of steps comprised of a number of invariant methods which have concrete implementations defined in the superclass, and variant/variable/customisable methods which do not have implementations unless they are defined in the subclass. Every subclass then shares the same invariant methods but has its own set of variant/variable/customisable methods. By following the guidelines in the Gang of Four book I have been able to create an abstract table class which contains 163 invariant methods and 95 variant methods. That is a LOT of code which is being shared.
It is not possible to take advantage of polymorphism unless you have the same method signature appearing in more than one class. These duplicate method signatures may appear by inheriting from the same superclass, but inheritance is not a requirement - it is possible to create several classes where the same method signatures are hard-coded instead of being inherited. How the duplicate methods get there is irrelevant, it is only the fact that they exist which matters. If you have a piece of code in object 'A' which calls a method in object 'B', but the same method is available in objects 'B1' to 'B99', you are then able to call that method in any of the 99 alternative objects. Although the method call is the same the object on which the call is made is different, so the results will be different as each of those 99 objects provides a different implementation of that method.
That explains the mechanics, but where can it be employed? I have already stated several important facts:
In addition my decades of experience with database applications has taught me the following:
Given the above it should be possible to create an object which encapsulates that behaviour and makes the method call on an object whose identity is not known until runtime. If you look at Figure 1 you should see that the methods in each Model (business/domain object) are accessed from a Controller, but unlike most novice programmers who create a separate Controller for each Model where the Model name is hard-coded, in my framework I have Controllers which call a known set of methods on an unknown object using a technique known as Dependency Injection. Each of the tasks in my application has its own component script which is very small as all it does is identify which Model and View are to be used before it hands control over to the Controller. Because the Controller accesses methods which are defined in the abstract table class, and because that same abstract class is inherited by every concrete table class (Model), you should see that the same Controller can be made to work with any Model.
If my framework contains 45 Controllers, and each of these can be used with any of the 400 Models in my ERP application, this means that I have 45 x 400 = 18,000 (yes, EIGHTEEN THOUSAND) opportunities for polymorphism. Is that a lot of reusability or what?
The best description of abstraction that I have ever encountered was found in a paper entitled Designing Reusable Classes which was published in 1988 by Ralph E. Johnson & Brian Foote. In it they describe the process of abstraction as separating the abstract from the concrete, the similar from the dissimilar. This is not the same as encapsulation which involves the creation of individual classes as it involves looking at a group of classes after they have been created looking for similarities and differences. If those classes share a similar set of protocols (operations or methods) then those protocols should instantly be regarded as candidates for being moved to an abstract class where they can be shared by members of that group through inheritance.
This is explained in more detail in What is "abstraction"?
An abstract class is distinguishable from a "real" (concrete) class as it cannot be instantiated into an object. It can only be inherited by a concrete subclass which can then be instantiated, thus creating an object which combines the methods within the superclass with those defined within the subclass.
An abstract class may contain either abstract or non-abstract (concrete) methods. Abstract methods cannot contain an implementation and must be overridden in the subclass. Concrete methods may contain an implementation which may or may not be overridden in the subclass. An abstract class is an essential part of the Template Method Pattern as it allows for the creation of "hook" methods which can interrupt a processing flow with custom code, where each subclass can have its own custom code.
While inheritance on its own is a valuable technique for code reuse, the Template Method Pattern is an essential part of a framework as it implements the Hollywood Principle (don't call us, we'll call you). The Template Method Pattern is used extensively in the RADICORE framework as every method called by a Controller on a Model is a template method. The standard code defined in the abstract table class handles all the standard processing, but some of these methods have been deliberately left empty so that they do absolutely nothing unless they are overridden in a subclass with code which is specific to that subclass.
My use of an abstract table class, which then allowed me to implement the Template Method Pattern for every user transaction within the application, thus producing enormous quantities of reusable code, came about because of some simple observations:
Using encapsulation, inheritance and polymorphism to create entity objects in the business/domain layer is one thing, but is it possible to create reusable service objects in the other layers? Unlike an entity which has state, data which can be accessed via multiple method calls, a service performs a single operation but does not have state, which means that it has to be provided with the necessary data on each method call. It could therefore be possible to create a single service object which can performs its function using any data instead of creating a different object for different sets of data. Below are examples of some of the reusable objects which exist in my framework.
I have seen more than one example on the internet where the novice programmer thinks that it is a good idea to create a different DAO for each table, but that is not how the DAO in the 3-Tier Architecture is supposed to work. In the first language that I used which incorporated a DAO this was an object which could deal with any table in a particular DBMS, which enabled the entire application to be switched from one DBMS to another simply by changing a single component. This behaviour is what I have duplicated in my PHP framework as I have available a separate DAO for each DBMS which I support. I started with MySQL, but later I added PostgreSQL, then Oracle and eventually SQL Server. This is made possible by the fact that each of the methods in the DAO which constructs and then executes the relevant query string includes in its arguments the database name, the table name, and the relevant column names with their values.
Some novice programmers question the idea of having a separate DAO as they think that once an application has been installed with a particular DBMS then it is highly unlikely that the DBMS will be changed. I would largely agree with that sentiment, but what about being able to choose the DBMS before the application is installed? I have developed an open source framework which can be downloaded and used by any team of developers, but I do not restrict it to be used with a single DBMS, I give the developer a choice. I have used the same framework to build a large ERP application as a package which can be used by multiple organisations, and this allows customers to choose the DBMS that they prefer before they install it.
In a web application all the screens which are shown to the user are constructed in the Presentation layer as HTML documents which conform to a standard which is (or supposed to be) supported by every web browser. Each HTML document is simply a large string of text with pieces of data enclosed in HTML tags. As I had become familiar with the use of XML and XSL in my previous language, where an XML document contains nothing but data while an XSL stylesheet contains a number of templates which can transform that data into HTML, I immediately saw the benefit of employing the same process in my own framework. I thus created a single View component which could extract the data from the Model(s), put that data into an XML document, load in the designated XSL stylesheet, then perform an XSL Transformation to create the HTML output which is then returned to the client's browser.
The View object does not contain any Model names as it functions using Dependency Injection where it is given an array of one or more objects and it calls standard methods on each of those objects to extract whatever data that it/they contain. These standard methods are inherited from the abstract table class, so they do not have to be duplicated in any Model class.
In my original implementation I had a separate XSL stylesheet for each different web page as they each required different columns to appear in different places with different controls. After producing a number of these stylesheets I could see a large amount of code which was similar while the only difference was the list of column names and their HTML controls. After a bit of experimentation I managed to remove the need for a multitude of customised stylesheets and replaced them with a small library of reusable XSL stylesheets. I did this by adding a <structure> element to the XML document which contains enough information to allow the XSL stylesheet to construct the variable application area within each HTML page. The contents of each <structure> element is obtained from a small screen structure script which also identifies the XSL file which is to be used.
By examining a large number of different web pages I could see a series of patterns emerging, and I managed to create a single reusable stylesheet for each pattern. This means that when creating a new task with a web page the developer does not need to spend any time on the standard parts of each page as this is already handled by the library of XSL stylesheets provided by the framework.
Not all tasks produce output which is displayed in a web page. Some use a PDF document, some use a CSV file, while some do not have any output at all. They simply do something without the aid of any dialog with the user before returning control to the component from which they were called. Just as I have a single class which produces all HTML output, I also have single classes for all PDF and CSV output.
The idea of having a small number of reusable Controllers is an impossible dream for novice programmers as the way they are taught to design their applications precludes this possibility. They are taught that when they have identified a task (use case or user transaction) that they should create a class with a method whose name corresponds with that particular task. Using this methodology they end up with method names such as
createShipment(). This is a totally bad idea as it means that in a large application with 3,000 tasks you end up with 3,000 unique method names, and this totally eliminates any possibility for code reuse via polymorphism.
As explained in Step 3 above you need to have identical method signatures appearing in multiple objects in order to provide the opportunity for polymorphism. Once you have done this you can produce components which call one or more of these methods on any of these interchangeable objects using the principle of Dependency Injection. As each of the above methods has the end result of inserting a record into a database table you can replace all those Controllers which call those unique methods with a single Controller which calls the generic
insertRecord() method on whatever table object is provided at runtime.
By recognising that each of the tasks (user transactions) in my application conforms to a pattern which may be repeated I have been able to create a library of reusable Transaction Patterns each of which has its own Controller script which calls a predetermined set of generic methods on one or more unknown Models (table classes). This means that instead of having a separate Controller for each Model which can only call methods which are unique to that Model I can have a separate Controller for each pattern of behaviour which can be applied to any Model. In this way I can apply the same pattern of behaviour to any table in my database by reusing the pattern instead of writing code to duplicate the behaviour.
I have so far identified and created 45 such Transaction Patterns which between them are used in over 3,700 tasks within my ERP application. Some of these patterns are used hundreds of times, some are used dozens of times while others are used only in rare circumstances.
Having code which you can reuse means that when you are writing a new component you can call this reusable code instead of having to duplicate it. In some cases this can make the creation of a new component so simple that it can be automated, which means that instead of writing the code yourself you can have it generated for you by pressing a button. Below are some of the code generation facilities which are included in my framework.
As pointed out previously my business/domain layer has a separate class for each database table. As a significant portion of the code which deals with the throughput of data from the Presentation layer to the Data Access layer and back again is essentially the same this has allowed me to identify a great deal of code which can be written once and then shared through inheritance from an abstract table class. Experienced database developers will immediately point out that each table has its own business rules, but these can be added in later as variant methods courtesy of my implementation of the Template Method Pattern.
Each table also has it own structure which provides the details for all those common characteristics, and it is these missing details which help to turn an abstract class into a concrete class. My previous language used an internal Data Model which was used to generate the necessary CREATE TABLE scripts, but in my PHP implementation I decided to do the reverse. Instead of maintaining the Data Model and then exporting to the database I import from the database into my version of the Data Model which I now call a Data Dictionary, then I export each table's data from the Data Dictionary into a series of PHP scripts which are then accessed at runtime. I originally created these PHP scripts by hand, but later I wrote a program to do it for me. In my Data Dictionary I built a process to export each table's data to two separate files:
The reason why I create two separate files is quite simple - after the class file has initially been created it may be amended later on to provide implementations for any of the variant/customisable methods, so this class file is never overwritten during the export process. It is also possible for the table's structure to change after it was first created, so all that is necessary is to re-import the changed structure and re-export the updated details. This will replace the contents of the structure file but not touch the class file.
As stated previously each task (use case or user transaction), regardless of its complexity, performs one or more operations on one or more database tables. This is a mixture of standard code which is provided by the framework and custom code to handle the business rules for that table and/or task. By using the Template Method Pattern the standard code is implemented within invariant methods which are defined in the abstract table class while the business rules are implemented within the variant/customisable methods which are defined within each concrete table class. The actual behaviour of each task has been implemented as a series of Transaction Patterns which have been built into the framework.
After generating a class file it is then necessary to create the tasks which allow the user to put data into and get data out of that table. In my framework each task has an entry in the MNU_TASK table in the MENU database which points to a small component script in the file system. I do not have a single task which performs all the possible operations on a table, instead I create a family of forms where each operation is carried out by a separate task. This is for reasons discussed in Component Design - Large and Complex vs. Small and Simple. Again I stated off by doing this all by hand, but later on I wrote another program to do it all for me. Within my Data Dictionary I now have a process which allows me to select a database table, select a Transaction Pattern, then press a button to create all the necessary database records and scripts.
At this point each task is in a basic but runnable state. Although the table class does not yet contain any variant/customisable methods for any specific business rules, it has enough logic to put data into and get data out of the database. Even the validation of user input is taken care of by the built-in validation object which checks all data with the contents of the table's $fieldspec array.
If you have been paying attention you should have noticed that once I have created a database table I can import that table's details into my Data Dictionary, press a button to create the corresponding class file, then press another button to generate the tasks to maintain that table which are then immediately runnable. This entire process can be achieved in 5 minutes without having to write a single line of code - no PHP, no SQL and no HTML. If you cannot achieve the same level of productivity with your methodology then I would suggest that you need to examine your approach and lean how to shift it into a higher gear.
Apart from creating reusable code by utilising encapsulation, inheritance and polymorphism, it is also possible to create it by building a library of functions and subroutines. Some libraries can be quite small while others can be quite large, but did you know that there is something which exists at a higher level above a library? In case you are a novice programmer who is still groping around in the dark I shall enlighten you - it is a thing called a framework. If you don't understand the difference between a library and a framework please read What is a Framework?
Instead of having to write your own code to call library functions a framework will create code with basic functionality which you can then alter by extending or overriding the framework code by adding implementations to the variant/customisable methods which are available through my use of the Template Method Pattern. This is the mechanism by which the flow of control is dictated by the framework instead of the caller. The invariant methods in the abstract class are always called, and the empty variant/customisable methods can be overridden in each concrete class to supply additional behaviour.
A proper framework will also contain built-in components to handle that functionality which is common to all the domains/subsystems within the application, for example:
The only definition of OOP which I find acceptable goes as follows:
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.
OOP is supposed to be better than earlier programming paradigms because it provides features which were designed specifically
to increase code reuse and decrease code maintenance. The more reusable code you have at your disposal then the less code you have to write. The less code you have to write to get the job done then the less time it will take to get the job done, and getting the job done in less time will make you more productive. However, unless you take advantage of these features and use them appropriately to actually produce more reusable code then your efforts will be wasted.
I built my PHP framework based on 20 years of prior experience with developing database applications, with their associated frameworks, in two different languages. I taught myself to use encapsulation, inheritance and polymorphism without being sidetracked by inappropriate practices. I did not follow these practices simply because I did not know they existed. Instead I used my own experience and intuition to create a framework for writing web applications which contained as many standard and reusable components as possible, and this has made me more productive than I have ever been, and more productive than any of my critics.
The amount of reusable code which is provided by my framework equates to large volumes of code that I DON'T have to write. Examples of this code is as follows:
If writing less code is a laudable aim, then how about the ability to write no code at all? As mentioned above in Generating Tasks I can create a new table in my database, then simply by pressing some buttons I can create and then run the basic tasks to maintain that table in just 5 minutes without writing a single line of code - no PHP, no SQL, no HTML. How much code do you have to write?
Here endeth the lesson. Don't applaud, just throw money.
The following articles describe aspects of my framework:
The following articles express my heretical views on the topic of OOP:
These are reasons why I consider some ideas to be complete rubbish:
Here are my views on changes to the PHP language and Backwards Compatibility:
The following are responses to criticisms of my methods:
Here are some miscellaneous articles: