Oop And Change Impact

Arguments related to ObjectOrientedProgrammings affect on the impact of changes on code and code maintenance effort.


Moved from BenefitsOfOo

Regarding claim: "[OOP] Reduces the impact of requirement changes on code."

I'd like to see an example. The examples in the popular OO books are not very realistic in my opinion. They assume change-pattern probabilities that differ from my experience. And the authors seem afraid of RDBMS. They seem to assume that change pattern X is more likely than change pattern Y, when in my many years of experience, they are either the same, or Y is more likely. Related: ChangePattern.

The reason an example rarely appears is because it's so obvious. The central point of OO (from a changing requirements POV) is the interface, which is a formal specification of how program A talks to program B. Provided the interface remains consistent, changes to A will not necessarily affect B. Interface inheritance makes conformance to an interface substantially easier. Implementation inheritance makes it easier still, provided you have an expressive enough type system (e.g., one which supports mix-ins). When it comes to relational databases, you don't have interfaces to hide behind. If a schema changes, you're right fscked -- the entire god-#### codebase needs to be retrofitted to account for the changes. I've been there, done that, got the t-shirt, sold it on e-bay, and bought myself a book on proper software engineering techniques with the proceeds.

Schemas can be considered an interface. And indirection can be provided with DB "views" if needed. One can also use application-level functions and DB-side stored procedures to "wrap" tables. However, this tends to limit one's ability to use relational algebra instead of iterative app code for many kinds of processes, perhaps flooding the network in the process. But, the devil's in the details. Please, a specific example/scenario.

Name Slice Scenario

I'll go ahead and introduce a scenario here. The database starts out with a schema as such:

  table: contact   // original version
  ----------
  contact_id
  name
  street_address
  city
  etc...
After a year or two, it has been decided to change it to give allow better person name data.

  table: contact   // revised version
  ----------
  contact_id
  last_name
  first_name
  middle_name   // or initial
  street_address
  city
  etc...

Obviously this change would break all application queries that explicitly reference "name".

With an OOP language that hides the difference between methods and attributes, one can simply rebuild the "name" method to be "calculated"; that is, append the 3 sub-parts of the name to produce "name".

However, this trait is not universal among OOP languages. But one could probably rig up an initiator/constructor method to perform the conversion. However, there's still the problem of saving "name". This is not a simple fix. The user-interface (screens) for collecting name info needs changing because business requirements have changed to require detail that it didn't require before. No paradigm (except maybe AI) can get around this part of the problem. It is a change in business requirements.

If one is using basic procedural-style API's to fetch table rows from table "contact", then some change is needed for any procedure using "name". But it is also possible to make a change on the DB side to avoid having to change every procedure. One approach is rename the table "contact" to something else, such as "contact_info", and then create a view called "contact" that looks just like the old table, using a calculated "name" column.

A better choice would be "view columns" such that one would not have to view-ify the entire table to hide the change, but merely one column. If view columns were implemented, then a "dummy" column called "name" would be added to the revised table that would calculate the name by appending the 3 new columns. (See [to be inserted].)

I agree that lack of view columns is an annoying flaw of existing RDBMS, but to be frank it would not used very often. Most of these kinds of schema changes tend to impact known sub-sections of applications such that one does not need to worry about the entire application, in my experience. This is why most vendors aren't in a hurry to add view-columns.

One could also use INSERT and UPDATE triggers to populate an actual "name" column based on the new name parts. But, this can be confusing.

Another approach is to make procedural wrappers, such as "getContact" that returns a row as a map (associative array) or the like. It can calculate "name", similar to the OOP approaches mentioned above.

However, bunches of accessors tend to bulk up code in my experience. Further, one cannot use relational algebra on accessors. It limits one to record-at-a-time processing.


Top's Specific Example of Change Impact Management using Relational Technology

For example, suppose we had a "suspicious contact tracker" sub-application that keeps a list of suspicious customers or suppliers for the Fraud Department to later investigate. We want to have a table called "suspicious_contacts" that keeps track of such. We don't need to move the actual name because we can use just a key to the "contacts" table because we can later JOIN to get it.

Here's a code example that implements QueryByExample-like techniques. I'll use ColdFusion code because it easily allows embedded conditionals in SQL . Comments start with "<!---".

  <cfFunction name="log_suspicious_name">
    <cfArgument name="firstNameMatch" default="">
    <cfArgument name="lastNameMatch"  default="">
    <cfArgument name="p_reason" required="true">
    <!--- Execute SQL query --->
    <cfQuery datasource="std">  
      INSERT INTO suspicious_contacts
        (contact_Ref, reason)
      SELECT contact_ID, '#p_reason#' 
      FROM contacts
      WHERE (1=1)
      <cfIf Not isBlank(firstNameMatch)>
        AND first_name LIKE '%#firstNameMatch#%'
      </cfIf>
      <cfIf Not isBlank(lastNameMatch)>
        AND last_name LIKE '%#lastNameMatch#%'
      </cfIf>
    </cfQuery>
    <!--- error checking not shown for brevity --->
  </cfFunction>

The beauty of this is that no data has to come back to the application. The query is telling the DB to take matching info from one table and put a reference to the matches in the other. The actual processing happens on the RDBMS server.

If we use method wrappers or app-side procedural wrappers, then we'd have to bring each and every row of the "contacts" table over to see if there is a match. Not only can this increase network traffic, but also is likely more coding. If we let the DB do what it does naturally as above, then we save code and network traffic. (The network is usually more of a resource bottleneck than CPU in biz apps.) It can add up with other operations.

Relational languages are a good thing. (SQL has its flaws, but even with them is very useful.) Imperative wrappers are often a poor substitute.

--top


Samuel A. Falvo II's Specific Counter-Example of Change Impact Management via ObjectOrientedDesign

I'm going to offer a counter-challenge -- implementing device drivers for an operating system using purely relational techniques. This is the classic example of polymorphism and interface management, both of which are instrumental components of proper OO design. Consider Unix' philosophy of "Most things are files." That alone is an abstraction. It's an abstraction that you cannot express relationally.

Next, we need to figure out what we can do with those files. Well, you can read from them, and you can write to them, at the minimum:

  class Readable is
    read(data: Buffer, sizeOfData: INT): INT;
  end;

class Writable is write(data: Buffer, sizeOfData: INT); end;

Sometimes, a device has the ability to relocate its read/write pointer too:

  class Seekable is
    seek(offset: INT);
    tell: INT;
  end;

So, for example, a simple RAM disk implementation might be defined as:

  class RamDiskDevice? <: Readable, Writable, Seekable is
    ...
  end;

However, a CD-ROM is not writable:

  class CdRomDevice? <: Readable, Seekable is
    ...
  end;

while a printer is neither readable nor seekable:

  class PrinterDevice? <: Writable is
    ...
  end;

But, what about things like network connections? Those support reading and writing, but not seeking. But, wait!! Network connections support "file-like behavior" only insofar as they're able to emulate a byte-pipe. Networks are inherently message-oriented. Thus, really, only reliable network connections may expose file-like interfaces with the anticipated semantics that doing so implies. Thus, we need a new set of interfaces which are similar to their byte-pipe bretheren:

  class MsgReadable? is ... end;
  class MsgWritable? is ... end;

So far so good: now, what kind of network connections are we talking about? Are these AX.25 connections? TCP/IP or UDP/IP? What about UDP/IP over AX.25? This kind of mixing and matching of software in ways not directly anticipated by any of their authors requires interface consistency. Schemas be damned -- it's an implementation detail at best.

Let's look specifically at AX.25, since I'm most familiar with it. AX.25 supports datagram operation like Ethernet and IP does, so it makes sense to expose its message-oriented interfaces appropriately.

  class AX25Connection <: MsgReadable?, MsgWritable? is ... end;

But, in OS version 2, customers are now demanding that we support AX.25's reliable mode as well (implementing, thus, a byte-pipe). We can implement this change fairly easily:

  class AX25Connection <: MsgReadable?, Readable,
                          MsgWritable?, Writable
  is ... end;

Things to note:

That means, in no uncertain terms, that if a bug were to appear in how AX25Connection handles its reliable-mode activities, fixing the class(es) behind this implementation will also very likely fix a heretofore undetected bug in, e.g., AtmConnection? or TcpIpConnection?. Procedural languages offer this capability too, but only for procedures, never for state. A procedure is worthless without some kind of state to operate on, and if fixing a procedure means I have to update a structure definition that requires the complete recompilation of both the OS and every application for that OS, then its role as a useful notation is compromised.

And that's where we get into OO versus relational. Relational has a lot of good ideas. But OO has even more good ideas, perhaps at the expense of some of those you find so near and dear to your heart. OK, so some OO folks re-invent the relational wheel now and again. But who cares, when it provides a net savings in development effort, code comprehension, and software reliability? SQL provides NONE of these features. Some of the SQL black magic you like to promote not only consumes VERY valuable time resources on the DB servers, but it also results in queries that are positively unreadable to mere mortals! Finally, reliability is compromised because, in my experience at least, sophisticated SQL has a nasty habit of being HIGHLY dependent on a specific SQL provider. Wanna move from MySQL to Oracle? You're SOL unless you want to retrofit all your queries to match. Sometimes, even specific versions of the same database from the same vendor cause issues. My website's blog broke utterly because Serendipity used a non-trivial SQL query which depended on the database's automatic type promotion behavior. When that behavior no longer existed, I had to hand-patch my blog's PHP files. Every. Single. One. Of. Them.

THAT is why OO handles change impacts a whole lot better than relational or procedural. THAT is, therefore, why the industry uses OO over these technologies. And it is, in fact, THAT very reason why functional languages, despite how I love them so, have not conquered the world. In fact, it wasn't until OCaml that people really started looking seriously at FunctionalLanguages in commercial industry, precisely because of its OO capabilities.


Regarding above example, I concede device drivers to OOP. See OverUsedOopExamples. I don't build device drivers for a living. If I did, they OOP may be the cat's meow. I generally don't challenge OOP for building device drivers. OOP may be better for *some* domains. I don't question that. A similar example can be found in TopsQueryResultSet, by the way. --top

First of all, you admit that you don't code anything except management-type applications.

Second of all, you then use your myopic world view to impose your pro-relational-everything dogma on, well, everything. You belittle OOP at every chance you get because it doesn't conform to your world view.

You then make grossly inaccurate claims about, in this case, change impact management based on your self-admitted myopic world view of coding.

What really pisses me off, though, is that when I respond with a VALID COUNTER-EXAMPLE to your claims about OopAndChangeImpact, you belittle ME (you must be near(sic); spelling corrected above) for doing so, despite YOU posting the most over-used RDBMS example in the known universe, while attempting to slough everything under the carpet, pretending to be cordial about it. "It's useful for *some* domains."

Look, for the last time, NOT EVERYTHING IN THIS WORLD REVOLVES AROUND YOU OR YOUR PATHETIC LITTLE MANAGEMENT APPLICATIONS. There is a HHHUUUGGGEEE world of software applications out there, and your little niche doesn't dominate. I'm sorry you feel overly challenged by this, but that's the truth.

Until you actually get hired in a capacity other than management applications, it is not I, but YOU, who is new here. Don't forget that. I've been a coder for some 30 years -- I've seen the fads come and go, and I've seen what worked and what didn't. So if you want to get personal about this, let's. I'd rather not.

I suggest this be moved to DomainPissingMatch. Very few OOP books put domain disclaimers in their writing. They strongly imply that OO is good for all software all the time. And I never said that I *only* do management reporting apps. And please STOP DELETING MY REPLIES! --top

You're lucky I don't delete this entire page. Be happy with what you have.

This is not a domain pissing match, either. You are not going to play favorites as long as I'm here -- you blame OOP for not putting stipulations in their materials on domain applicability. I blame you for the same. Fair is fair. Oh, and I already acknowledged that relational algebra has its uses and applications, but, I don't see that anywhere on this page anymore. So, uhh, PLEASE STOP DELETING MY REPLIES!

I have not intentionally deleted ANYBODY's replies. --top


CategoryObjectOrientation, CategoryExample, CategoryChange


EditText of this page (last edited January 2, 2009) or FindPage with title or text search