OOP pushers love to shove their buzzwords into your face if you point out problems with OOP. They say things like, "sure data sharing is a little harder in OOP, but OOP has iheritance." Thus, real problems don't matter if the abstract theory is taken care of.
Well, us TOP (Table-Oriented Programming) people can play the same game. In addition to describing the faults in the OOP buzzwords, we have come up with TOP buzzwords of our own. You want a buzzword fight? Ready, Aim, F....
First, there is no evidence that OOP drastically increases re-use. Second, what use is re-using half of the code when it takes twice as long to make the code in the first place! It would be like building an engine that is twice as feul-efficient, but finding out that the engine makes the vehicle weigh twice as much! (net gain <= 0) Note that not all OOP languages and techniques produce bloat, but that is the general trend.
Third, there is a faulty assumption that only OOP can provide components. Components are pre-built "plug-ins" or "add-ons" that can be added to software. I have used charting and DB components successfully with FORTRAN (a non-OOP language) before OOP was the rage. It worked very similarly to OOP objects. You had things like "handles" instead of an object (instance) that allowed the equivalent of multiple instances. For example, if the OOP approach looks something like this:
include thing_class objectthing yaddle1 = new objectthing() objectthing yaddle2 = new objectthing() yaddle1.someaction(8)then the non-OOP approach would look something like this:
include thing_package integer yaddle1, yaddle2 // declare handles yaddle1 = newthing() yaddle2 = newthing() someaction(yaddle1, 8)Not really that much different. What's the big deal? (Plus, there are other possible approaches.) I think OOP pushers need history lessons before promoting OOP's brand of component re-use. (It is true that the OOP way may catch more problems at compile time, but this is true of any concept that is embedded into a language. You can't realistically embed every possible concept into one language, as described under the OOP Q&A section.)
More on components, and more on Internal Vs. External Reuse, and a WikiWiki reuse discussion.
One goal of encapsulation is to prevent programmers from using the wrong procedure or data type on the wrong item. It also has the goal of creating "black boxes" where one does not have to know how it works, only how to use it. There are various ways to do this. OOP mentality often takes this concept to an extreme.
Although it is a worthy goal, it often ends up bloating the syntax beyond reasonable use. It also makes persistence storage more difficult and more program-dependant because OOP tends to want to meld data and procedures tightly together.
Some OO fans claim that encapsulation can prevent unauthorized updates to data. However, this assumes that it is possible to limit data access of a given data item to one class. For example, having class A prevent all other classes from issuing a change-request (such as an UPDATE query) to a given data item. I see nothing inherent in the OO paradigm which guarantees this. It would have to be system- or language-specific. (See Data Protection).
ITOP provides a form of encapsulation by using Control Tables to keep related logic in one easy-to-manage place. Read also about Aspects, Interface Change Management, Abstraction, and Interface Bloat for more perspectives on encapsulation.
Varying parameter passing was already discussed under the Wrong Credit section. An example of same-named functions would be having a "Draw" function for different shape types. ITOP can satisfy this using column functions on Control Tables. Our shape example shows how regular relational tables can assist in polymorphism.
See also The 3 Polymorphism Killers.
However, reality is not allways so simple. I have heard OOP programmers complain about finding OOP code from other programmers where inheritance was overused, creating "spaghetti" inheritance. This is the 90's version of "goto" spaghetti coding of the 60's.
In fact, the most popular selling OOP development environment, Visual Basic, has chose not to support direct inheritance. Dan Rahmel, et al., has this to say about inheritance:
Inheritance is a hotly contested topic in the object community because...a single change to a class can have far-reaching effects on all the children defined from that class. (From 'Client/Server Applications with Visual Basic 4, Sams Publishing, 1996.)
Inheritance is often sold as some magic scheme that can propagate "friendly" changes to all related parts of an OOP software system. Even carefully designed software is very fragile. If a human can't fully predict ALL the side-effects of changes, how is OOP software going to automatically predict and prevent unforeseen side-effects? (A human can perhaps predict 99% of the side-effects, but that missed 1% can crash the entire application.)
For example, suppose a generic OOP class for addresses was built. Whenever an address is needed for another class, the address class can simply be inherited or instantiated instead of recreating definitions over and over again for name, address, state, zip-code, etc.
So after years of inheriting or instantiating this address class to many applications and data sets, it is decided to add an "e-mail" field (property) to base address class. The developer is thinking, "WOW!, a change to one class propagates among several modules. What a time saver!"
However, some modules export the address to a customer's system. That system sees the new e-mail field and crashes because it was not designed to handle an e-mail field in it's addresses. (They are not using the same language and/or object definitions.)
Second, a report that prints out sales contact addresses for the CEO now looks funny because the new e-mail field bumped other fields off of an already crowded report line. (Managers like to cram as much into a report as possible to avoid flipping pages. They don't want to hear excuses about "protecting future field growth".)
Thus, a simple change broke at least two applications. With OOP you can inherit power, but you can also inherit unforeseen side-effects. Inheritance can create a house of cards. If you accidentally bump a root card in the wrong way, KABOOM!
People in Usenet who saw this example said things like "Oh, if you only did this and that to each instance or reference you would have avoided these problems." Even if one did not have the gift of hind-sight (which we as readers do), special variations for each usage is probably more work than simply copying address field formats to each new table/class.
Auto-propagation of changes can cause just as many problems as the lack of change propagation, if not more. Sometimes duplication of slightly varied constructs is better than a referenced central construct with a bunch of exceptions engineered into it.
(Note that a nifty compromise may be to automatically track address definition instances, but not automatically change them. This way when there is a change, address definitions can more easily be hunted down to see if they need manual tweaking. This avoids automatic changes, but does allow better management of changes. It also allows step-by-step changes instead of the sudden OOP propagation.)
Much of the famous, but annoying problems of Windows DLL conflicts result from a form of inheritance. A vendor, or even Microsoft will sometimes automatically install a newer version of a DLL. The newer version may have fixed some known bugs and added some new features or speed.
However, an existing application that uses the DLL that was just updated may not "like" the changes, and go KABOOM! When experts track down the "conflict" problems, they are often found to be the result of very subtle differences between the DLL versions. (It is impossible for a DLL builder to test a new DLL with all versions of all applications that used the older one.) Any programmer knows that subtle differences can be enough to crash programs.
If each application could only use the DLL's that it was shipped with (and was tested with), then other applications are less likely to interfere with it. "Good fences make for good neighbors." Although you cannot inherit improvements, you are also less likely to inherit unforeseen side-effects.
Further, the base classes can become obsolete as technology and/or your business changes or grows, but nobody will risk overhauling these base classes because so many other parts of the system rely on them. This is a common problem in the IT industry, even without (before) OOP. Inheritance will only magnify the problem.
Note that experience tells us that it is best to subclass only operations or objects that have simple, unambiguous, and non-changing interfaces (even if the implimentation is complex). For example, sorting is fairly easy to describe and define, and there is little risk in repairing or replacing a sorting class (assuming that things like collating sequences have been settled on.)
A variation of this concept is called the "fragile parent problem". See the above discussion on "Inheritance".
Note that some OO fans suggest performing periodic code reorganizations known as "refactoring". Please see the Boundaries write-up for more on refactoring (not to be confused with regular "factoring".)
It baffles us why autoperistence is marginalized by the industry.
Some have claimed that autopersistence slows down access to data items (objects). However, disk caching technology actually puts some or all of the structure into memory during processing, thus making its performance almost identical to memory-only structures. (Plus, it gives added "virtual addressing space" if RAM runs out.)
SQL generally lacks this feature because one cannot say something like:
select * from tablex where myfunc(location) > 7In this example, the SQL evaluator usually has no idea what "myfunc" is unless it was specified to the database server in some way. Contrast this to similar, yet working, XBase statements:
Use tablex List all for myfunc(location) > 7Although stored procedures and other tricks can sometimes work around this "blindness" limitation, it is still an important barrier to concepts like Control Tables and other powerful programming constructs. After all, its the programming languages' job to be the programming language, not the database server's job (unless a way can be found to cleanly interchange them.) Also DBMS scripting languages are rarely a treat even if you ignore the isolation factor.
Most OOP approaches require one to move a collection to a special container class that has one or more of the sort, search, summarize, view-filter, and related operations. By automatically inheriting these features, ITOP can be a great time (and code) saver.
View-filtering refers to the ability to narrow one's item view to a particular subset of the collection and/or collection properties. Filtering does not change the actual items, but items can be changed through a view. Aggregate item change operations are also a part of ITOP, similar to an SQL "Update" command, but with Internal-Aware Expressions. (See, we are already using our new vocabulary.)
See also the Collection Bill-of-Rights and the Graduation Problem below.
There is no reason why a common collection interface (or better yet, built-in syntax) cannot be provided for nearly ALL collections. This would:
If you agree that having different interfaces is silly, the next question is what the common interface should be. Answering that question is what ITOP is all about. (Criticisms of a common interface are addressed here.)
1 A B C 2 A B C 3 A B C 4 etc...Note that some number levels may have a few more or less letters than others. See Control Table Theory for more information about fixing comb code and recognizing variations of it.
In mathematical terms, repetition factoring out would turn:
xa + xb + xc + xd + xe + xf Into: x(a + b + c + d + e + f)See Goals & Metrics for repetition factoring situations.
API's and OOP method/properly syntax can get very combursome for some types of applications.