views:

629

answers:

14

I have a Gene class that keeps track of genes. Gene has a method for calculating the distance between two genes. Are there any reasons to make it static?

Which is better?

public static int geneDistance(Gene g0, Gene g1)

or

public int geneDistance(Gene other)

Arguments for/against making it static? I understand what it means for a member to be static, I'm just interested in its implications for maximum cleanliness/efficiency/etc.

I repeat the same pattern for returning trimmed versions of two genes, finding matches between genes, finding matches between animals (which contain collections of genes), etc.

+3  A: 

When you make a method static, it means that the method can be called without an instance of the class. It also means that the method cannot access instance variables unless it is passed a reference to an object.

Sometimes, it makes sense to make a method static, because the method is associated with the class, but not a particular instance of the class. For example, all the parseX methods, such as Integer.parseInt(String s). This converts a String to an int, but does not have anything to do with a particular instance of an Integer object.

If, on the other hand, a method must return some data which is unique to a particular instance of an object, (like most getter and setter methods), then it can't be static.

Charles Salvia
"It also means that the method itself can only access class variables which are also static." Huh? A static method given an instance `a` can access all of `a`'s fields and methods.
Jonathan Feinberg
He means they can't access `this` since there is no instance required to call it.
Mk12
Right - I should have worded that more like : a static method cannot access instance variables UNLESS it is passed a reference to an object
Charles Salvia
I think @Charles is speaking without reference to the OP's `public static int geneDistance(Gene g0, Gene g1)`. He's obviously referring to in-class static methods acting on the class rather than on passed-in instances.
non sequitor
Charles, you can edit your answer to clarify.
Jonathan Feinberg
+7  A: 

IMO there is no absolute "better", but public int geneDistance(Gene other) is stylistically more similar to other methods in Java (e.g. Object.equals, Comparable.compareTo), so I'd go that way.

ammoQ
A: 

Here's a meta-answer, and a fun exercise: survey a bunch of the Java SDK's library classes and see if you can categorize the commonalities between static methods in different classes.

Jonathan Feinberg
Unfortunately there are no meta-upvotes. ;)
Bombe
+13  A: 

Instance, not static


For this case I think the second choice is clearly better. If you think about it, any method could be implemented as static if you are willing to pass the object to it, this only seems to be a special case because the other parameter is also an instance.

Therefore, our search for symmetry and abstraction is slightly offended by having to choose between the two instance objects for the dot operator. But if you look at .method as . then operator, it isn't really a problem.

Plus, the only way to do functional-style chaining is with an attribute, i.e., instance method. You probably want thing.up.down.parent.next.distance(x) to work.

DigitalRoss
+6  A: 

I prefer the second form, i.e. instance method for the following reasons:

  1. static methods make testing hard because they can't be replaced,
  2. static methods are more procedural oriented (and thus less object oriented).

IMO, static methods are ok for utility classes (like StringUtils) but I prefer to not abuse using them.

Pascal Thivent
+1 Note that static imports can make the utility classes very clean, which is a rare case for statics. e.g. Something like an assertion framework for design-by-contract.
Michael Easter
That's true e.g. JUnit 4 and I like it. Didn't see the +1 though :)
Pascal Thivent
A: 

I'd select the second approach. I see no advantage in making the method static. Since the method is in the Gene class, making it static only adds one extra parameter with no extra gain. If you need a util class, that's a whole different deal. But in my opinion there's usually no need for a util class if you can add the method to the class in question.

Carlos
+2  A: 

public static int geneDistance(Gene g0, Gene g1) would be part of a separate utility class like Collections and Arrays in Java whereas public int geneDistance(Gene other) will be part of the Gene class. Considering you have other operations like "trimmed versions of two genes, finding matches between genes, finding matches between animals (which contain collections of genes), etc" I would create a separate static utility class for them as these operations aren't semantically meaningful to what a Gene is.

If the the semantics of "gene distance" can be wrapped up into your equals(Object o) method then you could consume it there or else include it in your static utility.

non sequitor
actually, I had a static utility originally but then moved all these functions into `Gene` and `Species`. Why are these semantically related to Genes?
Rosarch
I think the Collections utility class is there so it's easier for third parties implement the various Collection interfaces without needing to implement their own min, rotate, sort, etc. methods. For the Gene class, this is not a concern. Consider the "java.awt.Point" class, there's no Points utility class for calculating the distance between points.
Sam Barnum
@Sam I am not only looking at the single distance checking but also the trimming,matching,collection matching and whatever else he decides to put in which is in keeping with just what something like `Collections` represents. @Rosarch when I say not semantically related I mean finding something like the trimmed versions of multiple genes is a concern no necessarily meaningful to what a gene is (I certainly might be wrong since I am not the biologist lol :)) but hopefully you catch my drift.
non sequitor
+1  A: 
NawaMan
A: 

I think the problem domain should inform the answer beyond the general stylistic and/or OO considerations.

For example, I'm guessing that for the domain of genetic analysis, the notions of 'gene' and 'distance' are fairly concrete and will not require specialization through inheritance. Were that not the case, one could make a strong case for opting for the instance methods.

+1  A: 

I would make this an instance method. But that might be due to the fact that I have no clue of genes ;)

Instance methods can be overridden by subclasses which greatly reduces the complexity of your code (less need for if-statements). In the static method example, what will happen I you get a specific type of gene for which the distance is calculated differently? Ad another static method? If you'd have to process a polymorphic list of genes you'd have to look a the type of gene to select the correct distance method... which increases coupling and complexity.

p3t0r
A: 

The main reason to prefer the instance method is polymorphism. A static method cannot be overridden by a subclass, which means you can't customize the implementation based on the instance type. This might not apply in your case, but it is worth mentioning.

If gene distance is completely independent of the type of the gene, I would prefer using a separate utility class to make that independence more explicit. Having a geneDistance method as part of the Gene class implies that distance is a behavior related to the gene instance.

Jason Day
A: 

My rewording of Charle's answer :

If the method in question intends to change the state of the underlying object in any way, make it an instance method. Else, make it static.

Which depends on the way the object's class is designed.

In your case, alphazero, probably the int geneDistance(Gene g0, Gene g1) does not modify the state of an instance of Gene.

I would make this method static. And put it in a utility class like GeneUtils.

Of course, there might be other aspects of your problem that I am not aware of, but this is the general thumb of rule that I use.

P.S. -> The reason I would not put the method in the Gene class itself is because a Gene should not be responsible for computing it's distance from another Gene. ;-)

divesh premdeep
A: 

I would like to start answering on your question with the new one: What your class Gene is responsible for? May be you have heard about the 'Single-Responsibility Principle': A class should have only one reason to change. So, I believe if you answer this question you will be able to decide how your application should be designed. In this particular case, I would not use neither the first approach nor the second one. In my opinion it is much better to define new responsibility and encapsulate it in a separate class or may be a function.

Cicero
+2  A: 

I'll try to sum up some of the points already given here to which I agree.

Personally I don't think there is a "feels better" answer. Valid reasons do exist on why you don't wan't a utility class filled with static methods.

The short answer is that in an object oriented world you should use objects and all the good "stuff" that comes with them (encapsulation, polymorphism)

Polymorphism

If the method for calculating the distance between the genes varies, you should roughly (more likely a Strategy) have a Gene class per variation. Encapsulate what varies. Else you will end up with multiple ifs.

Open For Extension, Closed for Modification

That means that if a new method for calculating the distance between genes comes up down the line, you shouldn't modify existing code, but rather add new one. Else you risk breaking what's already there.

In this case you should add a new Gene class, not modify the code written in the #geneDistance

Tell Don't Ask

You should tell your objects what to do, not ask them for their state and make decisions for them. Suddenly you break the single responsibility principle since that's polymorphism.

Testability

Static methods may well be easy to test in isolation, but down the road you will make use of this static method in other classes. When it comes to testing that classes on isolation, you will have hard time doing it. Or rather not.

I'll let Misko have his saying which is more likely better than what I can come up with.

import junit.framework.Assert;

import org.junit.Test;

public class GeneTest
{
    public static abstract class Gene
    {
        public abstract int geneDistance(Gene other);
    }

    public static class GeneUtils
    {
        public static int geneDistance(Gene g0, Gene g1)
        {
            if( g0.equals(polymorphicGene) )
                return g0.geneDistance(g1);
            else if( g0.equals(oneDistanceGene) )
                return 1;
            else if( g0.equals(dummyGene) )
                return -1;
            else
                return 0;            
        }
    }


    private static Gene polymorphicGene = new Gene()
                                    {

                                        @Override
                                        public int geneDistance(Gene other) {
                                        return other.geneDistance(other);
                                        }
                                    };

    private static Gene zeroDistanceGene = new Gene() 
                                    {                                        
                                        @Override
                                        public int geneDistance(Gene other) {
                                        return 0;
                                        }
                                    };

    private static Gene oneDistanceGene = new Gene() 
                                    {                                        
                                        @Override
                                        public int geneDistance(Gene other) {
                                        return 1;
                                        }
                                    };

    private static Gene hardToTestOnIsolationGene = new Gene()
                                    {

                                        @Override
                                        public int geneDistance(Gene other) {
                                        return GeneUtils.geneDistance(this, other);
                                        }
                                    };

    private static Gene dummyGene = new Gene()
                                    {

                                        @Override
                                        public int geneDistance(Gene other) {
                                        return -1;
                                        }
                                    };                                    
    @Test
    public void testPolymorphism()
    {
        Assert.assertEquals(0, polymorphicGene.geneDistance(zeroDistanceGene));
        Assert.assertEquals(1, polymorphicGene.geneDistance(oneDistanceGene));
        Assert.assertEquals(-1, polymorphicGene.geneDistance(dummyGene));
    }

    @Test
    public void testTestability()
    {

        Assert.assertEquals(0, hardToTestOnIsolationGene.geneDistance(dummyGene));
        Assert.assertEquals(-1, polymorphicGene.geneDistance(dummyGene));
    }    

    @Test
    public void testOpenForExtensionClosedForModification()
    {

        Assert.assertEquals(0, GeneUtils.geneDistance(polymorphicGene, zeroDistanceGene));
        Assert.assertEquals(1, GeneUtils.geneDistance(oneDistanceGene, null));
        Assert.assertEquals(-1, GeneUtils.geneDistance(dummyGene, null));
    }    
}
Cue