aspose file tools*
The moose likes OO, Patterns, UML and Refactoring and the fly likes Is DRY enough to justify inheritance? Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Engineering » OO, Patterns, UML and Refactoring
Bookmark "Is DRY enough to justify inheritance?" Watch "Is DRY enough to justify inheritance?" New topic
Author

Is DRY enough to justify inheritance?

Junilu Lacar
Bartender

Joined: Feb 26, 2001
Posts: 5288
    
  10

Our application originally read from a third-party product's database. Now we need to extend the application to read from a newer version of the database. The problem is that some tables in the new version may have small but significant differences in their structures from the tables with the same names in the old version. For example, some columns that were in one table in the old version have been moved to another table in the new version. Also, the columns that make up the primary keys of some tables have changed in the newer version (the database designers unfortunately decided to use composite primary keys).

As we started to create classes to map to the new version of the database, some folks on our team suggested that we should refactor to eliminate code duplication (Don't Repeat Yourself) and create base classes to hold the attributes and corresponding getters and setters common to both versions of the databases and create subclasses for each version to hold attributes that were version-specific.

I raised the concern that since we were probably not going to write code that would process objects referenced as the base class, i.e. code that adhered to the
Liskov Substitution Principle, that could correctly process objects mapped from either database version, inheritance really didn't make sense. My concern is that creating an implementation inheritance hierarchy in this case would make our code unnecessarily brittle without any benefit other than eliminating the duplicate code of attribute declarations and their getters and setters.

On the other hand, if we won't write code where it will be possible to violate LSP anyway, so what?

Hence my question: Do you think that trying to keep DRY is enough to justify using inheritance in this case? Or does the fact that there are significant differences in the database tables to which we are mapping make it all right to have two separate but very similar sets of classes?

TIA,


Junilu - [How to Ask Questions] [How to Answer Questions]
Vladas Razas
Ranch Hand

Joined: Dec 02, 2003
Posts: 385
Maybe bridge pattern would help?

From GoF book:

Intent

Decouple an abstraction from its implementation so that the two can vary independently.
Vladas Razas
Ranch Hand

Joined: Dec 02, 2003
Posts: 385
Or does the fact that there are significant differences in the database tables to which we are mapping make it all right to have two separate but very similar sets of classes?


For this one you could use strategy pattern.
Scott Ambler
author
Ranch Hand

Joined: Dec 12, 2003
Posts: 608
Instead of coding around the questionable work of the database designers, why don't you refactor your database schema instead? Why complicate your code when the real problem appears to be elsewhere? See Process of DB Refactoring.

- Scott


<a href="http://www-306.ibm.com/software/rational/bios/ambler.html" target="_blank" rel="nofollow">Scott W. Ambler</a><br />Practice Leader Agile Development, IBM Rational<br /> <br />Now available: <a href="http://www.ambysoft.com/books/refactoringDatabases.html" target="_blank" rel="nofollow">Refactoring Databases: Evolutionary Database Design</a>
Edwin Keeton
Ranch Hand

Joined: Jul 10, 2002
Posts: 214

Assuming you're unsuccessful in your efforts to refactor a third-party database schema, I would suggest there are better ways to recognize the DRY principle than inheritance, such as applying the patterns others have suggested.


SCJP, SCWCD
Mr. C Lamont Gilbert
Ranch Hand

Joined: Oct 05, 2001
Posts: 1170

Is this a loaded question? I never heard of DRY before but it has nothing to do with object oriented design IMHO. The answer is NO

LSP and DRY don't represent the same tings. One saves typing, one encapsulates variation. I would absolutely not share base classes. I would absolutely duplicate the classes. Ctrl-c, Ctrl-v. Of course thats just my opinion and I dont run a big software house.

But it seems bassackwards to encapsulate this type of variation. On the one hand, if a common bug is found, both immediately benefit from the single class fix. On the other, if a new version of the schema is created, you will end up refactoring the base class, the two existing extenders of that class, and adding the new class...
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Mr. C Lamont Gilbert:
Is this a loaded question? I never heard of DRY before but it has nothing to do with object oriented design IMHO. The answer is NO


Well, it doesn't have something to do with OO, but it *has* with design.

I would absolutely not share base classes. I would absolutely duplicate the classes. Ctrl-c, Ctrl-v. Of course thats just my opinion and I dont run a big software house.


I probably wouldn't use simple inheritance, if only because it sounds too brittle to me. I definitely wouldn't rape and paste, either.

It's hard to say what I'd do without knowing much more specifics about what the classes do and where they need to differ, but I'd probably think about using Strategies, meta data or perhaps even code generation.

For example, how much of the difference between the classes behaviour could be inferred from the schemas themselfes?


The soul is dyed the color of its thoughts. Think only on those things that are in line with your principles and can bear the light of day. The content of your character is your choice. Day by day, what you do is who you become. Your integrity is your destiny - it is the light that guides your way. - Heraclitus
Junilu Lacar
Bartender

Joined: Feb 26, 2001
Posts: 5288
    
  10

We don't have the option of modifying the third-party database schemas. The databases are used to hold data that the vendor's Big piece of business machinery (bonus points if you can guess who ) creates from scanning documents. The vendor supplies the software packages that take input from the machine. We now have to interact with the two different versions of the software package and thus two different versions of the databases.

We are, in essence, extracting data from the third-party database(s) and persisting it to a database that we do have control over. We will have only one set of classes to model our side of the fence and these are the classes that we use to perform most of the business logic.

The classes in question are on the data extract side though. It didn't make sense to me to create an inheritance hierarchy for the extract classes because we would have separate adapter classes anyway. I.e. in the diagram below, it didn't make sense to me to make dtoV1 and dtoV2 be subclasses of some dtoBaseClass just to eliminate duplication of the getter/setter code for attributes that were common to both versions.



Thanks for your feedback and suggestions.

[ September 22, 2005: Message edited by: Junilu Lacar ]
[ September 22, 2005: Message edited by: Junilu Lacar ]
Stan James
(instanceof Sidekick)
Ranch Hand

Joined: Jan 29, 2003
Posts: 8791
I'll just suggest you can remove LSP from the discussion. If the common bits are in an abstract base class there is no possible way to use the base class so there is no need for subclasses to bu substitutable.

I like pluggable strategies and probably use them too much.


A good question is never answered. It is not a bolt to be tightened into place but a seed to be planted and to bear more seed toward the hope of greening the landscape of the idea. John Ciardi
Scott Ambler
author
Ranch Hand

Joined: Dec 12, 2003
Posts: 608
Doh! Sorry about that, missed the third party aspect of the db.

- Scott
Vladas Razas
Ranch Hand

Joined: Dec 02, 2003
Posts: 385

it didn't make sense to me to make dtoV1 and dtoV2 be subclasses of some dtoBaseClass just to eliminate duplication of the getter/setter code for attributes that were common to both versions.


If I understood correctly, you don't need to write strategies for every attribute. Only those that differ between your and vendor database.
Mr. C Lamont Gilbert
Ranch Hand

Joined: Oct 05, 2001
Posts: 1170

Originally posted by Ilja Preuss:


I probably wouldn't use simple inheritance, if only because it sounds too brittle to me. I definitely wouldn't rape and paste, either.

It's hard to say what I'd do without knowing much more specifics about what the classes do and where they need to differ, but I'd probably think about using Strategies, meta data or perhaps even code generation.

For example, how much of the difference between the classes behaviour could be inferred from the schemas themselfes?


I think you should look at the situation differently. Its not variance he is trying to abstract away, but versionance. I believe trying to use a class heirachy to encapsulate change due to versioning is a big mistake. Thats what CVS is for. Start a new branch, which is basically ctrl-c ctrl-v.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Mr. C Lamont Gilbert:
I think you should look at the situation differently. Its not variance he is trying to abstract away, but versionance. I believe trying to use a class heirachy to encapsulate change due to versioning is a big mistake. Thats what CVS is for. Start a new branch, which is basically ctrl-c ctrl-v.


Branches are good if you need to support old versions of your software for some time. They are horrible, in my experience, for maintaining different versions of the software that need to be evolved concurrently, *especially* if you have to use CVS. A combination of metadata and polymorphism is the right choice in that case.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Junilu Lacar:

We are, in essence, extracting data from the third-party database(s) and persisting it to a database that we do have control over. We will have only one set of classes to model our side of the fence and these are the classes that we use to perform most of the business logic.


How complex is that transfer? If it is relatively straightforward, that is can be expressed in relatively simple rules such as "table1.attribute1 needs to be mapped to table3.attribute4 and multiplied by 10 on the way", you could write the mapping in a more declarative style and have your java program interprete and execute it.
Mr. C Lamont Gilbert
Ranch Hand

Joined: Oct 05, 2001
Posts: 1170

Originally posted by Ilja Preuss:


Branches are good if you need to support old versions of your software for some time. They are horrible, in my experience, for maintaining different versions of the software that need to be evolved concurrently, *especially* if you have to use CVS. A combination of metadata and polymorphism is the right choice in that case.


The way the current base class will be created is to look at what is similar between the two versions and put that in the base class. When a third version comes, it could have less in common. Should we;
  • remove that part that is not common across all 3 versions from the base class and modify the two existing versions.
  • start a new base class for our new version?
  • ?


  • [ September 24, 2005: Message edited by: Mr. C Lamont Gilbert ]
    Junilu Lacar
    Bartender

    Joined: Feb 26, 2001
    Posts: 5288
        
      10

    Originally posted by Ilja Preuss:
    you could write the mapping in a more declarative style and have your java program interprete and execute it.


    We are using Hibernate to do the O-R mapping. The vendor's database uses composite keys, which we decided we did not want to do in our database. This made it necessary for us to write code to "convert" the objects mapped to the vendor's database into the objects that we had mapped to our database. The conversions were not necessarily one-to-one and each version of the vendor's database requires slightly different conversions.

    I did manage to get the team to go along with my preference of not creating a base class but I don't think they are totally convinced that it was not a good idea to create one so that they could eliminate duplication. I posted the question because I was hoping to add to my seemingly unconvincing answer that "it will just make the code more brittle and I don't think it's worth creating a hierarchy that doesn't provide any other benefits or use."
    Ilja Preuss
    author
    Sheriff

    Joined: Jul 11, 2001
    Posts: 14112
    Originally posted by Junilu Lacar:
    We are using Hibernate to do the O-R mapping.


    So you are actually doing a R-O-R mapping? I wonder wether it might be easier to just map from relational to relational, if you just want to convert from one schema to another. With other words, I don't see how putting the data into objects actually helps you...

    The vendor's database uses composite keys, which we decided we did not want to do in our database. This made it necessary for us to write code to "convert" the objects mapped to the vendor's database into the objects that we had mapped to our database. The conversions were not necessarily one-to-one and each version of the vendor's database requires slightly different conversions.


    Mhh, could you write reusable converter classes, perhaps combining different rules using the composite pattern or something like that?

    I did manage to get the team to go along with my preference of not creating a base class but I don't think they are totally convinced that it was not a good idea to create one so that they could eliminate duplication. I posted the question because I was hoping to add to my seemingly unconvincing answer that "it will just make the code more brittle and I don't think it's worth creating a hierarchy that doesn't provide any other benefits or use."


    I can understand your feeling that it's too brittle. On the other hand, I'd really try to remove the duplication in *some* way. If it isn't clear which way, I might well start with simple inheritance, and refactor away from it when I learn more about the needs of the system...
    Ilja Preuss
    author
    Sheriff

    Joined: Jul 11, 2001
    Posts: 14112
    Originally posted by Mr. C Lamont Gilbert:
    The way the current base class will be created is to look at what is similar between the two versions and put that in the base class. When a third version comes, it could have less in common.


    Yes, it could - or it could not. If we don't know yet, we probably should keep our options open.

    Should we;
  • remove that part that is not common across all 3 versions from the base class and modify the two existing versions.
  • start a new base class for our new version?




  • extract the differences into Strategies
  • handle the differences using the Interpreter pattern
  • use a more data driven approach
  • something I wouldn't think of before actually being confronted with the situation, having gathered a lot of feedback from trying different things and discussing them with my coworkers
  • any combination of the above



  • I would work hard on removing the duplication, and I would never (mis)use CVS branching for this. Seriously.
    Mr. C Lamont Gilbert
    Ranch Hand

    Joined: Oct 05, 2001
    Posts: 1170

    Originally posted by Ilja Preuss:


  • extract the differences into Strategies
  • handle the differences using the Interpreter pattern
  • use a more data driven approach
  • something I wouldn't think of before actually being confronted with the situation, having gathered a lot of feedback from trying different things and discussing them with my coworkers
  • any combination of the above



  • I would work hard on removing the duplication, and I would never (mis)use CVS branching for this. Seriously.


    To be honest, the OO encapsulation of variation did its job when they choose Hibernate. They have isolated the business side of their application from the changes in the datastore. Your OO design approach to solving this versioning issue is an attempt to isolate their DAO layer from changes in the datastore. I firmly believe that is impossible. But you can isolate one DAO layer from another.

    I suppose now we are having the arguement the OP is having with his coworkers Probably this is because we have different experiences. The path I choose provides easier protection from future versions. The path you choose provides easier maintainance of current versions. At least that is how it looks to me.
    Ilja Preuss
    author
    Sheriff

    Joined: Jul 11, 2001
    Posts: 14112
    Originally posted by Mr. C Lamont Gilbert:
    To be honest, the OO encapsulation of variation did its job when they choose Hibernate. They have isolated the business side of their application from the changes in the datastore.


    I'm not following you. If that were true, they wouldn't need to change the classes, they'd just need to change the hibernate mapping, or so it seems to me.


    Your OO design approach to solving this versioning issue is an attempt to isolate their DAO layer from changes in the datastore.


    Is it?

    The path I choose provides easier protection from future versions. The path you choose provides easier maintainance of current versions.


    The more we work on having the code express and handle the current differences in versions smoothly, the more flexible it will become with time to also handle differences in future versions smoothly. It will become less and less effort to support another version.
    Mr. C Lamont Gilbert
    Ranch Hand

    Joined: Oct 05, 2001
    Posts: 1170

    Originally posted by Ilja Preuss:

    The more we work on having the code express and handle the current differences in versions smoothly, the more flexible it will become with time to also handle differences in future versions smoothly. It will become less and less effort to support another version.


    This depends strongly on versions changing in the same way. A design deals with a specific type of change. If another type of change comes along, it requires a different or modified design. Your not supposed to redesign your architecture when a change comes, but part of your implementation.

    Furthermore, you will put a lot of work into designing an architecture to deal with something that may or may not happen. Its a kind of defensive architecture.

    However, its the DRY that I strongly disagree with. The idea of modifying the base class as a way of abstracting out the change is a poor choice. In this particular case, that would be compounding the problem since the two versions would be strongly tied to this base class, and a 3rd version would force refactoring of everything.

    So, sure OO can work here Just not the base class idea.
    [ September 25, 2005: Message edited by: Mr. C Lamont Gilbert ]
    Junilu Lacar
    Bartender

    Joined: Feb 26, 2001
    Posts: 5288
        
      10

    Originally posted by Ilja Preuss:
    they'd just need to change the hibernate mapping, or so it seems to me.


    That was my first thought, actually. However, the differences were significant enough to make re-mapping a bit of a challenge, the biggest one being the use of composite primary keys and the differences in them between the two versions.
    Ilja Preuss
    author
    Sheriff

    Joined: Jul 11, 2001
    Posts: 14112
    Originally posted by Mr. C Lamont Gilbert:
    A design deals with a specific type of change. If another type of change comes along, it requires a different or modified design.


    True. There is no solution that will solve any future problem, so the solution will need to be adapted in the future.

    Your not supposed to redesign your architecture when a change comes, but part of your implementation.


    What do you mean by "architecture" here, and why am I not supposed to "redesign" it?

    Furthermore, you will put a lot of work into designing an architecture to deal with something that may or may not happen. Its a kind of defensive architecture.


    What in what I wrote makes you think that I wanted to suggest speculative design? That's exactly *not* what I meant.

    However, its the DRY that I strongly disagree with. The idea of modifying the base class as a way of abstracting out the change is a poor choice.


    Might be or might not be, depending on what future changes bring. Until we know more about that, the most important thing is to keep options open. As someone who is skilled in refactoring, I don't feel that the inheritance solution is a very bad one, although I might well decide to do something different.

    I would refrain from using CVS branches because branching and refactoring don't mix well - extensive refactoring can cause a lot of merging trouble. Therefore branching would force me to commit to a design early, which is exactly what I don't want to do.

    In this particular case, that would be compounding the problem since the two versions would be strongly tied to this base class, and a 3rd version would force refactoring of everything.


    Isn't that a bit of an overstatement? Sounds likely to me that the third version would only force us to refactor those parts that differ in all three cases. I wouldn't expect much more than having to change a couple of Template Methods to Strategies, which doesn't sound too bad to me. Could probably be done in a couple of hours.
    Mr. C Lamont Gilbert
    Ranch Hand

    Joined: Oct 05, 2001
    Posts: 1170

    Originally posted by Ilja Preuss:
    What do you mean by "architecture" here, and why am I not supposed to "redesign" it?


    "Architecture" is the design of your program. For instance, if you adopt the strategy pattern, adding a new strategy is not a change of architecture. However, switching from strategy to chain of responsibility is.


    When you use a tool like hibernate or JDO you have already abstracted your business layer away from your data layer. Or you were supposed to.

    I think ORM works best when you have a DAO layer that is dedicated exclusively to to handling your ORM technology. I know most ORM try to advertise themselves as transparent, but in my experience, you have to create a layer for it.

    My strategy has been to create a common interface for my business layer to interact with. The implementation of these interfaces is basically glued to the ORM technology. I don't try to abstract away from it. I can't begin to imagine how one would maintain a single class heirachy supporting different schemas using ORM. If the schema changes, I toss the layer and create a new implementation.

    That has been successful for me and is why I don't favor maintaining two schemas with 1 class heirachy.
    Ilja Preuss
    author
    Sheriff

    Joined: Jul 11, 2001
    Posts: 14112
    Originally posted by Mr. C Lamont Gilbert:
    "Architecture" is the design of your program. For instance, if you adopt the strategy pattern, adding a new strategy is not a change of architecture. However, switching from strategy to chain of responsibility is.


    Ok. I do that switching all the time. What's wrong with it?

    When you use a tool like hibernate or JDO you have already abstracted your business layer away from your data layer. Or you were supposed to.


    Well, I understood Junilu to say that hibernate didn't fully abstract away from the data layer for his team.

    I can't begin to imagine how one would maintain a single class heirachy supporting different schemas using ORM.


    Well, I'm not very experienced in this regard, but I don't see what should be hard about it. If it is, perhaps using an ORM tool isn't the right approach for Junilu's problem?
    Mr. C Lamont Gilbert
    Ranch Hand

    Joined: Oct 05, 2001
    Posts: 1170

    Originally posted by Ilja Preuss:
    Ok. I do that switching all the time. What's wrong with it?

    Nothing is wrong with it. However, the objective of software design is to prevent this. This happens when it happens, but I feel its undesirable and does not scale well.

    Originally posted by Ilja Preuss:
    Well, I understood Junilu to say that hibernate didn't fully abstract away from the data layer for his team.


    Nope, my experience too. You need a layer dedicated to your ORM. I think this layer should change with the ORM and not try to be abstract from it.
    Ilja Preuss
    author
    Sheriff

    Joined: Jul 11, 2001
    Posts: 14112
    Originally posted by Mr. C Lamont Gilbert:
    Nothing is wrong with it. However, the objective of software design is to prevent this. This happens when it happens, but I feel its undesirable and does not scale well.


    I feel differently about this. The objective of software design is to keep the cost of development and maintenance low. As long as refactorings are cheap - for example by their impact being rather local - there is no need to avoid them. In fact they are a way to keep the cost low, because they allow us to defer design decisions until we have more information at hand to make a good decision. A design decision committed to too early easily can increase costs significantly.

    You need a layer dedicated to your ORM. I think this layer should change with the ORM and not try to be abstract from it.


    I'm not sure I understand what you are saying, or how it relates to what I've said.
    Bert Bates
    author
    Sheriff

    Joined: Oct 14, 2002
    Posts: 8883
        
        5
    you guys ought to bottle up this thread and sell it!


    Spot false dilemmas now, ask me how!
    (If you're not on the edge, you're taking up too much room.)
     
    I agree. Here's the link: http://aspose.com/file-tools
     
    subject: Is DRY enough to justify inheritance?