Dependency Injection Breaks Encapsulation

Haha wow that’s some good post-rationalisation there. Again we’re back to this nonsense about “original design parameters” Stop trying to weasel your way out of actually making a valid point. Given any two methods you can, by some metric argue that one is better than another. You have yet to show a situation where a singleton is measurably better than DI (And remember you need to tell us the metric you’re using!)

This statement is also true, of course:

A singleton can be applied wherever one class sends a message to another. This implies that I still have a choice as to whether I use a singleton or not, and I choose not to in those circumstances which are outside the original design parameters.

Your argument is dead in the water here, tony and you know it.

And for the hundredth time: Stop changing this subject and answer this question:

Why is a singleton more appropriate than DI when there is only one possible implementation of the dependency? And how are you measuring that “appropriateness”?

I disagree. Robert C. Martin’s article clearly demonstrates a situation where DI has distinct benefits. Just because the same technique can be used in other circumstances for other purposes is no justification for saying that DI should be used for every single dependency regardless of the circumstances.

Don’t you see how nonsensical this argument is? The inverse is also true: It’s also no justification for not using DI in other circumstances. The very paper you’re quoting simply shows one benefit.

Once again I point you to:

and

This is the argument you are making here. It’s a fallacy so stop making it, it’s not a valid argument.

Information hiding is not important. Changing class properties from “public” to “private” maes absolutely no difference to the code which is executed. All it does is impose restrictions on developers.

…and if you cannot see why imposing restrictions on developers is important then there really is no point continuing. I’m not going to explain it to you again but there’s plenty of information (not hidden!) easily accessible via google.

I keep asking you this and you keep ignoring it: What is the point you are trying to make? Or are you just diverting attention away from the question you cannot answer about DI?

DI is for injecting dependencies, and may be implemented in several ways, either with or without a dependency injection container. When I want to inject a dependency then I use DI. My implementation may be different from yours, but that is irrelevant. Where I have a dependency but choose not to inject it, then I don’t use DI, and the method I use instead of DI should be irrelevant. I should not have to prove that my method of not implementing DI is better than implementing DI. It should be sufficient for me to say that when I do not have the circumstances for which that pattern was initially designed then I do not have a good reason to implement that pattern.

If you look at all the proper descriptions od design patterns you should notice that they each identify the particular problem for which they are supposed to be the solution. It should be obvious that implementing a solution when you don’t actually have that problem is not a good idea. Have you heard of those programmers who try to write a program containing all 40+ patterns from the GoF book? Do you think it is a good idea to implement a pattern that you don’t actually need? I personally do not, and neither does Eric Gamma who wrote the following:

He also wrote:

You see the words “when you don’t need it”? That is why I feel justified in saying that when I don’t need a pattern I see no reason to implement it. You may disagree with me, but who are you to say that Eric Gamma is wrong?

The example there is fitting:

If someone wants to believe in leprechauns, they can avoid ever being proven wrong by using ad hoc hypotheses (l.g. by adding “they are invisible”, then “their motives are complex”, and so on)

This is exactly what you are doing here. with “that’s not what it’s intended for”.

Wrong again. If you’re choosing method A over method B, you must have reasoning for choosing one over another. It’s this reasoning we are questioning.

To use your own example: I can choose to crack open a nut with a sledgehammer and it may be justifiable if a sledgehammer is the only tool I have available. The problem is, all your arguments lack this justification.

You may say your reasoning is:

  • A coin toss
  • I didn’t know about DI
  • I didn’t understand why DI is better

Which are all valid reasons for using an alternative in the past, what it doesn’t do is justify their use in the future now you know better. If you want to keep using singletons, you need to justify why they should be used instead of DI. You have not been able to do this.

@TomB I’ve been quiet so far but still reading all these threads. Do you feel that it’s worth keeping this conversation? If you read one of the Tony’s article back in 2003 - 12 years ago (http://www.tonymarston.net/php-mysql/good-bad-oop.html) - we was confronted with the same questions and answers exactly the same way, probably the other guys just given up by exhaustion.

  • The language allows me to do it, therefore it cannot be wrong.
  • It works, therefore it cannot be wrong.

I cannot conceive of any way to achieve my objective without having those variables pass through the business layer, therefore I cannot accept any arguments that say they should not be there.


An abstract superclass containing generic code which can be applied to any database table/entity.
A concrete subclass for each individual database table/entity containing the specific information required to process that entity, which includes all the data validation and business rules.
A separate data access object (DAO) which can generate the SQL query for any table in any database. I have a separate class for each DBMS so I can easily switch from one DBMS to another.


and unless you can find any fault with my implementation of these two principles I think you will find it extremely difficult to come up with ANY design that could possibly offer less maintenance.


This is supposed to be the right way using ‘setters’:

<?php 

$client = new Client(); 
$client->setUserID    ( $_POST['userID'   ); 
$client->setEmail     ( $_POST['email'    ); 
$client->setFirstname ( $_POST['firstname'); 
$client->setLastname  ( $_POST['lastname' ); 
$client->setAddress1  ( $_POST['address1' ); 
$client->setAddress2  ( $_POST['address2' ); 
$client->setCity      ( $_POST['city'     ); 
$client->setProvince  ( $_POST['province' ); 
$client->setCountry   ( $_POST['country'  ); 

if ($client->submit($db) !== true) 
{ 
    // do error handling 
} 
?> 

This is my way:

 <?php 
  $dbobject = new Client;
  $dbobject->insertRecord($_POST);
  $errors = $dbobject->getErrors();
 ?> 

Why should I waste effort in unpacking the $_POST array and feeding the data in one field at a time when I can provide it all in one single step? It is just as easy for code inside the class to address each field as $array[‘field’] as it is by $this->field.


One of the benefits of reusable code is that you an write it once and share it many times. One of the drawbacks of reusable code is that you can screw up that one copy and you effectively screw up every place where it is shared. You simply have to weigh up the benefits and risks of a particular implementation, make a choice, then live with the consequences. My design has the same ratio of risk/benefit as any other design, so your statement has no value (IMHO).


Someone said: You should use arguments, not variables

I disagree entirely. The $where string may contain any number of variables, but I do not see any reason why I should unpack it into its component parts, pass in each part individually, then reassemble the parts into a string which can be used as the WHERE clause in an SQL query. I pass it in as a single string and use it to send out as a single string, so all that unpacking and re packing is totally unnecessary. A competent programmer would not suggest writing code to perform something which is not necessary.

You could say that a word is a collection of letters, so using your logic would you suggest that each individual word be handled as a collection of separate letters instead of a simple string? No? I thought not.


Someone said: Your design is less reusable, because the validation code cannot be used in other places where it makes sense due to the lack of orthogonality.

Also used loosely to mean ‘irrelevant to’, e.g. ‘This may be orthogonal to the discussion, but …’, similar to ‘going off at a tangent’.


If I have 12 database tables it is obvious that I am dealing with 12 different entities with different properties, and it is a fundamental principle of OO that each different entity is required to have its own class. Each different entity also requires its own database table, therefore there is a one-to-one relationship between entity, class and table. Having a separate class for each database table most certainly does NOT lose the potential benefit of low maintenance


So may nice examples in a single page that are still (12 years latter) used in this thread by Tony.

2 Likes

It’s a fair point. Replace “Science” with “Best practices” in this article: http://grist.org/climate-skeptics/the-anatomy-of-denial-why-truth-doesnt-always-win/ and you have Tony

1 Like

Yet, another example of, sorry, complete arrogance:

I do not use separate DBMS developers because they will want to follow their own set of stupid rules for designing databases. When a database is being used by MY application then I insist on designing that database according to MY rules.

also

Glen wrote: ps: This is not written out of arrogance and I don’t think I know it better than anyone else. I’m just a student, and also was pondering about a nice oo-approach to php/mysql scripting.

Here’s my advice (for what it’s worth):
Don’t give up your day job.
If you are paying for lessons then ask for your money back.
If you are teaching yourself then you need a new teacher.
If you are reading books then they are the wrong books, or they have some pages missing.


But there is one answer question that really explains the last 400 messages of this thread and explains the “why” of the 9000 LoC class. On this own thread Tony posts the question made by André Næss :

You should try object composition instead of inheritance
There is a very common error which people do the first time they do OO, and that is to overuse inheritance. It’s not that strange really as there’s always a lot of fighting over inheritance. But object composition is a second technique that frequently applies. Where you see only inheritance, I see inheritance and composition.

Which Tony answered, and based on this thread, still defends:

You have totally misunderstood my design as there is no way that I overuse inheritance. All my concrete table classes, and I have hundreds, inherit from a single abstract class. My inheritance hierarchy is only one level deep, so how can this be described as “overuse”?
(…)
In the first place I do not have a large inheritance hierarchy as all my concrete table classes inherit from a single abstract table class, therefore I am not overusing inheritance by any stretch of the imagination. If I don’t have a problem then why do I need a solution?

In the second place I am not going to change a method which is simple and effective for one that introduces another level of complexity for absolutely no discernible benefit. This is another one of those loony OO ideas that I shall consign to the khazi.

My conclusion from your statements is that, if a entity A share some responsibilities with entity B then you put the code in the parent generic entity X, but because this is “related” to the entity itself you place it there and nowhere else. So, which means that the generic entity is responsible for anything that happens one more that one entity. In your example, the GenericTable is responsible for binding the post data, validating, fetching, paging, foreing key validation, transaction, formating, uploading files, translating, holding data, some sql composition, … thus making 232 methods available there:

function Default_Table ()
function array2string ($array)
function cascadeDelete ($where, $parent_table=null)
function cascadeNullify ($update_array, $where)
function changeConfig ($fieldarray)
function checkWorkflow ($where)
function clearEditableData ($fieldarray)
function clearScrollArray ()
function commit ()
function convertTimeZone ($fieldarray, $fieldspec)
function currentOrHistoric ($string, $start_date='start_date', $end_date='end_date')
function customButton ($fieldarray, $button, $postarray, $row=null)
function deleteMultiple ($fieldarray)
function deleteRecord ($fieldarray, $parent_table=null)
function deleteRelations ($fieldarray)
function deleteScrollItem ($index)
function deleteSelection ($selection)
function eraseRecord ($fieldarray)
function eraseRelations ($fieldarray)
function executeQuery ($query)
function fetchRow ($resource)
function fetchRowChild ($row)
function filePickerSelect ($selection)
function fileUpload ($input_name, $temp_file)
function formatData ($fieldarray, &$css_array)
function free_result ($resource)
function &getChildData ()
function getClassName ()
function getColumnNames ($where=null)
function getCount ($where=null)
function getData ($where=null)
function getData_raw ($where=null)
function getData_serial ($where=null, $rdc_limit=null, $rdc_offset=null, $unbuffered=false)
function getDBname ()
function getEnum ($fieldname)
function getErrors ()
function getExpanded ()
function getExtraData ($input, $where=null)
function getFieldArray ()
function getFieldSpec ()
function getFieldSpec_original ()
function getForeignData ($fieldarray)
function getInitialData ($where)
function getInitialDataMultiple ($where)
function getInstruction ()
function getLanguageArray ($id)
function getLanguageEntries ($rows, $parent_data, $fieldlist)
function getLanguageText ($id, $arg1=null, $arg2=null, $arg3=null, $arg4=null, $arg5=null)
function getLastIndex ()
function getLastPage ()
function getLookupData ()
function getMessages ()
function getNodeData ($expanded, $where=null)
function getNumRows ()
function getOrderBy ()
function getOrderBySeq (&$orderby=null, $orderby_seq=null)
function getPageNo ()
function &getParentData ()
function getPkeyArray ($fieldarray=null, $next_task=null)
function getPkeyNames ()
function getPkeyNamesAdjusted ()
function getScrollIndex ()
function getScrollItem (&$index)
function getScrollSize ()
function getSearch ()
function getTableName ()
function getValRep ($item, $where=null, $orderby=null)
function getWhere ($next_task)
function initialise ($where=null, &$selection=null, $search=null)
function initialiseFileDownload ($where)
function initialiseFilePicker ($where, $search=null)
function initialiseFileUpload ($where)
function insertMultiple ($fieldarray)
function insertOrUpdate ($fieldarray)
function insertRecord ($fieldarray)
function multiQuery ($query)
function popupCall (&$popupname, $where, &$script_vars, $fieldarray, &$settings)
function popupReturn ($fieldarray, $return_from, $selection, $popup_offset=null)
function post_fileUpload ($filename, $filesize)
function post_search ($search, $selection)
function reInitialise ($fieldarray, $where)
function reset ($where=null, $keep_orderby=false)
function restart ($return_from, $return_action, $return_string=null)
function rollback ()
function scriptNext ($task_id, $where=null, $selection=null, $task_array=array())
function scriptPrevious ($errors=null, $messages=NULL, $action=NULL, $instruction=NULL)
function selectDB ($dbname)
function setAction ($action)
function setChildData ($data)
function setChildObject (&$childOBJ)
function setCurrentOrHistoric ()
function setDefaultOrderBy ($sql_orderby='')
function setFieldAccess ()
function setFieldArray ($fieldarray, $reset_pageno=true)
function setInstruction ($instruction)
function setLookupData ($input=null)
function setOrderBy ($sql_orderby, $sql_orderby_seq=null, $toggle=true)
function setOrderBySeq ($sql_orderby_seq)
function setPageNo ($pageno=null)
function setParentData ($data)
function setParentObject (&$parentOBJ)
function setRowsPerPage ($rows_per_page)
function setScrollArray ($where)
function setScrollIndex ($index='1')
function setSelectedRows ($select_string, $rows)
function setSqlSearch ($sql_search=null, $save=false)
function setSqlGroupBy ($sql_groupby=null)
function setSqlWhere ($sql_where)
function sqlSelectDefault ()
function sqlSelectInit ()
function startTransaction ()
function unFormatData ($fieldarray)
function unFormatNumber ($fieldarray)
function updateFieldArray ($fieldarray, $postarray)
function updateLinkData ($fieldarray, $postarray)
function updateMultiple ($fieldarray, $postarray=array())
function updateRecord ($fieldarray)
function updateSelection ($selection, $replace)
function validateDelete ($fieldarray, $parent_table=null)
function validateSearch ($fieldarray)
function validateUpdate ($fieldarray)
function _cm_changeConfig ($where, $fieldarray)
function _cm_commonValidation ($fieldarray, $originaldata)
function _cm_customButton ($fieldarray, $button)
function _cm_deleteSelection ($selection)
function _cm_filePickerSelect ($selection)
function _cm_fileUpload ($input_name, $temp_file, $wherearray)
function _cm_filterWhere ($array=null)
function _cm_formatData ($fieldarray, &$css_array)
function _cm_getColumnNames ($fieldarray)
function _cm_getDatabaseLock ()
function _cm_getExtraData ($where, $fieldarray)
function _cm_getForeignData ($fieldarray)
function _cm_getInitialData ($fieldarray)
function _cm_getInitialDataMultiple ($fieldarray)
function _cm_getNodeData ($expanded, $where, $where_array = null)
function _cm_getNodeKeys ($keys)
function _cm_getOrderBy ($orderby)
function _cm_getPkeyNames ($pkey_array, $task_id, $pattern_id)
function _cm_getValRep ($item=null, $where=null, $orderby=null)
function _cm_getWhere ($where, $task_id, $pattern_id)
function _cm_initialise ($where, &$selection, $search)
function _cm_initialiseFileDownload ($fieldarray)
function _cm_initialiseFilePicker ($fieldarray, $search)
function _cm_initialiseFileUpload ($fieldarray)
function _cm_ListView_header ($fieldarray)
function _cm_ListView_print_before ($prev_row, $curr_row)
function _cm_ListView_print_after ($curr_row, $next_row)
function _cm_ListView_total ()
function _cm_output_multi ($name, $fieldarray)
function _cm_popupCall (&$popupname, $where, $fieldarray, &$settings)
function _cm_popupReturn ($fieldarray, $return_from, &$select_array)
function _cm_post_deleteMultiple ($rows)
function _cm_post_deleteRecord ($fieldarray)
function _cm_post_eraseRecord ($fieldarray)
function _cm_post_fetchRow ($fieldarray)
function _cm_post_fileUpload ($filename, $filesize)
function _cm_post_getData ($rows, &$where)
function _cm_post_insertMultiple ($rows)
function _cm_post_insertOrUpdate ($fieldarray, $insert_count, $update_count)
function _cm_post_insertRecord ($fieldarray)
function _cm_post_lastRow ()
function _cm_post_output ($string, $filename)
function _cm_post_popupReturn ($fieldarray, $return_from, $select_array)
function _cm_post_search ($search, $selection)
function _cm_post_updateLinkdata ($rows, $postarray)
function _cm_post_updateMultiple ($rows)
function _cm_post_updateRecord ($fieldarray, $old_data)
function _cm_post_updateSelection ($selection, $replace)
function _cm_pre_cascadeDelete ($fieldarray)
function _cm_pre_deleteMultiple ($rows)
function _cm_pre_deleteRecord ($fieldarray)
function _cm_pre_eraseRecord ($fieldarray)
function _cm_pre_getData ($where, $where_array, $fieldarray=null)
function _cm_pre_insertMultiple ($rows)
function _cm_pre_insertOrUpdate ($rows)
function _cm_pre_insertRecord ($fieldarray)
function _cm_pre_output ($filename)
function _cm_pre_updateLinkdata ($rows, &$postarray)
function _cm_pre_updateMultiple ($rows)
function _cm_pre_updateRecord ($fieldarray)
function _cm_pre_updateSelection ($selection, $replace)
function _cm_reset ($where)
function _cm_restart ($pattern_id, $zone, $return_from, $return_action, $return_string)
function _cm_setJavaScript ($javascript)
function _cm_setScrollArray ($where, $where_array)
function _cm_unFormatData ($fieldarray)
function _cm_updateFieldarray ($fieldarray, $postarray, $rownum)
function _cm_updateSelection ($selection, $replace)
function _cm_validateDelete ($rowdata, $parent_table)
function _cm_validateInsert ($rowdata)
function _cm_validateSearch ($fieldarray)
function _cm_validateUpdate ($fieldarray, $originaldata)
function _ddl_getColumnSpecs ()
function _ddl_showColumns($dbname, $tablename)
function _ddl_showCreateTable ($dbname, $tablename)
function _ddl_showDatabases ($dbprefix)
function _ddl_showTables ($dbname)
function _ddl_showTableKeys ($dbname, $tablename)
function _dml_deleteRecord ($fieldarray)
function _dml_adjustWhere ($string_in)
function _dml_deleteSelection ($selection, $from=null, $using=null, $limit=0)
function _dml_free_result ($resource)
function _dml_getCount ($where)
function _dml_getData ($where, $raw=false)
function _dml_getData_serial ($where=null, $rdc_limit=null, $rdc_offset=null, $unbuffered_query=false)
function _dml_getEnum ($item)
function _dml_insertRecord ($fieldarray)
function _dml_multiQuery ($query)
function _dml_ReadBeforeUpdate ($where, $reuse_previous_select=false)
function _dml_updateRecord ($fieldarray, $oldarray, $where=null)
function _dml_updateSelection ($selection, $replace, $limit=0)
function _examineWorkflow ($input)
function _examineWorkflowInstance ($where)
function _extract_custom_fields ($rowdata)
function _getCustomProcessingObject ()
function &_getDBMSengine ($dbname=null, $unbuffered_query=false)
function _getInitialValues ($task_id=null)
function _getInitialWhere ($where)
function _processInstruction ($fieldarray)
function _qualify_dbname ($target_db, $this_db)
function _sqlAssembleWhere ($where, $where_array)
function _sqlAssembleWhereLink ($where, $where_array)
function _sqlForeignJoin (&$select, $from, $parent_relations)
function _sqlProcessJoin (&$select, $from, $reldata, &$new_relations)
function _sqlSelectAlternateLanguage ($sql_select, $reldata, $subquery=false)
function _switch_database ($tablename, $dbname)
function _validateInsert ($fieldarray)
function _validateUpdate ($fieldarray)
function __call ($method, $arguments)
function __sleep ()
function __wakeup ()

Just even looking at the “public” methods (without the underscore) there are 121 methods.
A few examples of the current problems, in my vision of a framework:

  • commit, rollback - What if I want to commit/rollback a transaction involving more that one entity ? In which entity should I use this? A random one, all? And if all, what if one fails?
  • post_fileUpload - Why should I care of this in the “database” layer? And if I don’t support file upload on a “Car” entity?
  • getLanguageArray - I don’t care about multilanguage, and isn’t this a front-end responsibility?
  • customButton - Again isn’t this front-end responsibility? Does it make sense use it in batch program? If not, what it is doing here?

Also, another quote from that article:

As far as I am concerned there is a one-to-one relationship between ‘entity’, ‘class’ and ‘database table’, so if ‘entity=class’ is correct and ‘entity=table’ is correct, how can ‘class=table’ be incorrect?

This is all plain wrong, any system/component can abstract another. So I can have an entity on my code (Person) that abstracts more that one table in the ER (Login + PersonDetails + AddressInfo + what ever I want) for many reasons: performance, obscure complexity, … I can have a many-to-many-to-many-to-many relationships. This is so true that if I had a one-to-one-to-one than I wouldn’t need an interface (different visions of my entities) just have a database system and let my users write it directly.

The problem is that it appears that he doesn’t understand OOP at all and isn’t really using OOP. 9000 lines of code in one class is not OOP. As he doesn’t appear to even know what OOP is, all of the discussions with him about how it should be used are meaningless since he isn’t using it.

To be able to meaningfully discuss anything that depends on OOP requires that each of the people in the discussion actually understand the concept and purpose of OOP.

The reason for the disagreements in this thread is because two entirely different things are being discussed.

1 Like

The thing I hate about being smart and envy about strength is this. When you are strong and a weak person tries to claim they’re stronger, you can pound them into submission and the resulting pain will make even those with barely above invertebrate intelligence understand that they are weaker than you, and should shut up.

But when you’re smart, no amount of linguistic thrashing, no amount of evidence, no amount of logic will stop a prattling utter and complete fool from continuing to run his mouth off.

Post was edited by cpradio to remove personal attack

To quote Mark Twain

“Never argue with a fool, onlookers may not be able to tell the difference.”

Okay, all. Here is an official warning. This topic is deviating again to personal bashing. I realize the likelihood of any of you seeing eye to eye with each other is 0%, but nonetheless, the personal attacks/insults, whatever are unnecessary.

If you want to quote an article and discredit the verbiage by referencing other well known writings, that’s perfectly within the guidelines, but to personally refer to someone as stupid, idiotic, or any other negative remarks will be edited out and that person will receive an official warning. Failure to abide by said warning could lead to a cool off period.

8 Likes

Agree. No need to personal attacks, either you give up (which I didn’t yet :disappointed: ) or you contribute.

Coming back to the whole dependecy injection theme, I’ve come across another example in @tony_marston404 own super class that it might give him a reason that DI is usefull.

At the original doc that make all this fuzz (http://www.tonymarston.net/php-mysql/dependency-injection-is-evil.html) @tony_marston404 pointed out:

Dependency Injection is only a good idea when an object has a dependency which can be switched at runtime)

On the RADICORE’s webstite , the very first statement says:

RADICORE is an open source (…) platform independent, browser independent and database independent.

complemented by the FAQ:

This means that RADICORE, although developed using MySQL, will run on any SQL-compliant database (provided that a DAO has been written for that database

On the 9000LoC there’s an if that I’ld like to point out located on the &_getDBMSengine function

    if (!class_exists($engine)) {
        // load class definition for this database engine
        if ($engine == 'mysql') {
            if (extension_loaded('mysqli')) {
                // use 'improved' mysql functions
                require_once "dml.mysqli.class.inc";
            } else {
                // use standard mysql functions
                require_once "dml.mysql.class.inc";
            } // if
        } elseif ($engine == 'oracle') {
            if (version_compare(phpversion(), '5.0.0', '<')) {
                // use old api's
                require_once "dml.oracle.php4.class.inc";
            } else {
                // use new api's
                require_once "dml.oracle.php5.class.inc";
            } // if
        } else {
            require_once "dml.$engine.class.inc";
        } // if
    } // if

Only the if explains the benefits of the dependency injection. Your default class Default_Table is not agnostic to the engine and need to be change if a new engine is created. Actuallly, from what I can understand from it, all the _getDBMSengine 170LoC function could be replaced by a dependency, either an instance of the DAO or DAO’s factory if it could be dynamic, and just use it.

This is the type of scenarion that a new Person() that could be replaced with new Persion(new Address()).

1 Like

I’ve also learned, a class with a lot of if/ elseif/if /elseif statements is another good sign of SRP not being followed and that is possibly a clear case for polymorphism. I think another good rule of clean coding is to have only one level of indentation. It is hard to follow, but if you can, you have really clean code.

This is great video of about clean coding, which mentions these rules.

Scott

Yes, you can, unless they are tricks.

Posted edited by cpradio to remove unnecessary language

Incorrect. My framework also deals with PostgreSQL and SQL Server, but do you see any code there which is specific to those two? No, you do not. The only reason that the above code is there is because there are different class files available depending on the version of PHP which is running.

While it could, I choose not to. Instead I choose not to instantiate my database object until the point at which I actually need it. With this technique I can access different databases on different servers within the same user transaction, and even have these different servers provided from different DBMS engines.

Which belong in your MySQL and Oracle abstractions and not in your default table class.

Which, from a database abstraction logic standpoint, also has absolutely nothing to do with a default table class.

I have to ask the question that was asked earlier again, what does the default table class represent? What single behavior does it do well? Please state that in one simple sentence.

You know Tony, I asked Uncle Bob Martin personally via Twitter what he thought of your class. You know what his answer was?

.

You know what the smiley

>8^O

means?

It is the smiley equivalent to this.

So are you now going to say that Bob Martin doesn’t know what he is talking about too?

Scott

2 Likes

Isn’t it more…

JanetLeighPsycho

??

Maybe that’s just my interpretation… :wink:

1 Like