NOTE: not all myths discussed are necessarily shared by all or even most OO proponents. They are based on "common notions" found in the business and programming world as I have observed them. I am making no claims as to the frequency of beliefs in the myths. My frequency of encounter may differ from yours. Discussing myths is not the same as "punching a straw-man", which some OO proponents claim I am doing by mentioning myths. Other popular sites talk about myths without quoting the sources for all. Why am I held to a higher standard? If you do not share belief in a given myth, then simply ignore that section. I don't know why this bothers some readers so much. I get constant complaints about it. Note that some of these myths are shared by Meyer.
Some OO proponents have strongly disagreed with a popularity connection between GUI's and OOP. It is difficult to really know for sure why society moves one way or another on such things without detailed surveys or neuron tracing, but my personal observation is that GUI's did impact the impression of OOP. Bertrand Meyer appears to confirm this in Chapter 1 of OOSC2.Screen objects can correspond closely with OOP objects, making them allegedly easier to manipulate in a program. I do not disagree that OOP works fairly well for GUI's, but it is now being sold as the solve-all and be-all of programming in general.
Some argue that OOP is still important even if not dealing directly with GUI's. In our opinion, much of the hype about OOP is faddish. OOP in itself does NOT allow programs to do things that they could not do before. OOP is more of a program organizational philosophy rather than a set of new external solutions or operations.
Although GUI's revolutionized the industry, there is more to computers than GUI's. Also, there may be other ways to handle GUI's besides OOP. Nobody ever bothered to do a proper study. There is more proof to the existence of UFO's than to OOP being the best way to do GUI's or any other programming topic. (Many of the bad impressions of procedural GUI's stem from the fact that 1980's languages and API's used for GUI's were often optimized for the hardware, not for the programmer. They also did not have the benefit of prior experience. They were pioneers.)
I am not saying OOP is useless, per se; many are just frustrated with the fact that OOP has slowed or even reversed programming progress in other areas. I have debated OO fans that appear ignorant to some nifty techniques available in old-fashioned procedural programming. Often times someone will compare C to C++ and conclude that the differences are paradigm differences. Often they are only language differences or ignorance of different techniques. For example, Sometimes programmers complain that subroutine parameters are too sensitive to positional ordering changes or quantity changes. However, named parameters and non-enforcement of parameter quantity can often alleviate these issues if a language supports them. (Named parameters can optionally make routines very Smalltalk-like.)
Table/collection oriented programming is another area that was blunted by OO hype and product penetration. OOP has perhaps contributed some good ideas to programming, but is certainly no holy grail. I am saddened to see non-OO research, training, and tools stop advancing because everybody focuses on OOP instead. OOP's alleged advantages are becoming a self-fulfilling-prophecy because of this. They should prove that OO is better for everything first before pulling the plug on the others.
Object Oriented Technology is a subjective philosophical paradigm only. As we shall see, it is an organizational religion that has not (so far) graduated into a proven general-purpose benefit.
Most experts realize that these examples are not very representative of "good" real world OO programming, and that actual implementation often involves fairly complex arrangements that make real OO messy and more confusing than its competitors. OO fans defend them as "just training examples," but there is rarely a disclaimer of such near the examples.
If you are new to OOP, please don't be fooled by simplistic examples. These bait-and-switch examples often take the form of geometric shapes, animal categories, vehicle taxonomies, employee types, Y2K dates, or bank account examples.
These examples often assume the world can usually be divided into clean, never-changing (or hierarchically-changing) categories or "chunks", in which groups of features always stay together or change in a lockstep dance within generally non-divisible chunks. The truth is messier, and OO is no better optimized to deal with dynamic feature relationships and changes than competitor paradigms, and in many cases seems to be messier in the end.
Also note that in many cases classifications are given by the users via an interface, and not done by programmers via programming code. Accountants build and maintain accounting code categories, for example, and not the programmers. (The internal result is usually database cross-references, and not OOP classes.)
Persistent storage (databases) is especially in turmoil in the OOP world. Some experts say that a company cannot get the benefits of OOP without using Object-Oriented Database systems; others say that companies need to hire "middle-layer" experts to convert conventional databases into virtual OO databases for other programmers. However, there are very few books on database connections to OOP (relative to other OOP titles), and probably fewer experts. Is it a neglected field? Is a layer expert not really needed? Are current OOP programmers doing it wrong because they have no layer experts around?
Further, little is known or agreed on about whether OO's benefits are mostly limited to large projects, or all project sizes. I get very mixed opinions when I ask OO proponents about project size and benefits. (See also: OOSC2 Critique section on project size.)
There is also a lot of mixed opinions about when, where, why, how, and if OO works better for some domains (industry types) over others. One OO proponent will say it works well for games but not embedded systems, and another will say the opposite. Some proponents say it works well for nearly all domains.
In addition, the OOP community is strongly divided on
whether the "component" model or the "inheritance" model
is the way to go. Microsoft seams to favor the component
model. The jury is still out on this one, although
inheritance seems to have the most
modeling and change-handling problems (discussed below).
Using relational tables with OOP requires converting (mapping) fields into objects and visa-verse when putting them back into the tables. This is a painstaking process and can waste a lot of programming time. It reminds one of the luggage and visa inspection process on international plane flights.
Further, the popular OOP languages like Java and C++ require the use of API's or API-like constructs to manipulate databases. API's are fine for occasional or special use, but if 80 percent of your program manipulates tables, API's become a bottleneck.
See Table Oriented Programming for more about OOP, TOP, and tables. (There is a little bit of topic overlap in this document.)
People read this section and keep mentioning "wonderful" products that allow automated mapping of relational tables to OOP objects. However, these appear not to be an improvement over traditional or table-oriented paradigms, but simply a tool to close the gap a bit. Why have an extra layer dedicated to converting one paradigm to another? There is nothing wrong with layers as long as the purpose is to hide low-level details from the programmer or code maintainer, but these mapping tools are translating from one high-level paradigm (relational) to another high-level paradigm (OOP). Thus, they are not improving the system, only unnecessarily complicating it.
There is also talk that OO systems are not very good at ad-hoc queries, which where made popular with SQL in relational systems. Part of the theory behind this claim is that OOP requires most object behavior to be declared in advance. In other words, a predetermined set of methods are associated with a data item (object). This is part of "encapsulation" in OOP lore. One is not supposed to "go outside of" these predetermined methods. Ad-hoc queries often require that unanticipated operations be applied to any and all data items. This appears to violate basic encapsulation principles.
Rather than pick sides on this complicated query issue, just be warned to be careful if ad-hoc queries are an important part of your business. The OOP proponents usually agree that most current OOP systems are not optimized for ad-hoc queries, but claim that it will improve with time.
OOP pushers like to talk about how great it is to meld the methods (procedures) and the data together. Data in traditional relational form is relatively easy to transfer to different systems as technology and vendors change. Procedures are NOT easy to transfer. If you mix the data in with methods, then you are STUCK with the current OO programming language to interpret your data. You cannot easily read the data except with the OOP language and/or program that generated it.
Even if you stay with one OOP language or protocol, one has to spend a lot of time building OO wrappers and mappers for incoming and outgoing data that does not use the same (or no) objects as the internal system. OO systems are in essence Xenophobic.
When a new language fad replaces OOP, how do you convert legacy Java objects (or their state/data) into Zamma-2008 objects? (I will eat a week's pay if OOP is still in vogue in 2015.) How many times do different programming languages and systems have to share data? Very often in the real world. Thus, it is usually safer to keep large data pools separate from methods. Everybody clamors for open systems; why not open data which is not tied to a particular programming paradigm?
Tools like Crystal Reports and data mining systems are much easier to work with if behavior and data are kept more or less isolated.
Some have suggested that object service interfaces like CORBA or COM will satisfy this need. However, these still rely on execution of some language or executable. They also require new interfaces be built for unanticipated requests. They may be satisfactory for external transfers, but not for the more highly-shared internal or close-partner information. Plus, there are non-OO equivalents of CORBA and COM. (DDE, EDI, and CGI are some examples.)
OOP is best used for situations where an OOP program or language should knowingly and exclusively have nearly complete control over the birth, processing, and death of data items (objects). I call this the "cradle-to-grave limit" of OOP. OOP should not be used where sharing is important.
Economic communism spread like wildfire in the first half of the 20th
century because it had such appealing ideals and ideas. Like OO,
these ideals were very seductive on paper. Intellectuals all
over the world were drawn in by its concepts in droves. However,
the complexities and dynamism of human nature proved not to
favor economic communism as a productive model when it hit
the road of the real world.
Perhaps it allows for more equality, but mostly by making everybody equally poor. Further, it forms a kind of "social currency" based on schmoosing and favoritism that is nearly impossible to objectively measure and tax. Thus, it is still not really class-less, but simply makes classes harder to measure.
This reminds me of the tendency of OO to rely too heavily on Protocol Coupling. In OOP this is where one class depends too heavily on the interface of another class. One cannot understand the first class until they understand the second. Further, understanding the second class may require understanding yet another, third class. Thus, you have a "grok-breakdown" chain-reaction in the heavier cases, and mild confusion in lighter ones.
Further, the class depends on the other protocol to work. If you change or remove the other class, you risk ruining the existing one.
It is true that protocol coupling is indeed not necessary in OOP, as OO apologists happily point out. However, removing it from OOP programs makes them look less and less like OO and more like procedural programming. This pattern pops up in other ways also, such as reducing reliance on hierarchies to avoid the myriad pitfalls of modeling with trees (discussed below).
Note that protocol coupling can also happen in procedural/relational programming in various ways (see link). However, in practice it is either less common, uses "lighter wires" such as tables, or the interface itself hides the unnecessary details from the user of a routine.
OOP apologists keep saying, "Well, if doing X creates problems, then don't do X". However, following such advice would leave most OOP programs stripped of characteristics that make OOP programs different from procedural/relational ones. It repeatedly seems that the features which make OOP unique are also the ones that cause the most problems.
In one debate I kept finding problems with the OOP version presented using various change scenarios. So, the OO fan kept re-arranging the methods and structure to reduce the places that needed changing in case variations on the given scenarios popped up again. Finally the OO fan said, "See, it can now handle all of your changes well." My response: "Do you realize that what you now have is a nice procedural module? Toss a few 'self' or 'this' references, and it would almost compile as procedural code." (Paraphrased)
Hierarchies can be divided into 2 categories: static and dynamic. Static hierarchies are classifications that do not change much over time. These include animal classifications, chemistry, and geometric shapes (circles, squares, cones, etc.). Static hierarchies are most often found in nature or occasionally from standards bodies.
Dynamic hierarchies are much more common in business applications. These include company structure, product classifications, employee classifications, plant operation specifications, and so forth. It is my contention that the inheritance model works quite poorly on dynamic classifications.
Some of the bigger problems with inheritance and IS-A modeling are:
In OOP one often would have 2 classes; one for the header and one for line items. To model the line item relationships, you would perhaps have an array/collection of all the line items for a given invoice.
class line_item {.....}
class invoice_header {
private line_items is array of line_item
...
}
However, the flaw in this is that you have to
go through the invoice header to get at the detail.
Sometimes you want to look at the detail regardless of
its "parent." For example, you may want to
count or sum all of product X that was shipped in a
given month. In that case, you don't care about the
parent invoice. (SQL: "select sum(price) from
line_items where product_id=X and month(shipDate)=Y")
Note that the different views may be needed for more than just ad-hoc queries. There could be regular reports on sales by product category, delivery delays (shipping orders are not always 1-to-1 with invoices), sales-by-vendor, sales-by-weight, sales-by-quantity, etc. In my opinion, it is risky to assume that one view aspect will always be the strongest or only aspect (such as by-invoice in this case).These kinds operations make me hesitant to "hard-wire" a hierarchy into a model. Often one does not want to travel through trees to get stuff that does not relate to the tree. (File directories also often suffer from this flaw.) Relational thinking is more adaptable this way because it tends not to say that one relationship is more important than another; thus, less artificial relationship favoritism. A tree is only one of many possible views/relationships of data. (Note that the different views may be simultaneous in many cases.)Another example of this is time-sheet detail lines. In one consultancy company, the project worked on for a given detail line was a more commonly-used relationship than the time-sheet or employee entity. Thus, modeling via a code-wired header-detail hierarchy could have been a rather large burden (code-wise and CPU-wise) if every project-oriented data request had to first go through the header (parent).
Note that both of these examples do not involve OOP inheritance. Abuse of hierarchies is not just limited to explicit inheritance. Also note that some languages can map array syntax to external or shared data sources. In such cases, some of the criticisms above may not apply.
In business you often look at or use the same data in many different ways. Getting too "taxonomy happy" is a hindrance to this.
Some will argue that overuse of inheritance or hierarchies is the fault of the programmers, and not the fault of the OOP paradigm. However, why build in a complex organizational paradigm like inheritance when its real-world use is quite limited? It just invites misuse. Inheritance is probably the most misused OO feature. Complexity should be "spent" with great care. The bottom line is that OOP's brand of inheritance should not be a base part of a language that claims to have a general purpose focus. It is abused by inexperienced or ignorant programmers far more often than the times that it actually helps software organization for the long run.
Some might also contend that inheritance should be kept in a language for the rare times when static inheritance does occur. I disagree because the very fact that static hierarchies don't change very often implies that an organization paradigm, such as OOP inheritance, is not an important issue. If something rarely changes, then change-management of it is not something to spend too much time or language bloat on. Language complexity is better spent on stuff that occurs often.
HAS-A relationships are simply more flexible. I agree that there are some potential compiler checking features and syntactical shortcuts that IS-A can provide, including popular forms of polymorphism. But adaptability is usually more important. IS-A is a stronger modeling statement, and thus is harder to undo than HAS-A.
The bottom line is that HAS-A relations can easily serve as IS-A relations with little or no change, but not the other way around. And, OO loses much of its differences under HAS-A. Classes become little more than procedural "modules" under HAS-A.
Some places where IS-A can cause problems:
My favorite political analogy for this is that it is like having to vote for a party or candidate when what you really want to do is vote on specific issues like abortion, defense spending, social programs, etc. The "granularity" of voting is often not fine enough. A given candidate will rarely support all the same issues that you do. An example manifestation of this is the need to override only one line instead of a whole method.
This kind of "lump thinking fallout" can be found in hierarchical taxonomies (A.K.A. "large sub-types"), method boundaries, noun-based "encapsulation", and strategy isolation. (Most of these were already briefly mentioned above. The hyperlinks go into more detail.) There are OO "workarounds" to some of these problems, but the cure is often worse than the disease. I usually find procedural techniques more friendly with regard to many kinds of changes that don't fit the boundaries or coverage of typical OO divisions. Changes that would only require the simple insertion or changing of an IF statement expression in procedural/relational programming may require moving OO methods from one sub-class to another, or splitting a method and all it's same-named polymorphic "cousins" into smaller chunks. In other words, more work and more code changes. Too much "shuffling around".
I suppose that the intellectual appeal is that you can manipulate and manage things in larger or more "structured" chunks. You know, "hiding details", "encapsulation", and other buzz-phrases. I am not against the goal of this in any way. It is just that OO's approach to these goals falters when the real world does not fit into or change within the confines of the boxes OO lays out.
It may be time to focus on languages and paradigms optimized for the less-than-ideal conditions. There is a complexity cost to OO. Thus, if its payback is not sufficient, it may be a drain.
Note that this point of view is not necessarily suggesting
that OO is "bad" per se, but that it has a different area of
optimization than other paradigms.
Organizational benefits are features that allegedly help organize software better so that it can be maintained and managed easier. However, OOP simply tends to favor one organizational grouping at the expense of another, rather than provide better grouping than procedural programming or other paradigms. (We have already seen how dubious inheritance, another organizational concept, can be.)
Protection benefits are similar to "strong typing". Strong typing is where a variable or object cannot be used with an operation that is not meant or not defined to handle that particular type. It is meant to protect the programmer or process from accidental misapplication of variables and/or objects to the wrong process, property, or assignment.
There are various pros and cons to strong typing. The pros are better compiler or interpreter detection or prevention of certain types of programmer mistakes, and the cons are that more "setup" information needs to be supplied and more conversion or translation between types is often needed. (This article at Scriptics Corporation suggests that weak typing results in much faster software development cycles.)
Further, as more systems are hooked together in various ways across vast distances and with different server and software brands, the ability to check interfaces and data services at compile-time is reduced. Thus, the value of compile-time checking may be diminishing in many domains.
Regardless of whether strong or weak typing is actually better, OOP appears to buck the general trend toward weaker typing in languages used for custom business applications, smaller scale projects, and rapid development projects. (Strong typing is perhaps preferred for mass-market tools, OS building, and medical applications.)
Note that OOP does not necessarily have to be strongly typed (look at Smalltalk), but a weakly typed OO language loses its claim to object/method mismatch protection. In which case, one is left with only the flimsy organizational benefit claims.
A decent study should have these aspects:
Some studies that are often cited were very limited in scope. For example, a large project was converted from C to C++. Allegedly, the new codebase was much smaller.
I consider this a poor test because C is a fairly low-level language, while C++ can be given higher-level constructions. I have, for example, converted a C music sequencer into XBase with a great improvement in productivity and a great reduction in code size. (Almost any language is an improvement over C. Its strengths are in OS portability and speed; and little else. However, C fans may disagree.)
Note that I must reject anecdotes as evidence because:
One Mind Fits All? |
|
You can tell a wood craftsman how you want the end result, but you are foolish to tell him how to construct it. If you are also a wood crafter, then you can offer tips, but only each individual knows what works best for them self in the end. (There are issues of maintenance teams, but this is too involved to discuss here.)
There are other paradigms that are not heard about in the press and brochures, or perhaps not yet fully explored, that could be an equal or better approach for an entity (person, culture, or application) than object paradigms. Even procedural programming is under-explored in some cases. Also, many programmers mistakenly use C as representative of procedural languages and are glad to move on to C++, which does offer more power simply because it has more features and vendor tools for it.
The OOP prophets out there promoting OO may indeed find objects better for themselves or their teams. However, they should not extrapolate this to everyone. Just because they think in terms of OOP objects does not mean everyone else does. I find that software designers and builders think very differently from each other. The human mind and business cultures come in many flavors, so perhaps our paradigms should do the same.
I often hear OO proponents say something along the lines of, "I always built my software in an OO-ish way, I just did not have a name for it. Then OOP formally came along and I was happy to see it built into the language."
I, on the other hand, think best in terms of tables and like to organize software projects using control tables. I have seen others do the same without ever knowing that they were using a "table paradigm." While OOP attempts to keep you on course by putting up edge barriers, control tables keep you on course by giving you a better view of the course. They both have similar goals, but use very different approaches to achieve these goals. If Table Oriented Programming were simply given a recognized identity, other tablers might also come forward. Another table fan had this to say:
...I was always told that OO was an intuitive way of thinking. Although I develop daily in OO languages, I do find OO rather unintuitive (although I manage). ... If I analyze a problem, I draw tables not objects and classes, I think in tables, those are much more intuitive to me. Even when I program in OOP, I rely as much as possible on tables. ...it's nice to see that I'm not the only one who 'thinks' in tables. ...
This implies that a programmer's personal preference is more important to getting results than the language or paradigm itself. In other-words, if a programmer can work with the tools and languages that he or she most prefers, then productivity and quality may be much higher than from passing down edicts based on a blanket choice of tool, language, or paradigm.
My personal experience tends to confirm this. When given tools that allow me to maximize my use of Table Oriented Programming, for example, I seem to get things done much faster and am more satisfied with the results. I have heard fans of other languages and techniques say similar things.
Thus, if you really want to get things done, hire a zealot or a team of like-minded zealots. "The best tool for the mind" might be just as important as "best tool for the job". Regardless, Zealot Oriented Programming is an area ripe for more research.
One caveat is the some zealots focus too much on following a certain methodology rather than getting something done. Thus, you have to be careful about distinguishing ideological zealots from practical zealots. It just may be that different forms of zealotry are needed for different situations. For example, the Eiffel language and philosophy involve building many "safety-checks" into the software to assure compliance with specifications. This philosophy and it's corresponding followers may not do well if fast turnaround time is required, but may be ideal for life-support hospital applications or missile applications, where mistakes can be deadly. (See also Long-Term Issues.)
OOP appears to be doing the same thing. It takes small or imagined problems and magnifies them to the point of irrational paranoia. Global variables*, case statements, and other straightforward procedural techniques are now officially banned by the Church of Objects.
It does not matter how many OO wrappers, mappers, meta structures, meta classes, adapters, or converters it takes, those evils must be stamped out at all costs. It does not matter that the resulting OOP program is bloated, tangled, and confusing; as long as the Great Evils are eliminated.
Sure, global variables sometimes cause name conflicts and other problems, but is sure bloat and roundaboutness better than a relatively slim possibility of a name conflict?
* There are procedural ways to group routines and scope to reduce or eliminate what would be global variables, but these rarely get any attention. Perhaps because name conflicts are not common enough to justify the extra syntax and expert proceduralists know this. Often times globals can be reduced or eliminated by using tables to store shared values or having more flexible access to tables. I am not promoting mass use of globals. Used judiciously, they are rarely a problem in small and medium applications in my experience.Bloat creates errors because of confusion and distracting clutter. Why are the errors introduced by having too many airbags somehow better than errors introduced by having none? Double Standard!
It reminds me of the cartoons where the main character destroys his entire house trying to kill a single mosquito. Perhaps that is where the term "buzzword" came from.
This is similar to a child's trick of spreading the unfinished food around the plate so that it looks like the child finished his or her food. Thus, the parents may accuse the child of being messy instead of not finishing his or her food.
The equivalent of the Case decisions are still there in OO
programs. They are simply spread around to such places as object
assignment and subclass comb
structures. OOP simply inverts the nesting and grouping
structure that is
found in traditional (procedural) programming. Inverting is not
the same as eliminating a structure. Further, putting methods
together based on variation (subtype) often pulls them apart with
regard to behavioral grouping. Behavioral grouping is at
least as important. For some undefined reason, OO fans tend to
dismiss the importance of behavioral grouping.
Examples which show sub-classing "reducing the quantity of spots that need updating" over case statements are misleading because they tend to ignore the fact that sub-classing increases the quantity of changes for the addition of new operations. In other words, sub-classing favors subtype-oriented changes at the expense of operational changes. It is more or less a zero-sum tradeoff, and not the free lunch that many OO proponents suggest. I see the OO side of the story all the time, but rarely are the down-sides mentioned. Further, real world occurrences of case statements usually don't match the OOP textbook pattern of repeated case-lists (except in poorly-engineered code).
OOP sometimes takes credit for ideas that are not necessarily part of OOP. For example, some criticize the variable scoping rules of procedural languages, saying that OOP improved it. However, some procedural languages like Pascal already allowed multiple levels of variable and procedure scoping before OOP became a mainstream fad.
Having variable parameter types and quantities has been part of many interpreted procedural languages a good time before OOP became a mainstream fad. For example, in XBase you can use the Type() function to query a parameter type. (It lacked formality, but it was there.)
Many of the table oriented approaches
that I recommend are sometimes called "OO in disguise" by some
OO fans. However, these have been in use in various
forms long before OOP was invented in 1967. (Improvements in
relational, indexing, and table browsing UI technology
make them easier to do now.)
They are usually making the mistake of comparing C to C++. C is just too low-level to serve as a decent representative of the potential of procedural programming.
Garbage collection in procedural programming usually comes about by having the end of a routine or end of the program automatically close out the structures and mark the memory as available for other uses. Example:
sub X {
local Y
global Z
Y = openTable("foo")
Z = openTable("bar")
doSomething_with(Y)
doSomething_with(Z)
}
In this example, the table referenced by handle
Y is automatically
closed and de-allocated when routine X finishes.
However, Z is not closed because it is connected
to a global variable. It will automatically
close at program end, though. (There would also usually
be something like a "close(Z)" operation
that could be used if disired.)
This is one of many approaches. There are different techniques for implementing this. In some languages the programmer can define or access "scope-end" triggers for variables (including structure "handles"), in others it only comes about when using built-in structures.
The issue of "de-allocating" external resources versus internal resources can get quite involved. Java's "Finalize" method is sometimes cited as a way to automatically de-allocate external resources automatically, but is nearly useless in practice because of the nature of Java. Another sticky issue is why/if external resources should be managed differently than external ones in the application code. The idea of swappable engines seems counter to this idea. I am planning on writing more about these issues in the future.Further, if you use tables instead of arrays and linked lists, then memory management is often handled by the table engine and not the application programmer. A decent collection system can handle and "have ready" many different operations. It reduces a lot of wheel reinventing.
I just heard someone say that they found an old procedural program of theirs that used too many global variables and too many parameters. Rather than blame his bad programming or lack of knowledge about procedural/relational organization techniques, he blamed the paradigm and used it as a sorry excuse to proceed with OOP.
OO fans see no problem with reading 1,700 page books about "proper OOP", yet never touch a one-page guide to "proper Procedural/Relational programming" (if they make such a thing anymore). In my opinion good procedural/relational (p/r) techniques are much easier to learn than OOP because they are less abstract.
However, nobody is championing procedural/relational anymore because it is out of style and a risk to career face right now. Americans have a habit of switching IT fads before perfecting the current one.
Also, many bad procedural/relational memories came from nasty languages like C. C is about as primative as one can get above assembler.
A common misconception is that one has to change all the procedural code if a DB field type or size changes. For example, if a percentage rate changes from an integer to a float/real, then OO fans often claim that it causes a procedural code change cascade. However, this usually only matters in strong-typed languages, like C, Java, and Pascal. (Weaker typing is better for p/r usage in my opinion.)
The fact that these claims come up time and time again testifies to the p/r ignorance that is floating around out there. These OOrban legends spread around like bad gossip via greedy snake OOil salespersons.
It is also sometimes stated that procedural/relational software cannot factor as well. I have found that this is not true. Given a few dynamic features and flexible variable scoping control, I can factor any procedural/relational program as small as an equivalent OO program. (The result may not always have the same built-in protection against forgetful maintenance programmers, but it is not more code.)
Another false bash of procedural/relational (P/R) is given by Bertrand Meyer (Ch. 5 of OOSC2). He sets up a false dichotomy of top-down versus OO. I have many times written P/R software without locking the units into the kind of arbitrary top-down ordering he describes. His dichotomy is purely fiction. Perhaps he likes OO so much because he is such a crappy proceduralist. Just because you cannot ride a bicycle does not mean that a horse is better for all.
Perhaps this is why components are much more popular than OO business modeling in actual applications being produced.
Components tend to have self-contained sources of state or data. However, business processes tend to need state (data) from a wide span of sources (other entities or classes). In other words, business processes have their tentacles all over the place. Things like month-end processing or calculating customer and volume discounts can often involve references to 4 or more entities in many systems. Even processes that start out under one entity often expand to multiple entities over time. (Accessors excepted. That is another issue.)
This is a major reason that OO business modeling is nearly hopeless. There is no one entity to associate primary behaviors with unless you arbitrarily pick one based on current references (which may change tomorrow). The remaining choices are either a bunch of confusing middle-men classes, or a class that is a behavioral class. A behavioral class is otherwise equivalent to a procedural module or subroutine.
By the way, I think that components can be done well in procedural contexts, just not in the C and Pascal-like languages often associated with procedural programming.
The term "object" in OOP is somewhat of a misnomer. It implies that OOP closely matches things in the real world such as cars, customers, invoices, etc. However, the biggest difference between OOP and competing paradigms is in how behavior is associated with related categories. Related categories are things that belong to the same family, such as circle, square, and triangle belonging to a "shape" family; or milk, cheese, and butter belonging to the family of "diary products".
In the real world, associations between behavior and physical objects is fairly weak and highly dynamic. But, we cannot recreate this in the computer, and therefore are forced to pick a very rough approximation to help our computers model a restricted subset of the real world.
In the real world, most "objects" can be acted upon by a wide variety of behaviors (from other objects). This includes earthquakes, floods, people, pets, vacuum cleaners, wind, flying golf balls, cars, new government regulations, etc. Real world objects do not have a list of predetermined "proper" behaviors associated with them, which is essentially what OOP does.
If the real world was like OOP, then we would get scenarios like,
"I am sorry Mr. Mugger, but I have no
Unlike the real world, OOP places strict limits about what can do what to what. (Inheritance allows larger sets of behaviors to be included on an object's "behavior list", but there is still a limit and inheritance usually cannot be done ad-hoc in a practical way.) OOP tends to be even pickier about behavior association than other paradigms. This makes it even further removed from the real world.
Some argue that computer application tend to deal with a relatively narrow subset of behaviors per object, and thus the strict behavior association is not a hindrance to building good OOP applications. This may indeed be true, but it still follows that OOP does not better model the real world than other paradigms. If anything, it is a worse model of the real world.
There are also other ways OOP deviates from the real world more than other paradigms. For example, normalized RDBMS tables often model the world quite well: one record per real-world "object". That object may be a customer, employee, invoice, part, etc. Many OO methods bring about abstractions that are not one-to-one with real-world objects. For example, one is encouraged to reference an address type to avoid repeating the address structure for a real world object. This may be considered higher abstraction, but is not a better fit to the real world. This is because the real world has a certain amount of redundancy and repetition that may be seen as counterproductive to a computer application. (Note that RDBMS can also have a single "Address" table if desired. I am not saying here whether this practice is bad or good, only saying that it deviates from the real world.)
Finally, as we saw above, inheritance is rarely applicable to the way real world collections (data) change over time. Real world changes rarely care about sticking to an artificial inheritance hierarchy.
They are also using the line that it will improve to acceptable levels over time. We would prefer that it be perfected and proven before it's shoved down our throats, not after.
It now seems like IBM-based mainframes and big servers are back in style. Everyone thought they were dead. They became so unfashionable in the early 90's that some companies got rid of them out of embarrassment, not out of any well-studied need.
One of the reasons mainframes are almost vogue again is because the GUI interface (user's screen) of web-based systems is no longer directly defined by the operating system or the hardware. Generating HTML output or talking to a Java applet (Web technology) is almost as easy on a mainframe as it is on a desktop PC.
Who knows, perhaps DOS will be the next "thing" for web server programming platforms. After all, Linux is making command lines "sexy" again (even though it usually comes with a GUI for most tasks.)
In short, business programming does not get the respect that it deserves. Just because it has failed to produce the aura of "rocket science" that other niches have, does not mean that business programmers should have to put up with technologies and paradigms better tuned for other niches.
It is time to stand up and be heard.
Note that despite its reputation, business development can get very tricky. Often this is because of the complex, multi-aspect, dynamically-changing rules that can affect anything and everything. It is tough to find isolated units (encapsulation, black box, etc.) in order to narrow your focus. (See "Fingers" above.)

"The good things about OOP [concepts] are often the information hiding and consistent underlying models which derive from clean thoughts, not linguistic cliches." (from S. Johnson, see links below)
End the Hype and Start the Thinking!