开发者

Java Code Style -- Interfaces vs. Abstract Classes

开发者 https://www.devze.com 2023-02-11 08:40 出处:网络
A new collaborator of mine who was reviewing some code I\'d written told me that she wasn\'t used to seeing interfaces used directly in Java code, e.g.:

A new collaborator of mine who was reviewing some code I'd written told me that she wasn't used to seeing interfaces used directly in Java code, e.g.:

public interface GeneralFoo { ... }

public cla开发者_JAVA百科ss SpecificFoo implements GeneralFoo { ... }

public class UsesFoo {
     GeneralFoo foo = new SpecificFoo();
}

instead, expecting to see

public interface GeneralFoo { ... }

public abstract class AbstractFoo implements GeneralFoo { ... }

public class SpecificFoo extends AbstractFoo { ... }

public class UsesFoo {
     AbstractFoo foo = new SpecificFoo();
}

I can see when this pattern makes sense, if all SpecificFoos share functionality through AbstractFoo, but if the various Foos have entirely different internal implementations (or we don't care how a specific Foo does Bar, as long as it does it), is there any harm in using an interface directly in code? I realize this is probably a tomato/tomato thing to some extent, but I'm curious if there's an advantage to the second style, or disadvantage to the first style, that I'm missing.


If you have no need for an abstract class with certain details common to all implementations, then there's no real need for an abstract class. Complexity often gets added to applications because there is some perceived need to support future features that haven't yet been defined. Stick with what works, and refactor later.


No, she's inexperienced, not right. Using interfaces is preferred, and writing redundant abstract super classes for the sake of redundancy is redundant.

UsesFoo should care about the behaviour specified by the interface, not about the super class of its dependencies.


For me "she wasn't used to" is not good enough reason. Ask her to elaborate on that.

Personally I'd use your solution, because:

  1. AbstractFoo is redundant and ads no value in current situation.

  2. Even if AbstractFoo was needed (for some additional functionality), I'd always use lowest needed type: if GeneralFoo was sufficient, then I'd use that, not some class derived from it.


It depends only on your problem.

If you use interfaces only, then if all your classes have a same method, it would have to be implemented redundantly (or moved away to a Util class).

On the other hand, if you do write an intermediary abstract class, you solved that problem, but now your subclass may not be a subclass of another class, because of absence of multiple inheritance in Java. If it was already necessary to extend some class, this is not possible.

So, shortly - it's a trade off. Use whichever is better in your particular case.


There is not harm in directly using an interface in code. If there were, Java would not have interfaces.

The disadvantages of using an interface directly include not being able to reach and class-specific methods which are not implemented in the interface. For poorly written interfaces, or classes which add a lot of "other" functionality, this is undesirable as you lose the ability to get to needed methods. However, in some cases this might be a reflection of a poor design choice in creating the interface. Without details it is too hard to know.

The disadvantages of using the base class directly include eventually ignoring the interface as it is not frequently used. In extreme cases, the interface becomes the code equivalent of a human appendix; "present but providing little to no functionality". Unused interfaces are not likely to be updated, as everyone will just use the base abstract class directly anyway. This allows your design to silently rot from the viewpoint of anyone who actually tries to use the interface. In extreme cases, it is not possible to handle an extending class through the interface to perform some critical functionality.

Personally, I favor returning classes via their interface and internally storing in members them via their lowest sub-class. This provides intimate knowledge of the class within the class's encapsulation, forces people to use the interface (keeping it up-to-date) externally, and the class's encapsulation allows possible future replacement without too much fuss.


I'm curious if there's an advantage to the second style, or disadvantage to the first style, that I'm missing

That reasons for the first interfaces style:

  1. Often, the design is such that the interface is the public interface of the concept while the abstract class is an implementation detail of the concept.

    For example, consider List and AbstractList in the collection framework. List is really what clients are usually after; fewer people know about about AbstractList because its an implementation detail to aid suppliers (implementers) of the interface), not clients (users) of the class.

  2. The interface is looser coupling, therefore more flexible to support future changes.

  3. Use the one that more clearer represents the requirement of the class, which is often the interface.

    For example, List is often used rather than AbsrtactList or ArrayList. Using the interface, it may be clearer to a future maintainer that this class needs some kind of List, but it does not specifically need an AbstractList or an ArrayList. If this class relied on some AbstractList-specific property, i.e. it needs to use an AbstractList method, then using AbstractList list = ... instead of List list = ... may be a hint that this code relies on something specific to an AbstractList .

  4. It may simplify testing/mocking to use the smaller, more abstract interface rather than to use the abstract class.


It is considered a bad practice by some to declare variables by their AbstractFoo signatures, as the UsesFoo class is coupled to some of the implementation details of foo.

This leads to less flexibility - you can not swap the runtime type of foo with any class that implements the GeneralFoo interface; you can only inject instances that implement the AbstractFoo descendant - leaving you with a smaller subset.

Ideally it should be possible for classes like UsesFoo to only know the interfaces of the collaborators they use, and not any implementation details.

And of course, if there is no need to declare anything abstract in a abstract class AbstractFoo implements GeneralFoo - i.e. no common implementation that all subclasses will re-use - then this is simply a waste of an extra file and levels in your hierarchy.


Firstly I use abstract and interface classes plentifully.

I think you need to see value in using an interface before using it. I think the design approach is, oh we have a class therefore we should have an abstract class and therefore we should have interfaces.

Firstly why do you need an interface, secondly why do you have an abstract class. It seems she may be adding things, for adding things sake. There needs to be clear value in the solution otherwise you are talking about code that has no value.

Emperically there you should see the value in her solution. If there is no value the solution is wrong, if it cant be explained to you she does not understand why she is doing it.

Simple code is the better solution and refactor when you need the complexity, flexibility or whatever perceived value she is getting from the solution.

Show the value or delete the code!

Oh one more thing have a look at the Java library code. Does that use the abstract / interface pattern that she is applying .. NO!

0

精彩评论

暂无评论...
验证码 换一张
取 消