• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Tim Cooke
  • Campbell Ritchie
  • paul wheaton
  • Jeanne Boyarsky
  • Ron McLeod
Sheriffs:
  • Paul Clapham
  • Devaka Cooray
Saloon Keepers:
  • Tim Holloway
  • Carey Brown
  • Piet Souris
Bartenders:

Large Java Class

 
Ranch Hand
Posts: 750
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi

I was wondering if performance is better when you have several smaller files as oppose to one large file.

Basically my chess engine Engine.java, is currently a huge a file, with many methods and instance variables.

My mate said it was better to split it so that the entire program isn't stored in RAM, when only parts of it are needed at certain times.

Is this true? It would however help in coding to have it packaged nicely instead of having to scroll up and down the same class to find something.

Also, since I am calling many methods per second, would it make any performance difference to say call a method from the same class, eg ABC(), than to call it from an object, eg. someObject.ABC() ?

Thanks for any advice.
 
Sheriff
Posts: 7001
6
Eclipse IDE Python C++ Debian Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
colin shuker:
I was wondering if performance is better when you have several smaller files as oppose to one large file.

Not really. Each class is only loaded once into memory, regardless of how often it is used.

Basically my chess engine Engine.java, is currently a huge a file, with many methods and instance variables.

How huge is "huge"? There is a limit of 64K bytes for a compiled java class, so presumably your Engine class is smaller than that.

My mate said it was better to split it so that the entire program isn't stored in RAM, when only parts of it are needed at certain times.

What sort of platform are you intending to run your application on? Unless you are *very* short of memory (for example on a very old phone, a smart card or a chewing-gum-sized embedded computer) then this will make no practical difference at all. An application which currently fits into a single class is so small that it will always all be loaded into memory on any general purpose computer. Remember that lots of gif files are bigger than 64k

It would however help in coding to have it packaged nicely instead of having to scroll up and down the same class to find something.

To me, this is the overwhelming reason for splitting your class, so that it makes sense to work with it. I'm also surprised that you haven't needed any other objects yet.

Also, since I am calling many methods per second, would it make any performance difference to say call a method from the same class, eg ABC(), than to call it from an object, eg. someObject.ABC() ?

This also makes no practical difference. Remember that calling a method on the same object is just a shortcut way of saying "call a method on this".
[ February 14, 2007: Message edited by: Frank Carver ]
 
colin shuker
Ranch Hand
Posts: 750
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The Engine.java file is currently 47K, and Engine.class file is 29K.

Well I guess were interested in the .class file, since this is what the processor uses, so I'm a little under half of 64K.

So is that safe then, as it is?
 
Frank Carver
Sheriff
Posts: 7001
6
Eclipse IDE Python C++ Debian Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Completely.

I routinely work on applications with around 500k of my own class files and maybe another 8000k of third-party jar files. Anything in two digits of Kb is very small in comparison.
 
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
A single class with 47k of source code is quite big, though. I like my classes to be around a handful of methods big, each method around a handful of lines of code. Not because of performance reasons, but for maintenance reasons.
 
author
Posts: 9050
21
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
My project is right at the "alpha" stage. Just a little bit more proof of concept and it will be time to do some refactoring. I've been having debates with my local OO guru about the size of classes. From a pure OO perspective, it seems like class size shouldn't be a concern, and one should worry only about concepts like cohesion and coupling. However, from a maintenance perspective I find that it's often easier to deal with a few smaller classes that work together than with one humongous class. If for no other reason than that I can have each class in its own window and see lots of stuff at once.

So, what do all you OO gurus do about this dilemma?
 
(instanceof Sidekick)
Posts: 8791
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I like to strive for the "a class should only have one reason to change" rule. Don't always get there. I find I have a pretty high tolerance for lots of little classes, even just a line or two of executable code for visitors, event handlers and strategies.

I remember when COBOL got the syntax to allow nested programs and made a lot of standard template stuff optional. Suddenly I was writing 10 line programs instead of 20,000. Caused some culture shock. I was quite hurt when somebody declared some of my finest (that is, the latest) work unmaintainable. Guess it depends on what you're used to.
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Bert Bates:
I've been having debates with my local OO guru about the size of classes.



What did the "guru" argue for?

From a pure OO perspective, it seems like class size shouldn't be a concern, and one should worry only about concepts like cohesion and coupling.



Well, but the bigger a class, the less likely it is cohesive, isn't it?

And actually, what does "pure OO perspective" mean, and why should we care?

However, from a maintenance perspective I find that it's often easier to deal with a few smaller classes that work together than with one humongous class. If for no other reason than that I can have each class in its own window and see lots of stuff at once.



I think the only reason to apply OO techniques is to improve maintainability and extensibility - mostly by using the tools to manage dependencies. Many small classes are just a natural consequence, in my opinion.

So, what do all you OO gurus do about this dilemma?



Don't know about the gurus, but personally I'd say that there is no dilemma.
 
Bert Bates
author
Posts: 9050
21
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You're being humble Ilja - you're totally an OO guru

It seems like the "potential" dilemma is that when I make a few smaller classes that are related to each other, and dependent on each other, some variation of high coupling occurs. Even if these related classes are well encapsulated, they end up needing each other to work correctly. In the end I agree that the bottom line is extensibility and maintainability.
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Bert Bates:
It seems like the "potential" dilemma is that when I make a few smaller classes that are related to each other, and dependent on each other, some variation of high coupling occurs.[/QB]



Well, yes. But the code in those classes would be even more coupled if you put it into the same class, wouldn't it? Putting it into different classes allows you to apply techiques like polymorphism to decouple them from each other at least to some amount.

Perhaps it would be helpful if we could discuss a concrete example?
 
Ranch Hand
Posts: 131
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Ideally, an instantiable class should just contain some public fields, and nothing else (subjective). However, to achieve dynamic dispatch in Java, without crazy typecasting, methods have to either be part of the class, or use the visitor pattern, so the minimum you need in terms of these methods is an 'accept' implementation, if you need dynamic dispatch. If you don't need dynamic dispatch, you don't need any instance methods.

The first step in making the class easier to manage is to make its methods static. Obviously some methods will be easier to do that to, such as the ones that don't use any fields.

The rest can also be made static, though doing this is subjective. Where the method needs to do different things depending on the type of the object, it can still do so, by using the visitor pattern.

Here's me refactoring something very simple into something even simpler:

http://docs.google.com/Doc?id=dx5mfkq_62cj4p5j
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Ricky Clarkson:
Ideally, an instantiable class should just contain some public fields, and nothing else (subjective).



I *so* totally disagree. In fact I'd argue that you don't langer have a class (even if you use the class keyword), you just have a struct.
 
Ricky Clarkson
Ranch Hand
Posts: 131
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Having a struct is fine. Take a look at how Common Lisp deals with methods. The class consists of slots, which are analogous to fields. These can be accessed from everywhere, although accessor functions are commonly used instead of direct slot access.

Methods are declared independently of the class, and the method is chosen at runtime based on the types of the arguments passed to it (not just the first - it's multiple dynamic dispatch).

If you have a method with one implementation; i.e., it doesn't differ per class, then you may as well make it a function (functions are chosen by name, not name+types). The same applies for Java, if you replace the term 'function' with 'static method'.

Maybe you'll still disagree, based on Common Lisp being less OO, or not OO. If so, take it up with Alan Kay.
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Ricky Clarkson:
Having a struct is fine.



Sometimes. I don't see how it's generally ideal, though.

Take a look at how Common Lisp deals with methods. The class consists of slots, which are analogous to fields. These can be accessed from everywhere, although accessor functions are commonly used instead of direct slot access.



In a well structured OO program, both direct field access and field access through getters should be rather rare outside of the declaring class. Defining classes in terms of behaviour instead of data leads to much better decoupling, in general.

Methods are declared independently of the class, and the method is chosen at runtime based on the types of the arguments passed to it (not just the first - it's multiple dynamic dispatch).



Which is not at all like static methods in Java. Static methods are *less* polymorphic than Java instance methods. Lisp methods are *more* polymorphic than Java instance methods.

I'm not very concerned about where the methods are defined. Not making use of polymophism defeats the whole purpose of using an OO language, though.

If you have a method with one implementation; i.e., it doesn't differ per class, then you may as well make it a function (functions are chosen by name, not name+types).



I assume changing a function to a method is easy in Lisp?

What is the advantage of making it a function instead of a method in Lisp?

The same applies for Java, if you replace the term 'function' with 'static method'.



With the difference that changing a static method to an instance method is probably a little bit more work in Java.

And while it's certainly possible to do (assuming that you have access to the source code), I don't see any advantage in starting with static methods. What am I missing?


Maybe you'll still disagree, based on Common Lisp being less OO, or not OO.



From the little I know, I'd probably say that the Common Lisp implementation of OO is quite nice. I'd like to try it on a project at some time.

I don't think that the idioms translate to Java in the way you seem to imply, though.

If so, take it up with Alan Kay.



I only know him from my adventures in Smalltalk. Care to elaborate what he has to do with Lisp or static methods in Java?
 
Ricky Clarkson
Ranch Hand
Posts: 131
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Many parts of programming are generally considered to be subjective. When to make a method static is one of them, hence my saying that this is subjective in the original post.

If you make some methods static, and others not, what is your rule for deciding which should be which? If you never use static, then you're probably going the route of the pure 'program-to-interfaces' programmer, and you probably use IoC everywhere (there are other ways). I like to use the example of Math.abs(double) here. Math.abs looks like a method that only ever needs to be defined once - you never need to change which implementation you use.

So most people do not wrap up Math.abs and get access to it from their IoC framework, i.e., they do not do 'context.getMathImplementation().abs(x)'.

Of course, this is flawed on my part - StrictMath.abs exists, but it serves to demonstrate a point - you don't need polymorphism everywhere. Polymorphism, in the cases where you don't need it, because you only have one implementation, is merely an extra form of indirection.

So, going back to the rule part - if you sometimes use static and sometimes not, what is the rule? Here's a suggested typical rule; If a method belongs in an object, you put it in the class as an instance method. How do you decide whether it belongs? Case by case.

I'm never happy if I have to decide things case by case. I want to abstract above that so that I decide once, and then apply it everywhere. It's part of the programmer's desire to automate; he even wants to automate himself. Of course, I may make the wrong decision, but that will be obvious when a case pops up that doesn't fit, then I'll consider the reasoning further.

Another way of deciding whether it should be static is to apply the rule "Can it possibly be implemented as a static method?". Disregarding methods that need polymorphism, the answer is 'yes'. This halfway point of making methods that don't need polymorphism static is valuable, but I'd like to go further.

Even a method that needs polymorphism can be implemented as a static method, thanks to the visitor pattern. So "Can it possibly be implemented as a static method?" is not a very good rule, although it serves us well in thinking about this.

You say that "defining classes in terms of behaviour instead of data leads to much better decoupling, in general.". That depends on the classes. If the classes actually depend on data, then making the data private and only exposing the behaviour makes it look like there is decoupling, but there is not. There is a hard coupling between the class and its user, unless there is more than one useful implementation of the class. If you seek to hide that coupling, you are obscuring the code in the same way that a virtually no-op setter method does; it's better to just expose the data.

You said: "Which is not at all like static methods in Java." I was associating Common Lisp methods with instance methods, not with static methods. Common Lisp functions are more like static methods.

You said: "Not making use of polymophism defeats the whole purpose of using an OO language". Not really. It's important to note that there is no OO language that does not depend on other paradigms. Lisp, Smalltalk and Ruby all depend on the procedural paradigm. Haskell depends on the functional paradigm. OO itself is not sufficient to make programs work. There is no OO equivalent to an if statement.

So some code will depend on polymorphism, but not all code will. That's OK, not everything in your application will map to OOP. It's better to make it clear which parts do not use polymorphism, and which parts do.

You said: "I assume changing a function to a method is easy in Lisp?"

Yes. Change (defun...) to (defmethod...) and I think that's all you need, though you can define types that the method applies to. I haven't written enough Lisp yet. You don't have to change use sites.

You said: "What is the advantage of making it a function instead of a method in Lisp?"

I don't know. The interesting thing is that there's very little difference between a function and a method. There's no difference in how you invoke it, which is quite different to Java, unless you follow the discouraged practice of calling static methods on instance variables (e.g., Toolkit t; t.getDefaultToolkit().whatever() .

A possible advantage, depending on the implementation of Lisp, would be that a function would be bound earlier, gaining a performance advantage analogous to the difference between a function and a virtual function in C++.

You said: "With the difference that changing a static method to an instance method is probably a little bit more work in Java."

I don't have an IDE open right now, but I expect IDEA supports refactoring a method from static to instance. It certainly supports the opposite, which is the way I'm suggesting to go.

You said: "I don't see any advantage in starting with static methods."

Ignoring access modifiers, which should all be public anyway, a static method is trivial to move to another class, i.e., a utility class. An instance method is not. When you implement an interface, if it has declared methods that could have been implemented as static methods, then you have to implement those. Of course, a solution is to inherit from an abstract base class, but that has its own problems.

About Alan Kay, he once said: "OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I'm not aware of them." I wonder whether he'd add some languages now.

Mentioning Alan Kay was simply a safeguard against my comment being disregarded based on Lisp not being OO (it is OO). You didn't take that route, anyway.
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Ricky Clarkson:
Many parts of programming are generally considered to be subjective.



True. Brace formatting comes to mind.

When to make a method static is one of them



Not as subjective as brace formatting, as it has some very objective consequences on the dependencies in the system, and hence its maintainability and extensibility.


If you make some methods static, and others not, what is your rule for deciding which should be which?



Good question, thanks for asking!

I don't have a hard and fast answer. Non-static is definitely the default for me, though. Mostly I make them static when I need to use them from several places and don't yet care to put them in a class where they make sense as an instance method. Or when I can't change the class they would actually belong to. Or when the method is concerned with the creation of instance of a class, for example.

If you never use static, then you're probably going the route of the pure 'program-to-interfaces' programmer, and you probably use IoC everywhere (there are other ways).



Not at all.

Programming to an interface is often the right thing to do, but purity is not a goal of mine. I do it to manage the dependencies in my source code, not to earn a medal for the purest code ever written.

And I typically find IoC containers to be too much meta-programming for my taste, although I regularly use other forms of dependency injection where it is appropriate.


I like to use the example of Math.abs(double) here. Math.abs looks like a method that only ever needs to be defined once - you never need to change which implementation you use.



If Java didn't have primitives, the right place for such a method would be on the base class for numbers. That's how it's done in Smalltalk, as you probably know. (Smalltalk has another advantage over Java, being that class level methods are polymorphic, too. Smalltalk doesn't have what you call "functions" at all.)

So most people do not wrap up Math.abs and get access to it from their IoC framework, i.e., they do not do 'context.getMathImplementation().abs(x)'.



The Math class is an example of a workaround around the fact that Java isn't as OO as it could be. It is not an example of what an ideal OO design should look like - not at all.

Of course, this is flawed on my part - StrictMath.abs exists, but it serves to demonstrate a point - you don't need polymorphism everywhere.



True. But we can't know where we will need polymorphism tomorrow. Shortening our ability to make use of polymorphism later at places we don't need it now will only reduce the flexibility of the system with no conceivable benefit. Or at least you didn't yet explain what benefit you see with using static methods. Perhpaps it comes later in this post...

Polymorphism, in the cases where you don't need it, because you only have one implementation, is merely an extra form of indirection.



An indirection that typically doesn't hurt anyone, or so it seems to me.

So, going back to the rule part - if you sometimes use static and sometimes not, what is the rule? Here's a suggested typical rule; If a method belongs in an object, you put it in the class as an instance method. How do you decide whether it belongs? Case by case.



Here is a proposed alternative rule: Only make it static when not making it static hurts you in any way.

I'm never happy if I have to decide things case by case. I want to abstract above that so that I decide once, and then apply it everywhere.



A noble ideal. In this case, I doubt that it will lead to ideal solutions.

It's part of the programmer's desire to automate; he even wants to automate himself. Of course, I may make the wrong decision, but that will be obvious when a case pops up that doesn't fit, then I'll consider the reasoning further.



Ah, so you have a default, and when that starts to hurt you, you decide case by case? We are not that different, then - only that my default is the exact opposite of yours.

Another way of deciding whether it should be static is to apply the rule "Can it possibly be implemented as a static method?". Disregarding methods that need polymorphism, the answer is 'yes'. This halfway point of making methods that don't need polymorphism static is valuable, but I'd like to go further.

Even a method that needs polymorphism can be implemented as a static method, thanks to the visitor pattern. So "Can it possibly be implemented as a static method?" is not a very good rule, although it serves us well in thinking about this.



I'd suggest that that is not the only reason it's not a good rule. Anyway, let's read on...


You say that "defining classes in terms of behaviour instead of data leads to much better decoupling, in general.". That depends on the classes. If the classes actually depend on data, then making the data private and only exposing the behaviour makes it look like there is decoupling, but there is not. There is a hard coupling between the class and its user, unless there is more than one useful implementation of the class.



Huh? So introducing a second useful implementation suddenly decouples the existing classes? That doesn't make sense to me at all.


If you seek to hide that coupling, you are obscuring the code in the same way that a virtually no-op setter method does; it's better to just expose the data.



I don't seek to hide coupling. Not sure what you are talking about here - do you have a more concrete example at hand?


You said: "Which is not at all like static methods in Java." I was associating Common Lisp methods with instance methods, not with static methods. Common Lisp functions are more like static methods.



OK.


You said: "Not making use of polymophism defeats the whole purpose of using an OO language". Not really. It's important to note that there is no OO language that does not depend on other paradigms. Lisp, Smalltalk and Ruby all depend on the procedural paradigm. Haskell depends on the functional paradigm. OO itself is not sufficient to make programs work.



I don't see a contradiction here. If we don't use OO features, we don't an OO language, do we? I'm arguing that polymorphism is the most important - in fact the defining - feature of an OO language.

There is no OO equivalent to an if statement.



Oh, there is. In Smalltalk, ifTrue: is a polymorphic method on the Boolean class. Quite elegant, if you ask me.

So some code will depend on polymorphism, but not all code will. That's OK, not everything in your application will map to OOP.



OK.

It's better to make it clear which parts do not use polymorphism, and which parts do.



Why?


You said: "I assume changing a function to a method is easy in Lisp?"

Yes. Change (defun...) to (defmethod...) and I think that's all you need, though you can define types that the method applies to. I haven't written enough Lisp yet. You don't have to change use sites.



Than I'd argue that that's a big difference between Lisp and Java. If in Java you change a static method to an instance method on one of its arguments, you will have to change all of its clients.


You said: "What is the advantage of making it a function instead of a method in Lisp?"

I don't know. The interesting thing is that there's very little difference between a function and a method. There's no difference in how you invoke it, which is quite different to Java, unless you follow the discouraged practice of calling static methods on instance variables (e.g., Toolkit t; t.getDefaultToolkit().whatever() .



I see. Seems to me that this idiom doesn't translate very well to Java.


A possible advantage, depending on the implementation of Lisp, would be that a function would be bound earlier, gaining a performance advantage analogous to the difference between a function and a virtual function in C++.



I can imagine. Of course that advantage is typically very small in Java, as the Hot Spot Engine can perform the same optimizations on instance methods that aren't used polymorphically. And if I'd really care, I'd rather help it by using the "final" keyword than making it static.


You said: "With the difference that changing a static method to an instance method is probably a little bit more work in Java."

I don't have an IDE open right now, but I expect IDEA supports refactoring a method from static to instance. It certainly supports the opposite, which is the way I'm suggesting to go.



When you suddenly need a method to be polymorphic, you will need to change it from static to instance. Even with a refactoring browser at hand that's a risk and unnecessary work - as I still don't see any significant advantage in making a method static that could also be non-static.


You said: "I don't see any advantage in starting with static methods."

Ignoring access modifiers, which should all be public anyway, a static method is trivial to move to another class, i.e., a utility class.



Interestingly, with modern IDEs like Eclipse (and I assume IDEA, too), it's also quite easy to move an instance method to another class, if we care to.

An instance method is not. When you implement an interface, if it has declared methods that could have been implemented as static methods, then you have to implement those.



I don't know how interfaces entered the discussion. We can use instance methods without declaring them in interfaces, can't we?



About Alan Kay, he once said: "OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I'm not aware of them." I wonder whether he'd add some languages now.



Perhaps Ruby. And I totally agree with him. I don't see anything that suggests using static methods, though - quite to the contrary.


Mentioning Alan Kay was simply a safeguard against my comment being disregarded based on Lisp not being OO (it is OO). You didn't take that route, anyway.



I see. I'm actually quite a fan of Smalltalk and Lisp - if just someone would pay me to program in those languages...
 
Frank Carver
Sheriff
Posts: 7001
6
Eclipse IDE Python C++ Debian Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
This is certainly an interesting discussion. There are so many points on the table at the moment that I can't really address them all one-by-one, but I would like to try and clarify some terminology.

It seems that there ere really four approaches to programming being considered here:

  • A bunch of data and methods all in a single class. This is the case which prompted the thread. In such a system each "thing", be it code or data, exists only once, and a single thread of code passes through in sequence. In this sitaution it really doesn't matter if things are marked coderanch, protected, private, or static. They are all accessible by all the code at all times. In effect this is traditional "procedural" programming, typified by languages such as Fortran, COBOL and C
  • Methods and data marked as static, in several classes. This seems to be the overall direction described by Ricky Clarkson. This helps with separating concerns, and allows the use of private and protected modifiers for encapsulation if required. It does not support polymorphism, because each class is still equivalent to a single object, and the "reciever" class is hard-coded in all method calls. This sounds very similar to "modular" programming, a technique introduced to help manage the growing complexity of procedural code, and typified by laguages such as Pascal and Ada.
  • Methods and data not marked as static. This seems to be the overall direction described by Ilja Preuss. Data are tied to particular object instances, and methods are polymorphically dispatched to the correct class implementation when invoked. Each class may be instantiated zero or more times, and may be subclassed to define more specific behaviour. This is generally known as "Object Oriented" programming and typified by languages such as Smalltalk.
  • Methods static, but no data persists between methods. This has implicitly cropped up a few times in the discussion, and is very interesting academically but difficult to achieve with real systems which require data to remain available for future requests. This is usually known as "functional" programming and typified by languages such as Lisp, APL and XSLT.


  • Most languages in widespread use (such Java, C++, Ruby, etc.) offer a mixture of features from the above catagries. Java can be used in all of these ways.

    My personal experience is that I very rarely find myself writing static methods, and only create static data items for compile-time constants.

    To a large degree this may because I write the great majority of code using Test-Driven Development (TDD) with the JUnit test framework. TDD with JUnit relies heavily on two things: instantiating a different object of the same class for each different test to ensure that there is no "crosstalk" between tests, and the ability to substitute any part of the system with a "test stub" to ensure that precisely the right code and values are being tested.

    Procedural and modular approaches make this kind of testing (and thus this approach to developing software) very difficult. In both of those approaches there is only ever one of anything. While this would be acceptable for purely functional programming, I find it extremely rare that I can solve real problems with a purely functional solution. This may of course be a problem with me rather than with functional programming per se.
     
    Ilja Preuss
    author
    Posts: 14112
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Frank, nice summary!

    Your last paragraph was a little bit confusing to me - did you want to say that functional programs are hard to unit test, too?
     
    Frank Carver
    Sheriff
    Posts: 7001
    6
    Eclipse IDE Python C++ Debian Java Linux
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Sorry Ilja. Rereading my comments they do seem a bit unclear.

    What I meant was that functional programs are easier to test, without needing a framework such as JUnit, because functions have no side-effects, and thus the framework does not need to instantiate separate objects. Programs in functional languages habitually make use of a kind of "dependency injection" but inject functions rather than objects.

    However, real-world problems are not usually amenable to purely functional solutions, so the point is moot.
     
    Ricky Clarkson
    Ranch Hand
    Posts: 131
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    "Not as subjective as brace formatting, as it [whether you use static or not] has some very objective consequences on the dependencies in the system, and hence its maintainability and extensibility."

    Interestingly, that's completely untrue. A static method is equivalent to a non-static method. Ruby's and Common Lisp's object models are both correct. With the possible exception of a visitor implementation, any method can be automatically refactored from static to non-static without any repercussions, as long as the static form has a non-null parameter that can be made the 'this'. I'm not sure if that makes sense, I can explain it better if needs be.

    "Mostly I make them static when I need to use them from several places and don't yet care to put them in a class where they make sense as an instance method."

    That just moves the question a little. How do you decide whether a method makes sense as an instance method?

    "Or when I can't change the class they would actually belong to."

    Unfortunately, that's quite often, and is why we still need casts when using some APIs. For example, in DOM you need to cast a Node to Element, though you can tell in advance whether the cast will fail. If the DOM code had a visitor implementation, this would be unnecessary - but the writers probably didn't envisage casts being a problem.

    API designers who want to provide everything as instance methods will tend not to implement the visitor pattern - after all, who will possibly want to write a visitor when they've already provided everything you could possibly want? If they provided everything as static methods, i.e., if they wrote code as if they were the client, they would already have written a visitor implementation (unless they like casting).

    "Not at all."

    So do you sometimes use static? If not, it wasn't addressed at you.

    "If Java didn't have primitives, the right place for such a method would be on the base class for numbers. That's how it's done in Smalltalk, as you probably know."

    Sure, but it is possible that sometimes you want code to use Math.abs and sometimes StrictMath.abs, so having it on the base class for numbers doesn't always make sense. I use Math.abs as an example because you practically never use anything else - there's no advantage to the indirection.

    "The Math class is an example of a workaround around the fact that Java isn't as OO as it could be."

    I would say that if you got rid of Java's primitives, moved abs to Double, Integer, etc., you wouldn't actually make abs more OO. You'd make it appear more OO. It's still one implementation. You can late-bind it, but there's one implementation, so there's no advantage in doing so. As soon as you have more than one useful implementation (and decent refactoring tools) it's worth the late binding.

    "But we can't know where we will need polymorphism tomorrow."

    True, but in the Java world we can refactor accurately. So when I need polymorphism, I can add it.

    "An indirection that typically doesn't hurt anyone, or so it seems to me."

    Any indirection that gives no benefit harms clarity.

    "Here is a proposed alternative rule: Only make it static when not making it static hurts you in any way."

    What is the logic behind that rule?

    "Ah, so you have a default, and when that starts to hurt you, you decide case by case?"

    No, I have a rule, and when it starts to hurt me, I reject it and look for a better rule. Certainly inbetween rules I may decide case by case.

    "So introducing a second useful implementation suddenly decouples the existing classes?"

    No, but it gives us a reason to decouple. If we only need one implementation the decoupling is not worth it. If we need more than one implementation the decoupling is necessary. Decoupling when it's not necessary is another form of indirection without benefit.

    "I don't seek to hide coupling. Not sure what you are talking about here - do you have a more concrete example at hand?"

    public final class Point{private int x,y; public Point(int x,int y){this.x=x;this.y=y} public int getX(){return x;} public int getY(){return y;} public void setX(int x){this.x=x;} public void setY(int y){this.y=y;}}

    The setters and getters are fairly low level implementations of hiding coupling, as from an API point of view we cannot see that the data is being stored directly as fields. There's no benefit in hiding that. There's no benefit here in making the fields private and using accessors, except when the client code is not under our control.

    "If we don't use OO features, we don't an OO language, do we? I'm arguing that polymorphism is the most important - in fact the defining - feature of an OO language."

    Fine, but not everything written in an OO language needs polymorphism.

    "In Smalltalk, ifTrue: is a polymorphic method on the Boolean class."

    So imagining that Smalltalk had Java syntax for a moment, you'd have:

    (3<5).ifTrue()
    {
    System.out.println("Phew, the < operator works");
    }

    If true and false are different implementations of Boolean, I can see how that would be elegant. In Java terms, we'd need closures first too.

    Changing the goalposts a little, apologetically, there is no OO equivalent of a loop (continuations may disprove that, as may recursion, I don't know - though neither are intrinsically OO).

    I said: "It's better to make it clear which parts do not use polymorphism, and which parts do."

    You said: "Why?"

    The general aim of making it clear how things work. Many classes exist that are overloaded with functionality, because it seems convenient to make the method an instance method. This harms simplicity. A static method is simpler than a non-static method, because there is only ever one implementation of it.

    A lot of the convenience of instance methods is syntactic - object.method() seems a lot nicer than SomeUtilityClass.method(object). There are two factors here - one is that Java requires that static methods be inside classes, making classes double as blueprints for creating objects, and bags of static methods. The second is that Java doesn't support mixins. Scala does, in a compile-time only way, and it seems to make sense. Effectively, you can add everything from SomeUtilityClass to your view of 'object', so you can call object.method() but it gets compiled as SomeUtilityClass.method(object).

    I'm liable to have erred in my description of Scala's mixins, having never written a line of Scala.

    "If in Java you change a static method to an instance method on one of its arguments, you will have to change all of its clients."

    Or have your IDE do it for you.

    "Even with a refactoring browser at hand that's a risk and unnecessary work - as I still don't see any significant advantage in making a method static that could also be non-static."

    It's not a risk, as the common refactoring tools simply don't make mistakes in this area.

    The advantage is in simplicity, and in not having to make the case by case decision about whether you want static or non-static.

    Alan Kay said that OOP is possible in Lisp. However, I don't think he's ever said that OOP is applicable to every problem, or every facet of every problem. Where late binding gives no benefits, I don't see why you'd use OOP. If you did use OOP without benefitting from late binding, I'd say that you're disguising non-OOP code as OOP, and I understand that I'm being quite subjective here.
     
    Ricky Clarkson
    Ranch Hand
    Posts: 131
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Frank Carver suggests that my static method approach does not support polymorphism. That is untrue, as long as the visitor pattern exists.

    Further, I never advocated static data. Those, even with a private modifier, are equivalent to global variables. Special variables in Lisp make that workable, in Java it's not worth using.

    [ February 18, 2007: Message edited by: Ricky Clarkson ]

    "However, real-world problems are not usually amenable to purely functional solutions, so the point is moot."

    That depends on the purely functional solutions. Monads in Haskell manage it quite well, as do 'world-passing' functions in Clean.
    [ February 18, 2007: Message edited by: Ricky Clarkson ]
     
    Frank Carver
    Sheriff
    Posts: 7001
    6
    Eclipse IDE Python C++ Debian Java Linux
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Ricky Clarkson wrote I would say that if you got rid of Java's primitives, moved abs to Double, Integer, etc., you wouldn't actually make abs more OO. You'd make it appear more OO. It's still one implementation. You can late-bind it, but there's one implementation, so there's no advantage in doing so. As soon as you have more than one useful implementation (and decent refactoring tools) it's worth the late binding.

    Are you really sure that there wouold only be "one implementation" in such a case? I can imagine that the the implementation of "abs" for an Integer could be simpler than the one for a Double, for example. Other functions currently implemented as static methods on Math might be even more different.

    In particular, making "abs" a single static method on a single class means that it is not scalable to any other number-like classes which might be introduced after it is coded, such as BigInteger and BigDecimal.

    I never advocated static data. Those, even with a private modifier, are equivalent to global variables.

    In which case I apologise for misrepresenting you. It does leave me puzzled as to how you propose to share data between your static methods, if not by using static data.

    That depends on the purely functional solutions. Monads in Haskell manage it quite well, as do 'world-passing' functions in Clean.

    OK, I'm not familiar with either of these languages, so let's take a concrete example.

    Imagine I need a small program with which the user can do three things: login, add an event to their calendar (users are not allowed to add events to other folks calendars), get details of a month of a calandar (either their own, or someone else's) for display or whatever. An API using stored data might look like:



    Note that I am deliberately ignoring details of user interface, output formatting etc.

    Can you give a functional/static example of how this might work without storing any context between calls?
     
    Stan James
    (instanceof Sidekick)
    Posts: 8791
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Where's Gerald when you need him to clear things up?
     
    Ricky Clarkson
    Ranch Hand
    Posts: 131
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Frank said: Are you really sure that there wouold only be "one implementation" in such a case?

    By one implementation, I mean one implementation that's useful given the same input type. You can certainly add types to the discussion to complicate matters. For example, if you put abs on Integer, Double, etc., it'd be a good idea to put abs on Number. Or would it? Suppose that you then want Quaternion to extend Number. I don't recall whether a quaternion has a useful abs definition (though it probably does). A definite bad case would be if you had a type called PositiveNumber. Why should a PositiveNumber need to implement abs?

    This can be simplified by splitting up Number into many interfaces, and allowing individual implementations to choose which they implement, but then it's hard, but not impossible, to write a method that can work with those. You can actually do this:

    public [N extends Abs,Plus[Integer]] N absThenAdd10(N input)
    {
    return input.abs().plus(10);
    }

    if Abs is an interface with one method, abs(), and Plus is an interface with one method, plus().

    Without tool support this gets unwieldy. It's a statically-checked, verbose form of duck typing.

    "In particular, making "abs" a single static method on a single class means that it is not scalable to any other number-like classes which might be introduced after it is coded, such as BigInteger and BigDecimal."

    That is true. It makes abs appropriate only for the types it is coded for. I don't see that as a problem in itself, and I've just outlined some difficulties with making it an instance method. You are free to make your own abs method that delegates to the original for some cases.

    "It does leave me puzzled as to how you propose to share data between your static methods, if not by using static data."

    Er, by putting the data in objects, and passing references to those objects between methods. I think that's fairly standard.

    The code example is fairly trivial to write without relying on static data.

    SomeObject obj=new SomeObject();
    obj.login(username,password);
    obj.addEvent(date, eventDetails);
    obj.getCalendarPage(month, username);

    It is just as feasible to make those methods static.

    SomeObject obj=new SomeObject();
    Utility.login(obj,username,password);
    Utility.addEvent(obj,date, eventDetails);
    Utility.getCalendarPage(obj,month, username);

    "Can you give a functional/static example of how this might work without storing any context between calls?"

    Local variables (like obj above) are not static, so your question is not relevant to the discussion, probably because you didn't understand me earlier.

    But, if you like, I can answer it - new SomeObject().login(username,password).addEvent(date,eventDetails).getCalendarPage(month,username);

    It's the same thing, pretty much.
    [ February 19, 2007: Message edited by: Ricky Clarkson ]
     
    Frank Carver
    Sheriff
    Posts: 7001
    6
    Eclipse IDE Python C++ Debian Java Linux
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    By one implementation, I mean one implementation that's useful given the same input type. You can certainly add types to the discussion to complicate matters. For example, if you put abs on Integer, Double, etc., it'd be a good idea to put abs on Number. Or would it? Suppose that you then want Quaternion to extend Number. I don't recall whether a quaternion has a useful abs definition (though it probably does). A definite bad case would be if you had a type called PositiveNumber. Why should a PositiveNumber need to implement abs?

    I'm not sure what you are getting at here. Surely if PositiveNumber implements the methods of Number, then all it needs to do is provide a very simple implementation of "abs" which just returns itself.

    [B]This can be simplified by splitting up Number into many interfaces, and allowing individual implementations to choose which they implement, but then it's hard, but not impossible, to write a method that can work with those. You can actually do this:


    [/B]

    I'm still baffled. That looks like basic polymorphism to me, using instance methods to dispatch to the appropriate implementation based on reciever type. Is this what you are recommending, or what you are trying to avoid?

    if Abs is an interface with one method, abs(), and Plus is an interface with one method, plus(). Without tool support this gets unwieldy. It's a statically-checked, verbose form of duck typing.

    Sure. But all I am recommending is the use of regular classes with polymorphic instance methods. Nowhere do I recommend one method per interface.

    That is true. It makes abs appropriate only for the types it is coded for. I don't see that as a problem in itself, and I've just outlined some difficulties with making it an instance method.

    As mentioned above, I still don't really see why the polymorphic example above would be difficult. A major point about polymorphism is that methods need not care about the implementation of objects given to them, only that they support a certain set of methods. In particular, they do not need to provide code for every possible type.

    In the standard Java APIs there is no Math.abs(BigInteger). To add such a thing would require changing the API of the Math class. With a polymorphic solution where each kind of number is responsible for its own "abs" implementation this would not be a problem.

    You are free to make your own abs method that delegates to the original for some cases.

    I hope I don't sound like a broken record, but I'm still trying to find out why you feel there is only a single implementation. With static methods you are tied to determining argument type in the static "library method"(s).

    [B][/B]

    That's the kind of OO code which I might write, a class with instance methods.

    It is just as feasible to make those methods static.

    And this, I guess, is where we differ

    [B][/B]

    This is just procedural code with a struct, and would be a typical way of doing it in a decidedly non-OO language such as C.

    I know this is possible, I've done it many times in the past. It seems a strange way to do things when proper OO is also available, though.

    By taking this approach we immediately lose the ability to have multiple Calendar implementations (say one which uses an in-memory store, one which uses a database, and one which exposes other methods for prepopulating dummy data or examining the contents during testing.)

    What I am having trouble understaning is why it would be worth going to all the trouble of marking the methods static and passing in an extra method parameter just to lose this flexibility. Perhaps I'm still missing something obvious, though.

    But, if you like, I can answer it - new SomeObject().login(username,password).addEvent(date,eventDetails).getCalendarPage(month,username);

    OK, that's a bit of a conceit, but I'll grant that it passes the letter of my challenge. I hope you understand that what I intended was an API more like where getCalendarMonth may be called zero or more times with or without logging in, and addEvent may only be called zero or more times, but only after a successful login. Presumably these method calls would be prompted by some sort of user action, so the code can not predetermine the call sequence.

    That's why I chose this as my "real world" example.
     
    Ricky Clarkson
    Ranch Hand
    Posts: 131
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I've edited my previous post, my generics were swallowed.

    "Surely if PositiveNumber implements the methods of Number, then all it needs to do is provide a very simple implementation of "abs" which just returns itself."

    Surely it makes no sense to call abs on PositiveNumber, therefore it makes no sense to implement abs on PositiveNumber.

    "I'm still baffled." - is this true after my edit?

    "But all I am recommending is the use of regular classes with polymorphic instance methods. Nowhere do I recommend one method per interface."

    That's fine except for cases where they don't make sense. For example, List[String] list=Arrays.asList("hello","world"); looks fine until you try to add something to list (exception). It's arguable that asList should return something other than List, perhaps something that implements size() and iterator(), or that it should support mutation.

    "In the standard Java APIs there is no Math.abs(BigInteger)."

    I was sticking to one version of abs, let's say, the one that takes a double. There is only one useful implementation of that, ignoring StrictMath, so there is no harm in it being static. We don't need to be able to select between Math.abs and StrictMath.abs without changing code, typically. If you move abs to the type of its argument, which you can't, because primitive types have no methods, then sure, there are flexibilities you don't get by keeping abs static, but as long as it's on Math, or some other such class, there's no gain in making it an instance method.

    "This is just procedural code with a struct, and would be a typical way of doing it in a decidedly non-OO language such as C."

    It's semantically no different to the first one. It is a typical way of doing it in a decidedly OO-supporting language such as Common Lisp.

    "we immediately lose the ability to have multiple Calendar implementations (say one which uses an in-memory store, one which uses a database, and one which exposes other methods for prepopulating dummy data or examining the contents during testing.)"

    Again, that is false as long as the visitor pattern exists.
     
    Ilja Preuss
    author
    Posts: 14112
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Ricky Clarkson:

    "Surely if PositiveNumber implements the methods of Number, then all it needs to do is provide a very simple implementation of "abs" which just returns itself."

    Surely it makes no sense to call abs on PositiveNumber, therefore it makes no sense to implement abs on PositiveNumber.



    If we want PositiveNumber to be a subtype of Number (which makes sense to me), it has to implement all operations on Number in a meaningful way to conform to Liskov's Substitution Principle.

    That is, I probably want to be able to pass a PositiveNumber to a code fragment that works on an arbitrary number. That code fragment might - without knowing whether it's currently working on a PositiveNumber or not - want to execute the abs operation. That's why it absolutely makes sense to implement abs on a PositiveNumber as "return this;".
     
    Stan James
    (instanceof Sidekick)
    Posts: 8791
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Again, that is false as long as the visitor pattern exists.



    This has appeared in here several times and triggered my interest. Can you describe how you use visitor behind static methods to give polymorphism?
     
    Ranch Hand
    Posts: 94
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Why worry about lines of code and such? Simple answer goes back to early 90's. CC (McCabe's cyclomatic complexity). The more ifs and buts you have in your code the more CC you have. Break down your code as soon as you encounter major control statement (be it class or method). The more CC you have testing and maintenance becomes more difficult.
     
    Ilja Preuss
    author
    Posts: 14112
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Ricky Clarkson:
    "Not as subjective as brace formatting, as it [whether you use static or not] has some very objective consequences on the dependencies in the system, and hence its maintainability and extensibility."

    Interestingly, that's completely untrue. A static method is equivalent to a non-static method. Ruby's and Common Lisp's object models are both correct. With the possible exception of a visitor implementation, any method can be automatically refactored from static to non-static without any repercussions, as long as the static form has a non-null parameter that can be made the 'this'.



    I see that it can be done. Basically what you would be doing in Java is reimplementing polymorphic method dispatch, instead of using the implementation that is coming with the language, wouldn't you?

    There are still consequences on the dependencies in the system, though. A Visitor pattern creates a circular dependency between the classes; and the static method needs to know about all the available implementations of the method.

    Normally in Java, you can just dynamically load a class that was compiled in isolation from the rest of the system, and the system will just happily accept it. That's heavily used, for example for JDBC drivers. I don't see how that would be possible with the static+visitor approach, but perhaps I'm missing something.

    But even if it should be possible, it looks like a lot of additional code complexity to me.

    "Mostly I make them static when I need to use them from several places and don't yet care to put them in a class where they make sense as an instance method."

    That just moves the question a little. How do you decide whether a method makes sense as an instance method?



    I think you misread me. It's always *possible* to create a class where a method would make sense as an instance method. I just sometimes don't feel that it's worth the effort to create a new class just to be able to not make the method static.

    "Or when I can't change the class they would actually belong to."

    Unfortunately, that's quite often



    Which, I think, is a shortcoming of Java. In Smalltalk (or Nice, for that matter), it's always possible to add a method to a class.

    Anyway, if we can't change a class, we can't add a method to it, so we need to work around that deficit. That doesn't make it an ideal design, though.

    For example, in DOM you need to cast a Node to Element, though you can tell in advance whether the cast will fail. If the DOM code had a visitor implementation, this would be unnecessary - but the writers probably didn't envisage casts being a problem.



    I don't see it as a problem in that case, either. After all, we really *know* the type of the object. I don't see what benefit a visitor would bring in that case.

    API designers who want to provide everything as instance methods will tend not to implement the visitor pattern - after all, who will possibly want to write a visitor when they've already provided everything you could possibly want?



    That's simply not true. Making methods non-static as default doesn't imply that everything you could possibly want is already provided. Also, there are other needs that lead to the implementation of the visitor pattern. I use Visitor quite often.


    "Not at all."

    So do you sometimes use static? If not, it wasn't addressed at you.



    Then what motivated you to write it?


    "An indirection that typically doesn't hurt anyone, or so it seems to me."

    Any indirection that gives no benefit harms clarity.



    I've never felt that instance methods reduce clarity. And the benefit is that it becomes less work to extend the system. The system is closer to following the Open Closed Principle.

    "Here is a proposed alternative rule: Only make it static when not making it static hurts you in any way."

    What is the logic behind that rule?



    The logic is that instance methods add flexibility to the system without adding complexity. You will disagree, but that's how I feel.

    "So introducing a second useful implementation suddenly decouples the existing classes?"

    No, but it gives us a reason to decouple. If we only need one implementation the decoupling is not worth it. If we need more than one implementation the decoupling is necessary. Decoupling when it's not necessary is another form of indirection without benefit.



    I wholeheartedly disagree. A well decoupled system is typically

    - easier to understand
    - easier to test
    - easier to maintain and extend

    "I don't seek to hide coupling. Not sure what you are talking about here - do you have a more concrete example at hand?"

    public final class Point{private int x,y; public Point(int x,int y){this.x=x;this.y=y} public int getX(){return x;} public int getY(){return y;} public void setX(int x){this.x=x;} public void setY(int y){this.y=y;}}

    The setters and getters are fairly low level implementations of hiding coupling, as from an API point of view we cannot see that the data is being stored directly as fields. There's no benefit in hiding that. There's no benefit here in making the fields private and using accessors, except when the client code is not under our control.



    I disagree that there is no benefit in hiding that the accessors are directly working on fields - after all, we could later change that (could even use a polar coordinate system inside) without clients needing to change.

    On the other hand, I totally agree that it is a quite weak class, because it doesn't have any true behaviour. We should look out for Feature Envy in its clients and move more behaviour into its instance methods.

    "If we don't use OO features, we don't an OO language, do we? I'm arguing that polymorphism is the most important - in fact the defining - feature of an OO language."

    Fine, but not everything written in an OO language needs polymorphism.



    Agreed. We just seem to disagree on the "costs" of polymorphism.

    "In Smalltalk, ifTrue: is a polymorphic method on the Boolean class."

    So imagining that Smalltalk had Java syntax for a moment, you'd have:

    (3<5).ifTrue()
    {
    System.out.println("Phew, the < operator works");
    }

    If true and false are different implementations of Boolean, I can see how that would be elegant. In Java terms, we'd need closures first too.



    So you agree that there actually *is* an OO equivalent of an if statement?


    Changing the goalposts a little, apologetically, there is no OO equivalent of a loop (continuations may disprove that, as may recursion, I don't know - though neither are intrinsically OO).



    I'm not following you. Are you really first stating something as a fact, an then saying that you are actually not sure that it's true?

    I said: "It's better to make it clear which parts do not use polymorphism, and which parts do."

    You said: "Why?"

    The general aim of making it clear how things work. Many classes exist that are overloaded with functionality, because it seems convenient to make the method an instance method.



    I agree that big classes are not a good idea (after all they violate the Single Responsibility Principle). You don't need static methods to break up big classes into smaller ones, though.


    A static method is simpler than a non-static method, because there is only ever one implementation of it.



    If I call a method on an object, I actually don't care how many implementations of that method exist. All I care about is that I can trust the method will do the right thing on the object.

    A lot of the convenience of instance methods is syntactic



    Perhaps. That's not my argument, though. I actually *want* all of my methods to be potentially polymorphic.

    "Even with a refactoring browser at hand that's a risk and unnecessary work - as I still don't see any significant advantage in making a method static that could also be non-static."

    It's not a risk, as the common refactoring tools simply don't make mistakes in this area.



    Yeah sure - they are totally bug-free...

    Alan Kay said that OOP is possible in Lisp. However, I don't think he's ever said that OOP is applicable to every problem, or every facet of every problem.



    "OOP to me means [...] and extreme late-binding of all things." - Alan Kay

    I'd also assume that if he thought that there was a benefit in having some methods not being polymorphic, there would be something like non-polymorphic methods in Smalltalk, or at least in Squeak. There isn't.

    Which doesn't necessarily mean that he's right, of course.

    Where late binding gives no benefits, I don't see why you'd use OOP. If you did use OOP without benefitting from late binding, I'd say that you're disguising non-OOP code as OOP, and I understand that I'm being quite subjective here.



    Please give a concrete example of where "disguising non-OOP code as OOP" has hurt you. Perhaps that will help me better understand your position...
     
    Ricky Clarkson
    Ranch Hand
    Posts: 131
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Ilja said: "If we want PositiveNumber to be a subtype of Number (which makes sense to me), it has to implement all operations on Number in a meaningful way to conform to Liskov's Substitution Principle."

    That is true, but that only suggests that, if there are implementations of Number for which abs makes no sense, then either they shouldn't be implementations of Number, or that abs shouldn't be available on Number.

    "That is, I probably want to be able to pass a PositiveNumber to a code fragment that works on an arbitrary number."

    Right, but what counts as a number depends on your requirements.

    Is a number a scalar, a vector (such as a quaternion), does it have unbounded precision, is it comparable to every other number (is i bigger than 1?)?

    If you apply a one-size-fits-all of defining Number, with its 100 operations, then you make implementing Number harder, and you make Number less general. It's hard to implement java.awt.Graphics for this reason, and some of List's methods are allowed to throw exceptions for this reason.

    It may be syntactically awkward to do it any other way, but it's a good idea to recognise that as a limitation of the language, and not let the language limit our thinking.
     
    Ricky Clarkson
    Ranch Hand
    Posts: 131
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Stan James said: "This has appeared in here several times and triggered my interest. Can you describe how you use visitor behind static methods to give polymorphism?"

    Yes. See http://docs.google.com/Doc?id=dx5mfkq_67gkjvpg
     
    Stan James
    (instanceof Sidekick)
    Posts: 8791
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Ok, I'd call that plain double dispatch rather than visitor. Visitor is a special case DD where the intent usually has to do with iterating a collection or object graph. Either way the intent involves adding functionality to an object without changing it. It's a nice hook to let users add unforseen behavior, but I wouldn't consider it a general alternative to behavior rich, polymorphic objects.

    Visitor & DD
     
    Ricky Clarkson
    Ranch Hand
    Posts: 131
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Ilja said: "A Visitor pattern creates a circular dependency between the classes; and the static method needs to know about all the available implementations of the method."

    That's true, and a limitation, but for code where you know all the visitable classes, it's not a problem. You can view it as a function over a closed set of types.

    I don't know of a way of making it open without using another language or using reflection, but then I haven't thought about it much.

    "Anyway, if we can't change a class, we can't add a method to it, so we need to work around that deficit."

    One way of working around it is the approach I've detailed, using visitors. However, then you have some cases where you use visitors, and some where you don't, and the differences are largely social - you don't have control over that source code.

    It might be better, for consistency's sake, to do the visitor approach all the time. Of course, you shouldn't be consistent at the expense of other attributes, so if it turns out to be harder to read/write the visitor approach, fine, stick to the inconsistent one.

    " don't see it as a problem in that case, either [casting a Node to Element in DOM]. After all, we really *know* the type of the object. I don't see what benefit a visitor would bring in that case."

    Suppose w3c did some crazy thing whereby they made Element no longer a subtype of Node, but instead used Elephant where Element had been. Now the code compiles and runs, assuming that there is still a type called Element in the appropriate place, but gives a ClassCastException. A visitor implementation would have failed to compile. Ok, this is a contrived example, but it's possible in practice. I've stung myself this way in the past.

    "I've never felt that instance methods reduce clarity. And the benefit is that it becomes less work to extend the system. The system is closer to following the Open Closed Principle."

    Instance methods can make it hard to see the guts of a class - and they can encourage bad practice, such as making a class have too many responsibilities. A Point2D class should really just know how to initialise itself, and how to give you x and y. It shouldn't care about much else, such as how to draw itself on the screen.

    In terms of extensibility, it's trivial using modern tools to refactor a static method to make it an instance method, so you can change your mind later.

    I said: "Decoupling when it's not necessary is another form of indirection without benefit."

    Ilja said: "I wholeheartedly disagree."

    I should rephrase that - "Decoupling when there's no benefit is another form of indirection without benefit.". If you have class X, which uses class Y, and you put interface Z in the middle, you've actually just increased coupling. Ok, you're coupling to interface, not implementation, but it's still coupling. And at some point you're going to have to say that the implementation of Z to use is Y. So you've added interface coupling, and moved some behavioural coupling.

    Another way of looking at it though, is that you've converted the behavioural coupling into interface coupling, and some external system (your IoC framework and an XML file?) is responsible for controlling the behavioural coupling.

    There are many cases of decoupling that actually work, but one-shot interfaces like Z above isn't one of them. That's just indirection, like adding another * to a pointer type in C for no reason.

    "I disagree that there is no benefit in hiding that the accessors are directly working on fields - after all, we could later change that (could even use a polar coordinate system inside) without clients needing to change."

    Depending on the scope of the code, changing all the clients could be a 3 click job.

    "So you agree that there actually *is* an OO equivalent of an if statement?"

    Yes.

    "I'm not following you. Are you really first stating something as a fact, an then saying that you are actually not sure that it's true?"

    No. I'm saying X, then suggesting that Y may prove it wrong. I can't state things as facts, because I'm not a deity.

    "You don't need static methods to break up big classes into smaller ones, though."

    Indeed, but it helps. It's trivial, even without tools, to move a static method from one class to another (ok, less trivial to update all the clients). It's a bit less trivial to do the same with instance methods, especially updating the clients.

    "I actually *want* all of my methods to be potentially polymorphic."

    When you make them potentially polymorphic, you introduce some level of doubt about what a method actually does, because some third party could come in and provide that method. It needn't be a third party, it could just be some code you've forgotten about.

    In the code I posted for Stan James, http://docs.google.com/Doc?id=dx5mfkq_67gkjvpg , implementing both versions of abs in one place is useful for another reason. It lets me see, easily, that I have some duplication. I could reason, based on that, that I could write Point as a class with an array of ints, and then there'd only need to be one implementation of abs, no visitor. Java's type system doesn't allow a good way of doing that though - you could accidentally have a 1D point where you wanted a 3D point, so it's probably good to have them as separate types. Still, it's good to know what the ideal is.

    "I'd also assume that if he [Alan Kay] thought that there was a benefit in having some methods not being polymorphic, there would be something like non-polymorphic methods in Smalltalk, or at least in Squeak. There isn't."

    It's possible that where Alan goes wrong is in thinking that OOP is universally appropriate. I'd rather look at code and easily see which bits are benefitting from OO, and which are benefitting from procedural or even functional programming. I'd rather be as direct as I can be without introducing complexity.

    "Please give a concrete example of where "disguising non-OOP code as OOP" has hurt you. Perhaps that will help me better understand your position..."

    I wouldn't say it hurt me in this case, but if you look at the first bit of code and the last in this blog post, I think you'll see that by making it look less like OOP, I've got to a simpler solution: http://rickyclarkson.blogspot.com/2007/02/sanitising-some-code.html
     
    Stan James
    (instanceof Sidekick)
    Posts: 8791
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    This has a familiar feel ... Fairly often somebody has a style they really like and use successfully and want to pull it into Java. After all, "A real programmer can write Fortran in any language." I've tried to take a style from one language into another several times and finally decided I might just as well really learn the new language. It takes a while. Sorry if that doesn't fit your self perception ... I could have you totally wrong.
     
    Ricky Clarkson
    Ranch Hand
    Posts: 131
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Stan, I think it really depends where you read it.

    From the page you linked to:

    "The purpose of the Visitor Pattern is to encapsulate an operation that you want to perform on the elements of a data structure."

    Ok, how about a data structure with one element in it?

    http://en.wikipedia.org/wiki/Visitor_pattern supports my use of the term 'visitor pattern'.
     
    Ricky Clarkson
    Ranch Hand
    Posts: 131
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Stan, I know what you mean about feature envy between languages. I have made some mistakes by experimenting with emulating features from other languages in the past. But that's ok, that's a perfectly valid way of learning.

    I've also been very successful with many features, e.g., the borrowed Maybe type from Haskell fits very nicely into Java.
     
    Ilja Preuss
    author
    Posts: 14112
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Ricky, I feel a little bit stuck here. I don't know much to add to explain my point of view. I think I understand yours - or at least my understanding hasn't improved much with the latest posts - but I just can't agree with it...

    The only thing I would like to add is that my view of what OO designs should look like is heavily influenced by the works of Robert C. Martin ( http://www.infoq.com/presentations/principles-agile-oo-design is a good - and fun - video on the basics of his view on OO). Applying the OO principles he discusses on our project has vastly improved our ability to handle the complexity of the nearly a million lines of java code, so I'm quite confident that they work well (granted, some acclimatization was necessary). And your approach feels quite contrary.

    I'd assume that if we worked together for a while, we'd learn a lot from each other. I don't see how to proceed in this forum, though, so I will probably enter lurk-mode in this thread for a while.
     
    Stan James
    (instanceof Sidekick)
    Posts: 8791
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Ok, how about a data structure with one element in it?



    It would be right at home with an object with no methods.

    BTW: The winky smiley is smiling. It's agreeing to disagree rather than giving you a hard time. Well, too hard a time.
    [ February 22, 2007: Message edited by: Stan James ]
     
    Ricky Clarkson
    Ranch Hand
    Posts: 131
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Ilja, I think we both understand both approaches. They're actually equivalent, except in Java, mine:

    1. Allows you to make complete reasoning - you can statically guarantee that the code does what it should do, because you can find all implementations. None can be added at runtime. Of course, whether that's a good thing is debatable.

    2. Prevents you from adding new implementations in unrelated code. That's just the first point said backwards, but in Java you can't easily add a type to a visitor. Perhaps you could achieve something with bytecode instrumentation or reflection, but in Common Lisp you can add a new implementation anywhere - in other words, not all the implementations of a method need to be in the same place, because there is no visitor, but language-supported multiple dynamic dispatch.

    I'm not sure yet how applicable my approach is to Java, as I've only recently started using it. It can be a useful step in simplifying code though, even if you ultimately return the implementations to instance methods. Having the implementations of one method all on your screen can be good if you want to see duplication.
    reply
      Bookmark Topic Watch Topic
    • New Topic