wood burning stoves 2.0*
The moose likes OO, Patterns, UML and Refactoring and the fly likes Multiple inheritance, static functions, and code reuse Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Murach's Java Servlets and JSP this week in the Servlets forum!
JavaRanch » Java Forums » Engineering » OO, Patterns, UML and Refactoring
Bookmark "Multiple inheritance, static functions, and code reuse" Watch "Multiple inheritance, static functions, and code reuse" New topic
Author

Multiple inheritance, static functions, and code reuse

Warren Dew
blacksmith
Ranch Hand

Joined: Mar 04, 2004
Posts: 1332
    
    2
There has been some discussion here of code reuse and whether static functions are easier to reuse. There has also been some discussion of whether multiple inheritance is a good thing. I recently ran into a situation where all of these interacted.

I'm working on a fairly large project where tests sometimes need to perform some common tasks during setup or teardown. Some of the database related common code had previously been factored out into a test case superclass for tests that interact with the database. One reused the code by inheriting from this class.

I wanted to factor out some code used by file system tests in a similar way. However, I ran into a problem: if I put them in a separate file system related test case superclass, Java's single inheritance limitation would prevent tests from using both the database code and the file system code. Such tests did exist. Nor could I make the new class a superclass of the database related class or vice versa, as there are tests that deal with the database and not the filesystem, and there are also tests that deal with the filesystem and not the database. I didn't want to glom them all into one huge class.

I ended up factoring the common file system code out into a new class containing only static functions, which got around the single inheritance limitation. I think that multiple inheritance would have allowed me to handle things more cleanly, though. As it was, I ended up having to jump through a few hoops to feed diagnostic data from the new class back to the individual test classes - it was solvable using an interface, but inheritance would have been cleaner.

Thoughts? How do other people handle situations like this?
Jeanne Boyarsky
internet detective
Marshal

Joined: May 26, 2003
Posts: 30068
    
149

Originally posted by Warren Dew:
whether static functions are easier to reuse

No!!! They are impossible to mock, extend, etc. This makes them harder, if not impossible, to test in addition to being harder to reuse. If the static function does something simple (like format a number), it is a true utility method and should be static. If it does something more involved, it should be an instance method, ideally with an interface. Or often, it should be a class in its own right.

In production code, I do the following:


However in a test case itself, a static method isn't as bad. I would still favor avoiding the static method so the functionality it provides can be reused across the real code as well. If you instantiate the FileSystemHelper in a test superclass, it isn't any more work when writing the test.


[Blog] [JavaRanch FAQ] [How To Ask Questions The Smart Way] [Book Promos]
Blogging on Certs: SCEA Part 1, Part 2 & 3, Core Spring 3, OCAJP, OCPJP beta, TOGAF part 1 and part 2
Don Kiddick
Ranch Hand

Joined: Dec 12, 2002
Posts: 580
I agree, can't use just composition to reuse the behaviour instead of inheritance ?
Vladas Razas
Ranch Hand

Joined: Dec 02, 2003
Posts: 385
I think this particular case is a good candidate for Strategy pattern use.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Warren Dew:
Java's single inheritance limitation would prevent tests from using both the database code and the file system code. Such tests did exist.


Sounds like a design smell to me. I'd try to refactor so that I could test the low level database and file system logic separated from the more high level logic you are supposedly testing in the tests mentioned above.

Strategy pattern sounds like a good starting point to think about improvements.
[ February 06, 2005: Message edited by: Ilja Preuss ]

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
Warren Dew
blacksmith
Ranch Hand

Joined: Mar 04, 2004
Posts: 1332
    
    2
Ilja Preuss:

Sounds like a design smell to me. I'd try to refactor so that I could test the low level database and file system logic separated from the more high level logic you are supposedly testing in the tests mentioned above.

I'm not sure what you mean.

To clarify, the functions factored out did things like "get a database connection as a test user with appropriate privileges to set up database preconditions and postconditions for a test", or "move a file from the same directory as the test class to the directory where the code being tested expects to find it". These functions were called during test setup and teardown only, and didn't directly test anything.

Can you expand on what you mean in light of that?
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Warren Dew:
Can you expand on what you mean in light of that?[/QB]


Of course!

If you needed to prepare both the filesystem and the database for the same test, I'd take that as an indication that my test tests too much, and probably the class I'm testing has too many responsibilities, too. I'd suspect that what I actually want to test are actually high level rules, that shouldn't care, for example, where data is coming from, but use some other objects for that, which I could mock for these tests.

Of course this is to a huge degree speculation until I learn more about what you was testing...
Mark Lybarger
Ranch Hand

Joined: Dec 19, 2003
Posts: 72
i think there is value in doing end to end black box type of testing like this. tests where you need the database, you need the remote application, you need the filesystem.

i also think it's certainly valid to put the help methods into a helper type of class. maybe you have a TestDatabaseHelper class and a TestFileSystemHelper class that sets up things for your tests.

single inheritance, multiple inheritance. what's the difference. we have to use the tools we have to get the job done
Gerald Davis
Ranch Hand

Joined: May 15, 2002
Posts: 872
The king of func is in da house and has a lot to say about this, I just cant at the moment until tomorrow.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Mark Lybarger:
i think there is value in doing end to end black box type of testing like this. tests where you need the database, you need the remote application, you need the filesystem.


Ah, yes - I somehow assumed that Warren was speaking about unit testing (probably because he mentioned a test case class).

When it comes to system tests, I'd probably use a tool like FitNesse, where every test makes use of an arbitrary number of test helper classes (called Fixtures in FitNesse), anyway.
Warren Dew
blacksmith
Ranch Hand

Joined: Mar 04, 2004
Posts: 1332
    
    2
Ilja Preuss:

Ah, yes - I somehow assumed that Warren was speaking about unit testing (probably because he mentioned a test case class).

I've been meaning to respond but haven't gotten to it ...

I was talking about automated regression testing, such as JUnit is typically used for. However, there may be a difference in philosophy between how I think those tests should be used and how others, possibly including you, use those tests. I'm going to go off on a tangent and discuss that philosophical difference.

If you truly do a pure "unit" test, testing just a module with everything else stubbed out, you would verify that the module acted as you thought it should. Such behavior would not change except when the code in that module changed. There wouldn't be any need to do a complete set of, say, JUnit tests when you checked something in, because all the modules would be tested completely independently and it would be impossible to break any unit tests in modules you weren't directly touching. You would really only have to run a unit test of the module you were creating or modifying.

In my experience, though, the most difficult bugs don't occur within individual modules: they occur in interactions between modules. Pure unit tests won't test for these things because they test the modules independently; they don't catch the interactions. Put another way, verifying that the module acts as the programmer thinks it will doesn't verify that it will actually work properly when integrated with other modules.

To me, the primary value of a system such as JUnit is not for pure unit tests, but for integration tests: tests that test the interactions between modules, without necessarily testing the whole system. The database part and the file system part may work fine independently, but when they talk to each other, subtle differences in the assumptions used by the programmers of each module may cause bugs that make things go wrong somewhere between the file on disk and the database on disk. And once they do work together, apparently innocent changes in one part may break the other part by invalidating some of those subtle assumptions. Unit tests that blindly stick to either file system or disk won't catch these bugs; only integration tests will catch them. That's why I prefer for my "JUnit" tests to be integration tests, checking at a minimum how the module being tested interacts with the modules being called. That means my "unit" test for the code that calls both the file system module and the database module will need to set up both the file system and the database.

It's true that you can defer these issues to system test, using JUnit only to test each unit independently. My feeling is that this defers all of the really difficult bugs to system test, which I consider later in the process than they should really be dealt with; that leads to the old trap of thinking you're done when the code is written, when what has been written really doesn't work at all, and the hard part - fixing the code so it does work - is yet to come. I prefer to see integration tests written and running as early as possible in the process, so that system test can proceed as smoothly as possible.
[ February 26, 2005: Message edited by: Warren Dew ]
Gerald Davis
Ranch Hand

Joined: May 15, 2002
Posts: 872
Originally posted by Jeanne Boyarsky:

No!!! They are impossible to mock, extend, etc. This makes them harder, if not impossible, to test in addition to being harder to reuse.


If you was to refactor a Class with instance methods into something more procedural what do you have? Static methods that can work on data or containers (list, arrays, dictionaries).

Sum up
1.Instance methods can only be used by data in class.
2.Static methods can be used by any data.

IF you find that you cannot help but create instance method because static methods would have too many parameters and complicate code OR procedurally you end up with function with too access to a global variable, THEN those pieces of code cannot be reused. Instance methods that are hard too refactor into static classes are simply hard to reuse any other way, no?

And any thought of reuse by inheritance: bye using multiple subclasses can be achieved by using one class or function for many different contexts.

Jackie = Person (�male�) or Natash= Person (�famale�) instead of Jackie = Male () or Natash = Female ()

I wonder how hard it is to reuse the few instance methods I have in my application. Probably a lot harder then it is to reuse yours, I tell thee!
Gerald Davis
Ranch Hand

Joined: May 15, 2002
Posts: 872
What I talking about I have not read about so might not be totally accurate but it is something that just popped into my head. I also assume that other geeks on the net also think this way; I just need to find them.

In application development, code is split up into two categories

1.Features (the muscle, the horsepower)
The programming language and its reusable librarys, third party librarys.

2.Context (the control)
The context specific code that is needed to create your application. It is like the master that controls the horse.

The static methods or function are symposiums with adding features to the language because I have factored out functionality that has no or very little application specific context; the less application specific context the better it is for reuse.

The instance methods- the methods that are hard to refactor into static- is the context specific information; consequently, it is hard to reuse.

What kind of destination does Object Oriented Technology have between these two categories? In my experience most OOP geeks mix code from both categories together so making feature reuse hard because it is mixed up with context specific data.

When building application I have one of two hats on:
1.Work on the Horsepower code of the application.
2.Work on the Control context specific code that will control this horsepower.
Warren Dew
blacksmith
Ranch Hand

Joined: Mar 04, 2004
Posts: 1332
    
    2
Gerald Davis:

What kind of destination does Object Oriented Technology have between these two categories? In my experience most OOP geeks mix code from both categories together so making feature reuse hard because it is mixed up with context specific data.

While that may be true, in my experience the same is true for the procedural geeks. That it's possible to code badly using either paradigm doesn't make it impossible to code well in either paradigm.

Your reasoning regarding "very little application specific context" makes sense - but what about situations where the application independent modules need to have private data for implementation, as well as code? Java classes like String and those in the collections library are not application specific, yet they work well as objects. In my opinion, that's the best use for object oriented programming - factoring out utility classes like String and ArrayList so they can be easily reused by application specific code.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Gerald Davis:
What kind of destination does Object Oriented Technology have between these two categories? In my experience most OOP geeks mix code from both categories together so making feature reuse hard because it is mixed up with context specific data.


That notion is totally contrary to my experience.

To me, OO is all about additional tools for managing code dependencies. They are perfectly applicable to decouple application specific code from more generic code, so making the generic code *more* reusable, not less.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Gerald Davis:
Sum up
1.Instance methods can only be used by data in class.
2.Static methods can be used by any data.


If a method is tightly couple to some specific data, it should be an instance method on an object containing that data. This doesn't have to have a negative impact on reusability, if you make those objects of small enough granularity.

With static methods, clients need to know *exactly* what data they are working with. With instance methods, they only need to know what they want to do, and send the object containing the data the appropriate message.
Gerald Davis
Ranch Hand

Joined: May 15, 2002
Posts: 872
Strings and container classes; I like to see them as primitive types; in some language they are.

str = �I wan�t to find first occurrence of the word find�
list =[�I�, �want�, �to�, �find�, �first�, �occurence�, �of�, �the� ,�word�,�find�]

Procedurally
nmbr= find(str,� occurence�)

OOP
nmbr = Str.find(�occurence�)
nmbr = list.find(�occurence�)


OOP
As you can see, the Object Oriented example has two different find methods one for string and one for list. Does those find() methods share any common functionality? It�s hard to know because they are in two different places.

Procedurally
The Procedural example uses one method find() to handle both. The chances are that the find() function can share common functionality between String and List.
The more objects and types use this find() function the better. And don�t you start thinking that this find() function would become bloated I would simply use helper functions as needed.

OOP
If you Object Oriented geeks wanted to be flash, I guess that you would create a Find Class Interface then maybe use an Abstract Classes to share any commonality. It makes you think, unlike this Find Class, the String Class just a collection of unrelated functionality example Find(), Split(), UpperCase() Length(). Isn�t the principle of Object Orientation for one object to have only one role?

The sin of unnatural grouping is a crime that many if not most OOP geeks do without knowing. Grouping functionality by action (Find(), Split(), UpperCase() Length()) is better then grouping by Class Type.
Stan James
(instanceof Sidekick)
Ranch Hand

Joined: Jan 29, 2003
Posts: 8791
The sin of unnatural grouping


Well, that sin might be grouping two chunks of code together because they have the same name, eg "Find". It's useful to ask yourself why are two routines in the same code unit? Even in procedural programming there are different types of cohesion.

Functional: A chunk of code does one thing - the best.

Sequential: We do several steps in sequence, perhaps sharing data.

Communicational: Happen to use the same data but are otherwise unrelated.

Temporal: We need to do several things at the same time, eg init().

Logical: Logical cohesion is used when one of several operations is selected by a flag passed into a function. This type of cohesion is weak because you're depending on flow control to decide what a function should do. This is a no-no.

Coincidental aka Total Chaos: This is what happens when code is stuffed into a function because the programmer didn't know where else to put it. Yuck!

I used to have some neat references about this stuff in COBOL, but I found the above bullets in a Perl PHP site: How Do I Write a Good Function

Frankly, Gerald's suggestion sounds like "group functions teogether because they have the same name" leading to logical cohesion. Gerald wisely suggested helper functions to avoid putting all the code in one place, but the small functions will be at the same granularity as the object methods he doesn't like.

Lest you think "Communicational" sounds like objects come out bad on this scale, read her recommendation ... get the data once and make separate functions to do the "otherwise unrelated" functions. Exactly what OO does but as a defining difference from procedural langauges OO hides the data.
[ February 27, 2005: Message edited by: Stan James ]

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
Gerald Davis
Ranch Hand

Joined: May 15, 2002
Posts: 872
Wahhh Stan James your a deep brother, I need some time to take this all in. I will also go to your link.
Stan James
(instanceof Sidekick)
Ranch Hand

Joined: Jan 29, 2003
Posts: 8791
Just googling on cohesion I got mostly pre-OO papers. We have to leap into other terms to find much OO literature.

An interesting aspect of cohesion is the Single Responsibility Principle. Cooler than just saying a module should do one thing well, this says a class should only have one reason to change. This aims at the heart of maintainability. Given change x in the requirements, how many things have to change.
[ February 27, 2005: Message edited by: Stan James ]
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Gerald Davis:
OOP
nmbr = Str.find(�occurence�)
nmbr = list.find(�occurence�)


Possibly, though there are a whole bunch of different places to put the find code in an OO design, depending on what one things is likely to change or needs to be flexible.


OOP
As you can see, the Object Oriented example has two different find methods one for string and one for list. Does those find() methods share any common functionality? It�s hard to know because they are in two different places.


And as a client of those classes, I actually don't care. Why should I?


Procedurally
The Procedural example uses one method find() to handle both. The chances are that the find() function can share common functionality between String and List.


The OO methods can do that, too, of course, they'd just do it differently (probably in a more decoupled way).


The more objects and types use this find() function the better.


Why? As far as I can tell, it would just couples those types to the find function - and the more you do that, the more monolitic the system becomes. *Unless* you apply some techniques to abstract away from the concrete types inside the function - for example by using polymorphic method calls on them.

OOP
If you Object Oriented geeks wanted to be flash, I guess that you would create a Find Class Interface then maybe use an Abstract Classes to share any commonality.


I'd more likely use delegation than inheritance for this.

The sin of unnatural grouping is a crime that many if not most OOP geeks do without knowing. Grouping functionality by action (Find(), Split(), UpperCase() Length()) is better then grouping by Class Type.


The former might be true, in which case I'd say that they need to be educated. I don't buy the latter, though. It certainly depends on how you design your classes, doesn't it?
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Gerald Davis:
Grouping functionality by action (Find(), Split(), UpperCase() Length()) is better then grouping by Class Type.


That's a false dichotomy: in OOD/P you regularly have classes that *are* actions (see Command pattern, for example).
Gerald Davis
Ranch Hand

Joined: May 15, 2002
Posts: 872
I wanna go home
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Gerald Davis:
I wanna go home


I hope this doesn't mean we discouraged you from continuing this discussion. I find it to be very interesting, as your provocative statements force me to specify my thoughts on OO.

I just wish you could come up with some more elaborate examples to play with. On the other hand I sympathize with you if you "really would like to, but just yet don't find the time to do so" or something...
Gerald Davis
Ranch Hand

Joined: May 15, 2002
Posts: 872
I want to go home, ment simply that, its was cold -4c and I have miles to travel without any wheels. I just typed it in for you to acknolege that I have seen your reply, but cannot be bothered to reply or think about it.

I am still bussy but Stan James link helped me a lot and helped me to think about the long forgotten words Coupling And Cohesion. I am know doing a google search on them; the results are interesting.

http://c2.com/cgi/wiki?CouplingAndCohesion

I like this link it, almost acknoleges what I know mayself, if it wasn't for this link I would eventualy have created my own website on the subject. It happens again and again whenever I come up with good idea someone else has already thought of it before me

[ February 28, 2005: Message edited by: Gerald Davis ]

[ February 28, 2005: Message edited by: Gerald Davis ]
[ February 28, 2005: Message edited by: Gerald Davis ]
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
"Great minds think alike."
Stan James
(instanceof Sidekick)
Ranch Hand

Joined: Jan 29, 2003
Posts: 8791
Wow, that Wiki page has just about every possible viewpoint in the universe. Which ones did you like?
 
 
subject: Multiple inheritance, static functions, and code reuse
 
Similar Threads
Longer Post - ALL IBM ICE EXAM Q's for UML
is it advisable to use super.execute()?
[LONG POST-1] Pre-assessment test
Method overloading
Answers to IBM 486 sample test