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
Spot false dilemmas now, ask me how!
(If you're not on the edge, you're taking up too much room.)
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
Originally posted by Bert Bates:
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?
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
Spot false dilemmas now, ask me how!
(If you're not on the edge, you're taking up too much room.)
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]
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
Originally posted by Ricky Clarkson:
Ideally, an instantiable class should just contain some public fields, and nothing else (subjective).
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
Originally posted by Ricky Clarkson:
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.
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
Originally posted by Ricky Clarkson:
Many parts of programming are generally considered to be subjective.
When to make a method static is one of them
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.
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.
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
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
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
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.
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
Again, that is false as long as the visitor pattern exists.
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
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'.
"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
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?
"Not at all."
So do you sometimes use static? If not, it wasn't addressed at you.![]()
"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?
"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.
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
"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.
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.
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
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
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
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
Ok, how about a data structure with one element in it?
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
Did you see how Paul cut 87% off of his electric heat bill with 82 watts of micro heaters? |