Tony Marston's Blog About software development, PHP and OOP

Are you achieving the aims of OOP?

Posted on 1st August 2019 by Tony Marston

Amended on 12th May 2024

Introduction
Universal rules
Inappropriate rules
What is OOP?
Encapsulation
Inheritance
Polymorphism
Abstraction
What is the benefit of reusable code?
How do you create reusable code?
Step 1 - Encapsulation
Step 2 - Inheritance
Step 3 - Polymorphism
Step 4 - Abstraction
Other ways to write less code
Creating reusable services
Reusable Data Access Objects
Reusable Views
Reusable Controllers
Generating components
Generating Classes
Generating Tasks
A final area of reusability
Summary
References
Amendment History
Comments

Introduction

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 are supposed to better because of the following:

Object Oriented Programming is programming which is oriented around objects, thus taking advantage of Encapsulation, Inheritance and Polymorphism to increase code reuse and decrease code maintenance.

This sentiment is echoed in Designing Reusable Classes which was published in 1988 by by Ralph E. Johnson & Brian Foote where they say:

Since a major motivation for object-oriented programming is software reuse, this paper describes how classes are developed so that they will be reusable.

In this paper they describe how looking for abstractions can lead to a technique for building classes which they refer to as programming-by-difference, which can be summarised as follows:

Abstraction is the act of separating the abstract from the concrete, the similar from the different. An abstraction is usually discovered by generalizing from a number of concrete examples. This then permits a style of programming called programming-by-difference in which the similar protocols can be moved to an abstract class and the differences can be isolated in concrete subclasses.

Even though their paper was written with the Smalltalk language in mind (which is strictly typed) which means that some parts of it are irrelevant when it comes to PHP (which is dynamically typed), there are other parts which are still relevant, as explained in The meaning of "abstraction".

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.

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. The less time it takes to get the job done then the more productive you will be, and achieving high productivity is the best way to win friends and influence people. In the RADICORE framework, where each application subsystem is treated as a plug-in, each user transaction (use case) will be comprised of the four different elements which are shown in Figure 1 below:

Figure 1 - A combination of the 3-Tier Architecture plus MVC

model-view-controller-03a (5K)

There is also a version of this structure which is more detailed.

So, how many of the above four components do you NOT have to write?

The surprising thing is that I achieved this level of reusability by not following those ideas called "best practices". This is because when I started building my PHP framework in 2002 I did not know they existed. When I was made aware of them some years later I could see straight away that by amending my code to follow them I would end up with much more code and less reusability, so I decided to ignore them altogether. While I have identified a small selection of rules which I regard as being universally applicable, I have also identified some rules which I do not follow as I consider them to be an obstacle to reusability and therefore totally inappropriate. The remainder of this article documents the steps I took when building my framework in order to maximise the amount of reusable code which it produced.

Universal rules

These are the only rules/guidelines I have encountered which can be said to be universally applicable. See if you agree.

  1. The primary "rule" that I have followed in all the languages that I have used is based on the following statement from Abelson and Sussman in their book Structure and Interpretation of Computer Programs which was first published in 1985:
    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:

  2. Try to follow is the KISS principle. This means that if you have a choice between a simple solution and a complex solution, then you should ALWAYS choose the simplest one as it will pay dividends in the long run. Another way of expressing this principle is as follows:
    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.

  3. Try to follow the DRY principle. This means that you should avoid having the same piece of logic duplicated in multiple places as any change to that logic, such as fixing a bug or adding an enhancement, would have to be duplicated in all of those places. If you missed out just one of those places then your application could behave in an unexpected manner. It would be better to put that logic into a reusable library so that it can be defined once and reused many times.
  4. If it ain't broke don't fix it is a principle which originated in the world of mechanical/electrical engineering, but is just as applicable in software engineering. It means that once you have found a simple solution that works then don't try to fiddle with it to make it "better", "purer" or the mythical "perfect" as your efforts could have the opposite effect. The practice of constantly fiddling with working software just to make it "better" is attacked in When is Enough, Enough?
  5. Don't violate the YAGNI (You Aren't Gonna Need It) principle. Don't insert a solution to a problem which does not exist in your code. Just because a new feature is added to the language does not mean that you should immediately change your code to implement this feature. Unless you actually have the problem for which the feature is a solution then adding this solution will be a complete waste of time and achieves nothing except add to code bloat. This idea is discussed in the following:
  6. Prevention is better than Cure is a principle which states that if you do something which causes a problem then instead of trying to deal with that problem a better solution would be not to cause that problem in the first place. A typical example is where the database is designed using one methodology while the software is designed using a different and totally incompatible methodology. Because they are incompatible they produce a problem known as Object-Relational Impedence Mismatch which is often solved by introducing an extra piece of software known as an Object-Relational Mapper (ORM). I avoid this problem by *NOT* designing my software using an incompatible methodology. Instead I design my database first, then build a separate class for each table which has knowledge of that table's structure. Each table class inherits common methods which correspond directly with the ubiquitous CRUD operations. By eliminating the mismatch I have eliminated the need for an ORM.

    It has been my experience that the structure of the database takes priority over the structure of your code, and it is easier to structure your code around the database design than to structure your database around the software design. Eric S. Raymond, the author of The Cathedral and the Bazaar put it like this:

    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.
  7. Avoid putting all of your code into a single monolithic procedure as the different pathways through that code can be difficult to spot. DO NOT create a single object called "application" with a single method called "run". Instead take a large procedure which does lots of things into a number of smaller procedures each of which does a single thing. This is known as Modular Programming and makes it possible to produce a structure chart which is a great documentation aid. As well as being able to describe the structure of an individual program it should also be possible to produce a structure chart for the entire application, as shown in this diagram of the RADICORE framework, for example.
  8. Try to achieve the best level of cohesion. High cohesion is better than Low cohesion. High cohesion is achieved when related functions are grouped together in the same module. Low cohesion is achieved when related functions are spread across different modules, or when a module contains functions which are not related. High cohesion can be achieved by producing modules which each have a single responsibility or concern. This can be done, for example, by implementing the 3 Tier Architecture or Model-View-Controller design pattern.
  9. Try to achieve the best level of coupling. Loose coupling is better than Tight coupling. Loose coupling will help avoid the ripple effect where a change to one module leads to corresponding changes in other modules. This can be achieved by having high levels of polymorphism using stable interfaces which are resistant to change, and this in turn promotes the use of dependency injection.

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.

Inappropriate rules

It was not until several years after I had starting publishing what I had achieved that some so-called "experts" in the field of OOP informed me that everything I was doing was wrong, and because of that my work was totally useless. When they said "wrong" what they actually meant was "different from what they had been taught" which is not the same thing. What they had been taught, and which is still being taught today, is that in order to be a "proper" OO programmer you must follow the following sets of rules:

When I examined these principles I took an instant dislike to them for the simple reason that by changing my code to follow them I would be destroying vast amounts of reusable code, so I decided to ignore them. I have documented criticisms of some of these rules in the following:

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 as software which is not effective should be regarded as being unacceptable no matter how low the price. 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 and pedantic 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:

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 above.

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 45 Controllers and 400 Models this produces 45 x 400 = 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.


What is OOP?

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, Inheritance and Polymorphism to increase code reuse and decrease code maintenance.

These three characteristics can be described as follows:

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. This binds together the data and functions that manipulate the data.

More details can be found in What is OOP? - encapsulation

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. 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

Polymorphism Same interface, different implementation. The ability to substitute one class for another. By the word "interface" I do not mean object interface but method signature. 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

Some programmers insist that there is a fourth component called abstraction, but I disagree. This is a mental process which cannot be performed in code, but which helps you design code which can be reused.

Abstraction 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 and The meaning of "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 and thus reduce the amount of code which you have to write yourself. 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.


What is the benefit of reusable code?

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.


How do you create reusable code?

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 above.

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:

  1. Value objects - an immutable object whose responsibility is mainly holding state but may have some behavior. Examples of Value Objects might be Color, Temperature, Price and Size.
  2. Entities - an object whose job is to hold state and associated behavior. Examples of this might be Account, Product or User.
  3. Services - an object which performs an operation. It encapsulates an activity but has no encapsulated state (that is, it is stateless). Examples of Services could include a parser, an authenticator, a validator or a transformer (such as transforming raw data into XML or HTML).

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:

Step 1 - Encapsulation

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.

Step 2 - Inheritance

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:

  1. How many superclasses do you have?
  2. How much code is in each superclass?
  3. How many subclasses are there for each superclass?

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:

  1. Each table has a unique name within its database. Note that an application may access tables across multiple databases. My ERP application, for example, contains over 15 separate domains/subsystems, and each has its own database.
  2. Each table has a structure comprised of one or more columns. Each column name must be unique within its table.
  3. Each column has a data type which is taken from a known list.
  4. Each table has a primary key which is comprised of one or more columns which provides a unique identity for each record in that table.
  5. Each table may have a number of additional unique keys which are known as candidate keys.
  6. Each table may be linked to another table in what is known as a one-to-many or parent-to-child/senior-to-junior relationship. Each relationship is between two tables where the "one" is the "parent/senior" and the "many" is the "child/junior":

    Note that a table may be related to itself, in which case it is both the parent and the child.

  7. Regardless of a table's structure it it subject to exactly the same operations - Create, Read, Update and Delete. Each of these is constructed as a query string which follows a uniform pattern, so is a prime candidate for abstraction.

In the RADICORE framework the abstract table class contains a set of common table methods and a set of common table properties.

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 Create, Read, Update and Delete operations 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 450 database tables, so if each of those 450 table classes inherits from the same abstract class then that is a lot of code sharing. As the rules for generating and executing SQL queries are exactly the same irrespective of which table you are handling this is covered by a pre-written Data Access Object, so there is no code duplication there.

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 "hook" 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 220 invariant methods and 150 variant methods. That is a LOT of code which is being shared.

Step 3 - Polymorphism

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:

  1. Every object in my domain/business layer is a database table which has its own class.
  2. Every concrete table class inherits a great deal of sharable code from the same abstract table class.
  3. Every concrete class therefore automatically contains the methods to perform the Create, Read, Update and Delete (CRUD) operations as these are inherited from the abstract class.

In addition my decades of experience with database applications has taught me the following:

  1. Every task (use case or user transaction) achieves its purpose by performing one or more CRUD operations on one or more database tables where these operations may be interspersed with business rules. Each table will have its own rules, and there may be rules which are specific to a particular task.
  2. Each task may be categorised by its behaviour (what operations it performs) and its content (the objects on which it operates).
  3. When you have written a large number of different tasks on a large number of different database tables you may see, as I have, that some of these tasks have the same behaviour but different content.

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?

Step 4 - Abstraction

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 and 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. Note that inheriting from an abstract class is the preferred method as it avoids those problems which can be encountered when inheriting from one concrete class to create a different concrete class.

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 supplied 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 a series of steps containing a mixture of invariant methods and "hook" methods which can interrupt the 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:


Other ways to write less code

Putting repeating code into reusable components so that it can be called multiple times instead of being duplicated multiple times is one way of writing less code, but there are other techniques which you can employ which allow you to achieve a result with less code. Examples which I use are as follows:


Creating reusable services

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.

Reusable Data Access Objects

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.

Reusable Views

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.

Reusable Controllers

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 createCustomer(), createProduct(), createOrder() and 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 4,000 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.


Generating components

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.

Generating Classes

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.

Generating Tasks

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.


A final area of reusability

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:


Summary

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.

References

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 on how to do OOP "properly" 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:


Amendment History

12 May 2024 Added Other ways to write less code
09 Dec 2023 Added Abstraction and Step 4 - Abstraction
Added Inappropriate rules

counter