In the world of computer programming in general, and object-oriented programming in particular, there are numerous concepts, terms and principles being banded around which can mean different things to different people, which may be implemented in numerous different ways, and where each different implementation has its own set of pros and cons. Some programmers follow these concepts, terms and principles in a purely dogmatic fashion while others have a more pragmatic approach. I have put this little impromptu quiz together so that you may identify whether you are a dogmatist or a pragmatist.
$result = $dbobject->update($_POST['userID'], $_POST['email'], $_POST['firstname'], $_POST['lastname'], $_POST['address1'], $_POST['address2'], $_POST['city'], $_POST['province'], $_POST['country'], );
<?php $dbobject = new Person(); $dbobject->setUserID ( $_POST['userID' ); $dbobject->setEmail ( $_POST['email' ); $dbobject->setFirstname ( $_POST['firstname'); $dbobject->setLastname ( $_POST['lastname' ); $dbobject->setAddress1 ( $_POST['address1' ); $dbobject->setAddress2 ( $_POST['address2' ); $dbobject->setCity ( $_POST['city' ); $dbobject->setProvince ( $_POST['province' ); $dbobject->setCountry ( $_POST['country' ); if ($dbobject->updatePerson($db) !== true) { // do error handling } ?>
<?php $table_id = 'whatever'; ..... require_once 'classes/$table_id.class.inc'; // $table_id is provided by the previous script $dbobject = new $table_id; $result = $dbobject->updateRecord($_POST); if ($dbobject->errors) { // do error handling } ?>
$User_d = $dbobject->getUserID(); $Email = $dbobject->getEmail(); $Firstname = $dbobject->getFirstname(); $Lastname = $dbobject->getLastname(); $Address1 = $dbobject->getAddress1(); $Address2 = $dbobject->getAddress2(); $City = $dbobject->getCity(); $Province = $dbobject->getProvince(); $Country = $dbobject->getCountry(); if ($dbobject->updatePerson($db) !== true) { // do error handling } ?>
$fieldarray = $dbobject->getFieldArray();
$object->load($_POST); $object->validate(); $object->store();
$object->insertRecord($_POST); OR $object->updateRecord($_POST);
Computer programming is an art, not a science, for which you need talent to be any good. A person without talent cannot read a book on playing a musical instrument and become a musician, or read a book of recipes and become a chef, or read a book on how to use a hammer and chisel and become a sculptor. If you don't have the necessary talent to begin with you will never be anything more than a "dabbler" instead of being a "master".
A programmer who cannot produce cost-effective software and justify his salary will be a liability and not an asset, and will eventually be found out and fired.
You should always follow the KISS principle and not the KICK principle.
Erich Gamma himself, one of the Gang of Four, said the following in an interview:
Do not start immediately throwing patterns into a design, but use them as you go and understand more of the problem. Because of this I really like to use patterns after the fact, refactoring to patterns.
Trying to use all the patterns is a bad thing, because you will end up with synthetic designs - speculative designs that have flexibility that no one needs. These days software is too complex.
A lot of the patterns are about extensibility and reusability. When you really need extensibility, then patterns provide you with a way to achieve it and this is cool. But when you don't need it, you should keep your design simple and not add unnecessary levels of indirection.
If he says that you should use patterns sparingly, then who are you to argue?
Alan Kay, the man who invented the term, said that any language which did not support these three concepts had no right to call itself "object oriented". In his paper called Why C++ is not just an Object Oriented Programming Language the author Bjarne Stroustrup said the following:
A language or technique is object-oriented if and only if it directly supports:
- Abstraction - providing some form of classes and objects.
- Inheritance - providing the ability to build new abstractions out of existing ones.
- Runtime polymorphism - providing some form of runtime binding.
So if those two say that then who are you to argue?
Both paradigms are designed around the idea of writing imperative statements which are executed in a linear fashion, but OOP also supports the concepts of encapsulation, inheritance and polymorphism. The commands are the same, it is only the way in which they are packaged which is different.
I have seen far too many descriptions of OOP which fail to identify what OOP has which other paradigms do not, and what advantages these differences have to offer. Any description which fails to identify encapsulation, inheritance and polymorphism which can be used to increase the amount of reusable code is therefore not painting a clear picture. Explanations should be simple and concise, and should not use 10 dollar words to explain 10 cent concepts.
Explanations which are not simple, concise and precise are confusing to the novice developer. The use of clever words which can be interpreted in several different ways often leads to different and sometimes conflicting descriptions. If an error creeps in then that error can grow in subsequent descriptions.
Before you can encapsulate something you first have to identify that "thing". It should be an entity which will be of interest to your application, something which has data and operations which can manipulate that data. You then create a class for that entity, define all its data as properties (also known as attributes) and its operations as methods. Note that ALL the data and ALL the operations for an entity SHOULD be placed in the same class. If you split the class into numerous smaller classes then you will be reducing cohesion and increasing coupling.
Modelling the "real world" is not a clever idea unless you are writing software which communicates directly with objects in the real world. When writing a database application which deals with entities such as "Customer" and "Product" you will not be communicating with real customers or real products, just with the data about those entities which is held in a database. A database is a collection of things called "tables", so there will be a "Customer" table and a "Product" table. Designing code which communicates directly with the relevant objects would always be my first choice.
The answer to question 8 stated that you first need to identify all the entities which will be of interest to your application and then create a separate class for each entity. It is possible that you may recognise an entity in the real world which, after the process of data normalisation, results in more than one table in the database, such as the concept of an ORDER as shown in the following diagram:
A compound "order" object
It would be a great mistake to create a single class to encapsulate the concept of an order for the simple reason that this is a compound entity which results in multiple tables in the database. As each table in the database can be regarded as being a separate entity in its own right then creating a compound/composite class which is responsible for multiple tables would break the Single Responsibility Principle. It would also create the following problems:
Note that there is no communication between the database entity and the "real world" entity, so designing software which appears to communicate with an object with which there is no actual communication, then being forced to write additional code (such as an Object-Relational Mapper) to deal with the translation from a theoretical entity to an actual entity (or entities), strikes me as being totally illogical.
The biggest error with most descriptions of encapsulation is that far too many people seem to think that it must enforce visibility restrictions on the data. This is wrong! ENCAPSULATION WAS NEVER MEANT TO MEAN DATA HIDING! If you don't believe me take a look at:
If you extend ClassA into ClassB then ClassB ends up as a combination of everything in ClassA plus whatever is defined in ClassB. ClassB can be used to either override properties and methods in ClassA or to define new properties and methods of its own.
When you inherit from a class (abstract or concrete) which contains one or more non-abstract methods you use the extends keyword. This is called implementation inheritance.
When you inherit from an interface (an abstract class which contains nothing but abstract methods) you use the implements keyword. This is called interface inheritance.
Note that the creators of these two concepts got their knickers in a twist as the implements keyword does NOT give you implementation inheritance. Go figure!
Options (a) and (b) are often overused which can cause problems for which the solution is the Composite Reuse Principle which is commonly referred to as favour composition over inheritance
. Refusing to use inheritance altogether just because some usages may cause problems strikes me as being an over-reaction, like amputating a leg just because the toenails are too long.
I never inherit from one concrete class to form another concrete class, and I never create deep inheritance hierarchies, so I automatically avoid the problems which other people encounter.
In his article Object Composition vs. Inheritance the author Paul John Rajlich wrote the following:
Most designers overuse inheritance, resulting in large inheritance hierarchies that can become hard to deal with. Object composition is a different method of reusing functionality.
....
However, inheritance is still necessary. You cannot always get all the necessary functionality by assembling existing components.
....
The disadvantage of class inheritance is that the subclass becomes dependent on the parent class implementation. This makes it harder to reuse the subclass, especially if part of the inherited implementation is no longer desirable. ... One way around this problem is to only inherit from abstract classes.
The same idea was also mentioned in the Gang of Four book where, in the section titled Inheritance versus Composition, it says the following:
Implementation dependencies can cause problems when you are trying to reuse a subclass. Should any aspect of the inherited implementation not be appropriate for new problem domains, the parent class must be rewritten or replaced by something more appropriate. This dependency limits flexibility and ultimately reusability. One cure for this is to inherit only from abstract classes, since they usually provide little or no implementation.
That last statement inherit only from abstract classes since they usually provide little or no implementation
is contradicted later on in the book when it describes the Template Method Pattern which uses a mixture of unvarying methods (with implementations) and variable methods (without implementations) in an abstract class. That chapter specifically states that Template methods are a fundamental technique for code reuse
, which means that the more template methods you have the more implementations you have. In my own framework every method called by a Controller on a Model is defined within the same abstract class, so that adds up to a lot of methods.
Having large amounts of code which are reused in large numbers of places provides much more reusability than small amounts of code which are reused in small numbers of places.
In my framework every method which is called by a Controller on a Model is an instance of a Template Method, which explains why my abstract table class is so large. That single abstract class is inherited by every one of my Model classes (I currently have over 400) so that produces a HUGE amount of reusability. That's much better than having 100 abstract classes that are only inherited 4 times each.
Some people assume that polymorphism can only exist through inheritance, but this is not the case. It is perfectly possible for method signatures and their implementations to be hard-coded into a class without any sort of inheritance. For polymorphism to exist all you need is the same method signature to be defined in more than one concrete class. This is usually achieved through inheritance, but need not be.
You must have polymorphism before you can use dependency injection, so it helps to produce reusable code which is the major objective of OOP.
The fact that polymorphism provides identically-named operations which behave differently in different contexts
does not really identify how you can take advantage of this situation. This is where Dependency Injection comes into play as it describes a mechanism whereby objects which have the same method signature can be swapped around at runtime to produce different results.
For example, in my framework every concrete table class has an insertRecord() method which is inherited from the abstract table class. This allows data to be inserted into that table provided that it passes all the validation rules. I have a Controller which calls this method, but instead of having the table name hard-coded into each Controller (which would require multiple Controllers) I inject it from above. This means that instead of:
$dbobject = new Customer; $fieldarray = $dbobject->insertRecord($_POST);
I can have:
$table_id = 'whatever'; ..... $dbobject = new $table_id; $fieldarray = $dbobject->insertRecord($_POST);
Where the value of $table_id can be set to any one of my 400 concrete table classes.
Let me emphasis this point. This technique means that instead of having a separate Controller to handle the inserts for each of my 400 table classes I have a single Controller which can handle the inserts for ANY of those 400 table classes. Is that a good example of reusability or what?
Option (a) would be a waste of time if you did not have a choice of different objects to inject as without that choice you would be providing a mechanism that would never be used, thus violating YAGNI.
If you read what Robert C. Martin wrote in his article The Dependency Inversion Principle (PDF) you will see where he gives an example of a "Copy" program to show how this principle can be used. This program is used to copy data from one device to another, but where the actual implementation of the "read" and "write" methods can vary for each device. This means that the "Copy" program itself would need to be changed should any implementation change for any device, or even to add a completely new device.
The solution proposed by Uncle Bob was to create a separate class/object for each device, each with its own implementations for the "read" and "write" methods, and to inject the relevant device objects into the "Copy" program so that all it need to is call the $deviceIN->read() and $deviceOUT->write() methods to complete its assigned task. This means that you could create objects for devices and inject them into the "Copy" program without ever having to change that program.
This technique is called Dependency Injection because the "Copy" program is dependent on the device objects to complete its assigned task, and these objects are instantiated externally and then injected instead of being instantiated internally.
Note here that there are several different device objects having the "read" and "write" methods (thus producing polymorphism) which means that there is a choice of different objects which can be injected at runtime. If you do not have a choice of objects to inject then implementing this technique which deals with a choice of objects would be a waste of time.
For a more detailed discussion on this topic I invite you to read Dependency Injection is EVIL.
Designing software to deal with theoretical entities only to discover later that in reality these entities are totally different has always seemed like a stupid idea to me. When I first started to write database applications using poorly structured code I encountered problem after problem. The solution, as taught to me in Jackson Structured Programming, was to design a program structure which was built around the database structure, thus eliminating the problems instead of writing code to work around them. This is a philosophy which I have followed ever since, and I will not change it for anyone. This is why I always start by designing a properly normalised database, then use each table's structure to create a class for that table. This is a standard process that I use for each table, so I have managed to automate it by building a Data Dictionary into which I can import each table's structure at the press of a button, then export that structure into a class file at the press of another button.
Being able to create fully-functioning table classes by pressing a few buttons instead of writing reams of code seems like a good idea to me, especially when you create large applications with hundreds of table classes.
Option (a) is used only by retards who don't understand SQL. Someone writing a database application who doesn't understand SQL is just as useless as someone writing a web application who doesn't understand HTML. If you don't understand how the objects with which your software is communicating actually work then how can you possibly believe that you can write cost-effective software?
For a more detailed discussion on this topic I invite you to read Object-Relational Mappers are EVIL.
Option (a) is used only by those who don't know any better.
Each use case (user transaction or task) is designed to perform a function for the user, such as "Create Product", "Create Customer", "Create Invoice" and "Pay Invoice". Programmers who have been badly taught create a separate method for each use case and end up with a list of methods such as the following:
This method has so-o-o many disadvantages:
Option (b) only becomes apparent when you realise that every use case, regardless of what function it carries out for the user, always follows the same pattern: it performs one or more CRUD operations on one or more tables, and may process business rules during either or both of the input and output phases. In my framework each concrete table class inherits standard methods to perform those CRUD operations from a single abstract table class. I add the functions "Create Product", "Create Customer", "Create Invoice" and "Pay Invoice" to my menu database as a TASK which identifies which component script in the file system will perform that task, and I add each task to a MENU which is used to show the user which functions he is allowed to perform. When the user selects one of those menu options it runs the nominated component script which then calls the necessary CRUD operations on the specified table.
Each method that the Controller calls on a Model is available in every Model by virtue of the fact that it is defined in the abstract table class which is inherited by every concrete table class. Each method is an instance of the Template Method Pattern which contains a mixture of invariant and variable methods. Standard boilerplate code is defined in the invariant methods in the superclass while the variable hook methods can have their implementations defined within each individual subclass.
It is a feature of my framework that each Controller script performs its operations on an unspecified Model and can therefore be used with ANY Model. At runtime the identity of the Model to be used is specified in the component script before it hands over control to a particular Controller. This is my form of dependency injection which is only possible because I have so much polymorphism available due to the fact that every concrete table class inherits from the same abstract table class. Below is an example of the "traditional" way compared with the heretical "Tony Marston" way.
traditional | effect on the database | the Tony Marston way |
---|---|---|
createProduct() | insert a record into the PRODUCT table |
$table_id = 'product'; .... require "classes/$table_id.class.inc"; $dbobject = new $table_id; $result = $dbobject->insertRecord($_POST); |
createCustomer() | insert a record into the CUSTOMER table |
$table_id = 'customer'; .... require "classes/$table_id.class.inc"; $dbobject = new $table_id; $result = $dbobject->insertRecord($_POST); |
createInvoice() | insert a record into the INVOICE table |
$table_id = 'invoice'; .... require "classes/$table_id.class.inc"; $dbobject = new $table_id; $result = $dbobject->insertRecord($_POST); |
payInvoice() | insert a record into the PAYMENT table |
$table_id = 'payment'; .... require "classes/$table_id.class.inc"; $dbobject = new $table_id; $result = $dbobject->insertRecord($_POST); |
My version of the payInvoice() method does away with those stupid arguments I have seen on the interweb where some people say it should be $invoice->pay() while others say it should be $pay->invoice(). In my simplified world the paying of an invoice results in the INSERT of a record into the PAYMENT table which means that I can use the generic insertRecord() method on the PAYMENT object. It's not rocket science!
As you should be able to see the "traditional" way uses large amounts of unique and therefore unsharable code whereas my "heretical" way uses non-unique sharable code in each Controller that can be used with any Model. This is only possible because of the way I have implemented inheritance to provide maximum polymorphism which then enables the most efficient use of dependency injection. My use of the Template Method Pattern gives each Model instant access to large amounts of standard boilerplate code which can be easily augmented using the numerous hook methods.
Too many people think that (a) is the rule because they have not seen any examples which show otherwise.
When you have built as many user transactions with screens that I have (and I have built thousands) you should notice that each screen can be broken down into different zones or areas. Some of these zones can be filled in by the framework while others are supplied by the application. While some screens deal only with a single application component there may be others which deal with a hierarchy of components where each component has a separate object in the Controller and a separate zone on the screen. This is what I was used to in the last language which I used before switching to PHP, so I saw no reason why I should not continue with that practice.
This means, for example, that if you have two tables in a parent-child relationship, such as CUSTOMER and ORDER, you may wish to have a screen which shows a single CUSTOMER in the top/parent zone and a list of ORDERS for that particular CUSTOMER in the bottom/child zone. I have found it much easier to deal with those two zones by having a separate Model for each zone instead of having a single Model for multiple zones.
In my library of Transaction Patterns I have expanded this idea to allow some screens to have three or even four application zones.
Too many people think that (a) is the rule because they have not seen any examples which show otherwise.
I have seen too many code samples on the interweb which follow the practice of creating just one Controller for each Model to handle all the possible use cases which are available for that Model. This is a practice which was common in my COBOL days, but after constructing my first framework I began to notice its weaknesses, so I switched to the practice of creating a separate Controller for each use case. This is documented in Component Design - Large and Complex vs. Small and Simple. By having a separate Controller for each use case I avoid the problem of having large and complex Controllers which handle multiple use cases. I can also add new use cases to an existing Model without having to change any existing Controllers. This means that a Model can be accessed by any number of Controllers.
By use case I mean a single unit of work, sometimes called a user transaction or task, which can be selected from a menu button or navigation button. The most common set of forms that a database table may require is known as a Forms Family and while some people seem to think that the whole family is a single use case I have found benefits in treating each member of that family as a separate use case in its own right. Each task has a different structure, behaviour and content, and trying to deal with a single Controller which handles all possible combinations of those three characteristics can be quite challenging. That is why as long ago as the mid-1980s I decided to split a large program with multiple responsibilities into a series of smaller programs each with a single responsibility. Each of these programs has its own entry in the MENU database and its own component script, and as the behaviour of each program is centered around which combination of the CRUD operations it perform on its database table I have been able to code each Controller so that it performs its operations on a table whose identity is supplied at run-time through my implementation of Dependency Injection. This means that I can have a single Model accessed by as many Controllers as I see fit, and I can add new use cases for a Model, each with its own Controller, without having to amend any existing Controller.
Option (a) is chosen by those who do not know how to create reusable Controllers that can work with any Model.
My decades of prior experience with designing and building database applications in non-OO languages taught me several important points:
The very first Controller that I created had the table name hard-coded into it. When I copied that Controller to do exactly the same thing on another table I discovered that the only thing which I had to change was the table name, so I asked myself the question If the only difference between these two Controllers is the table name, is there a mechanism which allows me to pass that name in as a variable instead of a literal?
The answer is YES. Instead of being forced to write the following code:
require_once "classes/product.class.inc"; $dbobject = new product; $fieldarray = $dbobject->insertRecord($_POST);I discovered that I could write the following instead.
$table_id = 'product'; ..... require_once "classes/$table_id.class.inc"; $dbobject = new $table_id; $fieldarray = $dbobject->insertRecord($_POST);
The value for $table_id is now defined in a separate component script which resembles the following:
<?php $table_id = "product"; // identify the Model $screen = 'product.detail.screen.inc'; // identify the View require 'std.add1.inc'; // activate the Controller ?>
Note here that none of my Controllers contains any hard-coded table or column names. This means that each Controller can call its methods on any Model regardless of what columns it contains, and each Model can be accessed by any Controller. While I started with just a small set of reusable Controllers this list has expanded into a library of 40, one for each of my Transaction Patterns. If I have 40 Controllers and 450 table classes that means that I can deal with 40 x 450 = 18,000 (yes, EIGHTEEN THOUSAND) combinations.
Many years later I discovered that what I was doing was a form of Dependency Injection. I found it very strange that no-one else had used DI in this manner and was capable of having hundreds of alternatives for use as a dependent object.
I was taught to use option (a) when I was a junior programmer, but I quickly learned its limitations and its weaknesses. Having a Controller which can perform many tasks means that it may have method calls which are relevant to only one particular task, so ensuring that the right method calls are made in the correct sequence requires tricky code, and the need for tricky code can lead to mistakes. Been there, done that, got the t-shirt. Things get more complicated when different tasks within the same controller need different screens. Then add into the mix the requirement for each task to be the subject of role-based access control so that certain tasks can only be accessed by certain users and what started as a can of worms is now a bucket of worms.
Instead of having a large, complex program that can do lots of things it may be better to split it into a series of smaller and simpler programs which do one thing each. This idea is explored in greater detail in Component Design - Large and Complex vs. Small and Simple. It was years later that I discovered that my approach was following the Single Responsibility Principle in that I had created smaller components which were responsible for only a single task each instead of having a large component which was responsible for several tasks.
Options (a) and (b) are examples of tight coupling because changes to the number of columns in that table will cause the method signature to change, thus causing a ripple effect which necessitates corresponding changes to other modules. You should also notice that in each of these two samples both the table and column names are hard-coded, which means that this code is so tightly coupled it can only be used with one particular Model.
Option (c) avoids these issues completely so is therefore an example of loose coupling. No column names are specified, which means that the number of columns can be altered at will without having to change any method signatures. You should also notice that because the name of the database table is not hard-coded in the Controller it can be used with any table in the database as every method that the Controller calls is always available in every Model by virtue of the fact that every one of those methods is defined in the abstract table class which is inherited by every concrete table class.
Note that the contents of fieldarray will be validated by standard code in the abstract table class in order to ensure that the SQL query which will be generated will not fail. If the validation fails the insert will be skipped and all error messages will be passed back to the user. Any columns which are not part of the table's structure will be ignored, which means that they can be used on a call to insertRecord() on another table.
Option (a) produces tight coupling which should be avoided. Using individual getters to extract column values one by one is just the same as using setters to insert column values one by one.
By having all an object's data made available in a single $fieldarray variable it makes life so much easier. The Controller does not need to know the names of the columns whose data is being inserted into or extracted from the Model. Neither does the View which does nothing but iterate through the array and insert every element into an XML document before it is transformed into HTML output using an XSL stylesheet.
As the contents of $fieldarray is totally dynamic it may contain only a subset of the columns which are part of that table's structure, or it may even contain columns from other sources such as from JOINs to other tables in the sql SELECT statement.
Option (a) is chosen by novices as they are under the impression that a Model should never contain invalid data. This is bunkum. The only golden rule, which I learned in the 20 years of programming before I switched to an OO language, is that the data MUST be validated BEFORE the SQL query is constructed and executed otherwise invalid data will cause the query to fail, and this will cause the program to terminate immediately.
This means that it is perfectly acceptable to insert data into an object and have that object validate the data internally, and only when that data passes all its validation checks should it update the database. Any validation failures should result in the database update being skipped and any error messages being sent back to the user so that he/she can correct his/her mistakes.
Data validation is performed in two separate phases. The first is called primary validation which checks that the input data does not violate any of the rules contained in the $fieldspec array. The second phase is called secondary validation which has to be handled by custom code in any of the relevant hook methods.
The rules of both the 3-Tier Architecture and Model-View-Controller dictate that ALL business rules (which includes data validation) be performed within the Business/Domain/Model layer, which then prohibits having business rules in any of the other layers.
Option (a) is chosen by novices whose use of getters and setters automatically ties them to one set of fields at a time. Option (b) has been allowed in Robert C. Martin's Table Module pattern, so if he says it's OK then who are you to argue.
By using a single $fieldarray variable to hold all the data for an object I can allow this to be an associative array to contain the data for a single row, or allow it to be an indexed array where each index number contains an associative array for that row's data.
The definition found in wikipedia states that it inverts the flow of control in a manner described as the "Hollywood Principle" (don't call us, we'll call you). This is the difference between using a library and a framework - you have to write code to call a library routine, whereas a framework has the means to call the code which you write. The best design pattern to achieve this is the Template Method Pattern which is a fundamental feature of my framework.
Option (a) is wrong because the Dependency Inversion Principle has nothing to do with inverting the flow of control, it merely changes the place where a dependent object is instantiated.
Option (a) provides the opportunity to call these methods out of sequence which could then cause the generated SQL query to be rejected.
If those three methods have to be called in that sequence then instead of calling them one at a time you can create a single super-method which will call them for you. This will guarantee that the sub-methods will always be called in the correct sequence. If there is any condition checking between one step and another then this can be performed in the super-method. Any changes to these conditions can then be made in the super-method so that they are instantly available to all users of that method.
Those of you who are still awake and on the ball should instantly recognise that this type of super-method is an ideal candidate for conversion into a Template Method which defines the skeleton of an operation in terms of a number of high-level steps.
use when appropriateapply to your current circumstances or not, so you take the path of least resistance and substitute
use alwaysinstead. By following the rules without question even in those circumstances where they may not be appropriate you are nothing more than a cargo cult programmer. You will always be a code monkey and never a code meister.
If you have answered with mostly (b)s, or (c)s when available, then you are a pragmatist. You know which solutions produce the best results and which alternatives to avoid. Instead of following rules blindly you question them, and if you don't like the answer you ignore them as you know that they will be adding to your problems instead of reducing them. Dogmatists will dislike your free spirit, they will call you a maverick, a non-conformist or, even worse, a heretic for failing to follow the accepted path. However, you will have one vital characteristic that your dogmatist colleagues will lack - your ability to produce cost-effective solutions will make you an asset in the eyes of your employer whereas their dogged determination to follow the rules regardless of the results will make them a liability.
If your answers are split half-and-half then you have to decide whether you are a "glass half full" or "glass half empty" type of person. You have reached a crossroads in your career, so you need to decide whether you follow the path of righteousness with the rest of us pragmatists, or follow the path of wickedness, the path of darkness and despair, so favoured by the dogmatists.
While it may seem easy to do as you are told and follow the rules, the same styles and the same practices as everyone else around you, especially when you are a junior member of a team of so-called "experts", all you will end up doing is copying their mistakes. As you gain more experience you should be exposed to more ideas, different ideas, some of which may challenge or even contradict what you have been taught so far. You should try to experiment with some of these new ideas and learn for yourself whether they have merit or not.
Remember that progress is never made by doing things in the same old way, by copying what has been done before. You have to break the mould, throw out the old rules and start afresh. The mantra for a truly pragmatic programmer should be "innovate, don't imitate".
A reference to this article appeared in this reddit post. You will notice that none of the people who commented actually provided any answers, they just followed their normal practice of insulting me left, right and center. Is there anyone out there who is capable of intelligent debate?
If you regard this article as a series of interview questions then I'm afraid that all the respondents so far have failed miserably.
Instead of forcing you to read the whole thread I will summarise some of the comments and my responses below.
Sorry but your first question has two subjective "answers" and then presents one as correct and the other as wrong. Didn't get any further than that.That person clearly hasn't grasped the fact that every article ever written is oriented around the experiences of the author and is therefore subjective. It can only be classed as "objective" if it based on real facts and not influenced by personal beliefs or feelings. This article was meant to sort out the pragmatists from the dogmatists. Answer (a) is what I have seen consistently promoted as the "right" way, the "only" way, but my experience has taught me that there is always more than one way and each alternative has its own set of pros and cons. Where I have offered pragmatic answers which are alternatives to the traditional ones which are favoured by dogmatists I have tried to explain why my answers have more pros and fewer cons. If you dismiss my answers as pure heresy simply because they challenge the accepted dogma then you are heading down the road of becoming nothing more than a Cargo Cult Programmer who follows rules blindly without understanding them. In my book the dogmatic answer is always wrong and the pragmatic answer is always right.
Indeed, so why are you saying one answer is correct and the other is wrong?Answer (a) is "wrong" because it does not provide as much reusable code as my alternative, and as the whole objective of OOP is supposed to be the creation of as much reusable code as possible then answer (a) has clearly failed. It may satisfy a dogmatist, but it is clearly the wrong answer for a pragmatist.
Subjective answers cannot be "right" or "wrong".which is incorrect. Those answers are subject to my own personal experience, and in my personal experience answer (a) is "wrong" simply because it is NOT the answer that, as a pragmatist, I would choose.
The question is about what computer science is. It has absolutely no bearing on reusability of anything.which indicates that he did not read the question. I did not ask "What is computer science" I asked "Is computer programming a science or an art?" I personally consider it to be an art as without talent you will never become proficient, just as a person without any musical talent will never become a musician regardless of how many books he reads.
Are you implying that somebody born with the right talent can pop out their mum's vagina and start programming flawlessly from birth?followed by
So, people have to learn programming regardless of any pre-existing "talent" then, yes? Glad you agree. Now can you quantify what "talent" is?If I have to explain the meaning of the word "talent" to someone then it is quite obvious, to me at least, that they don't have any.
You're not challenging anything. You're asserting subjectivity as fact.followed by
You made an assertive statement about a subjective opinion.This person clearly does not understand what assertive actually means. A simple lookup on google came up with the following definition:
Being assertive is a core communication skill. Being assertive means that you express yourself effectively and stand up for your point of view, while also respecting the rights and beliefs of others.Here is a definition for subjectivity:
the quality of being based on or influenced by personal feelings, tastes, or opinions.Here is a definition for fact:
something that is known to have happened or to exist, especially something for which proof exists, or about which there is information.Yes, I am being assertive, but why shouldn't I be? Yes, my preferred answers are subjective, but that is because they are the product of decades of personal experience. Yes, I express these opinions as facts simply because I have employed them for over 15 years and they have proven to be more effective than their dogmatic counterparts. They are not wild theories which have not yet been put into practice, they have been practiced for over 15 years and have proven to produce results which are more cost-effective than anything else I have seen on the internet.
The question wasn't about OOP. And didn't present it as an opinion. You stated it as an objective fact. Which it is not.It may not have been about OOP in particular, but it is an important question nevertheless. If you believe that computer programming is a science which can be broken down into a set of rules which can be followed by absolutely anybody in a dogmatic fashion regardless of talent then in my personal experience you are barking up the wrong tree. Ever since I started programming in the 1970s every single piece of source code ever written contained the name if its programmer as the author. This means that each computer program is a work of authorship which is defined as:
The state or fact of being the writer of a book, article, or document, or the creator of a work of art.A work of authorship is a work of art which can be protected under the laws of copyright. That is a fact. When writing a book an author must use words that are available in the dictionary and string them together using the rules of grammar before he can produce something which his intended audience will be willing to read. However, the use of dictionary and grammar on their own will not guarantee a best seller, the author must have a talent for writing before that can happen. The job of a computer programmer is to take an instruction manual that a human can read and convert it into instructions that a computer can read. In this case the "dictionary" is the list of statements, instructions or constructs which the programming language provides, and the "grammar" is how you can group certain instructions together in order to achieve certain tasks, such as sending data to or receiving data from a web browser, or sending data to or receiving data from a database.
Just as the author of a book requires more than following a dictionary and the rules of grammar to write a best seller, the author of a computer program requires more than following a dictionary and the rules of grammar to write a successful program. In this case "successful" is judged to be a cost-effective solution in the eyes of the person who pays the programmer's wages. As well as providing a reliable and effective solution which the computer can execute it is much more important to provide source code which can be easily read, understood and maintained by others. There are many different thoughts on how source code can be structured in order to make it readable, but every programmer and his dog has a totally different opinion on what this structure should be. Dogmatists think that it is possible to provide a single set of rules which, if followed by the letter and without question, will always result in perfection. Pragmatists know otherwise. That is not just my opinion, it is shared by Jeff Atwood in his article Level 5 means never having to say you're sorry.
There are differences in opinion which cannot be stated as fact.I disagree. The internet is full of articles which state the author's opinion as fact. Take a look at the following examples:
your question states nothing on using a controller or loading data or validating itIt should be obvious that the object being called is the Model which is being called from a Controller. It should also be obvious that when the user presses the SUBMIT button an an HTML form the data from that form is presented to the Controller in the $_POST array. The object of the exercise is to get the data from the HTML form and insert it into the database. It should also be obvious that this procedure has to be done in stages:
The point I tried to make was that I have seen many code samples on the internet where those three steps are performed in three separate calls from the Controller on the Model. This leads to the complaint that it is possible to insert some calls between the validate() and store() methods which have the effect of sending unvalidated data to the database. If those three methods always have to be called in that sequence then it is far better to create a new method which calls those methods in that sequence. This is not just a new idea that I dreamed up, it is an example of the Template Method Pattern which was discussed in the following books:
I have no idea what load(), validate() and store() does.OMG! If you cannot understand such basic operations then why are you criticising someone who does? Let me spell it out for you in terms that even a clueless newbie should understand:
It depends on how important it is to have reusable code doesn't it?This means that he does not understand the main purpose of OOP in the first place. The only reason to replace one programming paradigm with another is to invent one which is better. But what is the definition of "better"? The only no-nonsense definition of OOP which I encountered at the start of my journey into this alien world after two decades of working with non-OO languages (COBOL and UNIFACE) went 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.I read this as saying that this new paradigm had three things - encapsulation, inheritance and polymorphism - which the older paradigms did not, and these could be used to help the programmer write more pieces of code which were reusable. In case you do not understand what "reusable" means let me explain it for you. If you write a piece of code once then reuse it multiple times, possibly hundreds or even thousands of times, it means that when you want to repeat that logic you don't have to spend time in re-writing, re-testing and debugging the same piece of code multiple times. By using code that you don't have to write you automatically make yourself more productive. My entire framework has been geared around this idea, and I have at my disposal much more reusable code than I have ever seen in any rival framework. It has now reached the point where, at the touch of a few buttons, I can create the tasks to maintain the contents of a new database table and run them immediately without having to write a single line of code - no PHP, no HTML, no SQL. All the basic functionality is there, and all the programmer has to do to handle the non-standard business rules is to put the relevant code into the relevant hook methods. Unless you can achieve the same levels of productivity then any criticisms of my methods will have the same effect as a lead balloon. The fact that I have managed to achieve such high levels of productivity by breaking so many of those rules which are favoured by the traditionalists and the dogmatists (those labelled (a) in my pop quiz) just convinces me that those rules are not worth the toilet paper on which they were written. In case you don't understand what that means, it is just a polite way of saying "your rules are crap". Put THAT into your IDE and compile it!
your alternative to DI was proven to be ineffectivebut when I asked him to provide such proof he could not. Besides, I never suggested an alternative to DI, I simply suggested NOT using DI in those circumstances where it was not appropriate - where there is no alternative implementation available for injection - as this would be a violation of YAGNI. I explained where I use DI in my framework to achieve actual benefits as demonstrated by the fact that I have 40 reusable Controllers into which I can inject any of my 450 table classes. This produces 40 x 450 = 18,000 (yes, EIGHTEEN THOUSAND) opportunities for polymorphism, and every competent programmer should be able to tell you that you cannot use DI without polymorphism.
He then tried to claim that other frameworks could achieve the same thing, but could not offer any proof. He certainly could not point to the existence of any reusable controllers in any framework, such as those which are documented in Transaction Patterns for Web Applications.
I am a better and more productive programmer because almost everyone on this reddit will agree with mebut when asked to prove it by taking this challenge he bottled out, as usual.
In another reddit post someone (who shall remain nameless) complained that my answer to Question #9 was unacceptable as it did nothing but promote my personal style. Excuse me! I did not invent the DRY Principle as my personal style, it was devised by others as one of those practices which every good programmer should follow. It simply means that where you see the same block of code appearing in multiple places it would be good practice to put that block of code in some sort of reusable subroutine so that you can replace each copy of that block of code with a subroutine call. Apart from reducing the amount of code which you have to write, which is a good idea in its own right, it also means that if you ever need to change the contents of this block of code you only need to change the one subroutine instead of every copy of that code.
Take the following block of code which I used in Question #29:
$object->load($_POST); $object->validate(); $object->store();
It may not look like much, but that is because it does include any code required for error checking.
In this scenario the above code is contained in the Controller and $object
identifies the Model. If you don't know about the MVC design pattern you should go away and study it NOW! You should then realise that this code will be duplicated in every script where a Controller calls a Model, and that could be a large number of scripts.
The problem with this block of code is that it is technically possible, after loading a validating some data, to "accidentally" load in some unvalidated data before storing it, which could potentially create a problem. The only way to ensure that this cannot happen is to wrap that block of code in a subroutine (or in this case a new method in the Model) so that it is physically impossible to insert unvalidated data before the $object->store()
. This is achieved in the following code which I put forward as my answer:
$object->insertRecord($_POST); OR $object->updateRecord($_POST);
You should notice that instead of one subroutine I have created two. Why is this? Because the logic required for an INSERT operation is different from that required for an UPDATE. Apart from the fact the validation rules are different, the SQL query which is generated is also different. You can see the differences when you compare the internal workings of these methods:
Note that these methods appear in every concrete table class (Model), but they are inherited from an abstract table class and therefore shared and not duplicated. The use of an abstract class then enables the use of the Template Method Pattern, which explains those methods with a "_cm_" prefix. The use of an abstract class and the Template Method Pattern are both described in the Gang of Four book as fundamental techniques for code reuse. In case you did not get the memo the ability to create higher volumes of reusable code is supposed to be the primary objective behind OOP.
If you are wondering why I pass the contents of the $_POST array as a single argument instead of loading each element individually then take a look at question #24 and its answer where it explains the benefits of loose coupling over tight coupling.
You should be able to see that none of the characteristics in my answer was a personal invention, I am simply utilising ideas that were formulated by other people:
The only thing that I "invented" was how to incorporate these ideas into my code. Surely that is supposed to be what every programmer should be capable of doing.
04 Feb 2023 | Added My Comment #14. |
31 May 2020 | Added Comments on reddit comments. |