开发者

Interfaces having only one implementation

When using jmock, you can mock a concrete class if you set the class imposteriser. I noticed that the class imposterizer is in the legacy package, so I really don't want to use it (especially because it super easy to extract an interface using the refactoring tools in my IDE). I don't like t开发者_StackOverflow社区o have instance variables of concrete classes either.

Extracting the interface, however, I've noticed a pattern emerging in my code base. A lot of the time an interface has only one implementing concrete class. I prefer to use an interface wherever possible, but it seems really verbose to have all these extra files. Also, it's slightly tedious to update the interface and update the implementer every time I want to add a new method to the class.

Is this just the price you pay for proper abstraction or is there a better approach that I haven't thought of? Should all classes implement an interface even if the only thing in the interface is getters/setters?


If the interface is only getters and setters, that sounds like it's more to do with data than behaviour - and doesn't sound like the sort of thing I'd mock. I'm happy to use simple production code directly within tests for other classes. I only inject dependencies for classes providing services.

I feel your pain, but personally I do still write the interface, even if there's currently only one production implementation. Quite often I'll find I write a stub or fake implementation as well after mocking for a while. Fakes can end up being rather simpler to use (leading to clearer tests) unless you're really interested in testing the interaction between the caller and the service.

It also means that when someone wants to look at methods a particular dependency provides, they can see just the interface with no implementation involved.


Some general points I go by:

  • The mocks in your test are alternative implementations. So even if you only have one implementation in your product code, there will be more than one in the code base.
  • Prefer constructor injection to getters and setters. This will also make immutable objects more natural.
  • Interfaces describe the contract of a piece of behaviour, and does so in a way that the users of the interface need not know about the details of the implementation.
  • Don't make interfaces for things that aren't concerned with these things: abstracting away implementation details; multiple alternative implementations; mockability; behaviour.
  • Prefer designs that have many small composed parts, over designs that have few large interconnected parts.
  • If something is hard to test or mock, then try breaking it apart into smaller parts that have simpler APIs, and then compose those.
  • Give things precise names, and look out for duplication not only in code, but also in structure and names.
  • Avoid implementation inheritance - prefer composition instead. Interface inheritance is harder to misuse, and often falls out naturally when needed anyway.

These points will hopefully help you to make simpler and more testable and maintainable designs. At least after a bit of experience trying to follow them.


If it's verbose and tedious it's bad style, no matter what the theoretical advantages are.

If it's easy to extract the interface using your tools why do it now? Why not do it when it actually has some real purpose. I see too much code that is architected with the future in mind instead of solving the problem at hand.

Of course all debate about these issues is just opinion. There are no facts to be found.


You mention "proper abstraction". Well, it's only proper if the interface makes sense for more than one class. Especially considering that you are extracting the interfaces from concrete classes, you're probably ending up with methods in the interface which shouldn't be there. That is of course, if it makes sense for the interface itself to exists in the first place.

The last thing I'm going to point out is that your interfaces don't seem particularly stable. You seem to be implying that adding methods to interfaces is commonplace for you. I'd question the abstraction of class when the contract of the class (i.e. its interface) is so volatile.

All in all, I'm not convinced that immediately providing an interface for everything constitutes proper abstraction. Rather, waiting for the class's public interface to stabilize first, and then considering a proper abstraction (i.e. not just hitting the "extract interface..." button in IDE) is much better from a design perspective. Plus, a class might abstract into several unrelated interfaces, rather than a single interface.


If your interface is primarily properties, it's likely that all the implementing classes are related, maybe an abstract class would be better suited for your needs. Then you can avoid some of the boilerplate. The primary functions of the abstract class could still be abstracted further by an interface.

interface IRunner {
      void run();
      int doOtherThing();
}

abstract class Thing implements IRunner {

    //this is class-specific
    abstract void run();
    //this is common to all 'Things'
    int doOtherThing() { return 0; }

    //as are these properties
    public int getProp() {...}
    public void setProp(int val) {...}

}

public class Goo extends Thing {
      public void run() {
         int i = getProp() + doOtherThing();
         makeMagicGoo(i);
      }

}


If having the extra interfaces offends you, you could create the mock objects (by hand) as subclasses of the classes you are mocking.

If a class merely consists of (simple) getters and setters, there hardly seems any point in mocking it. Just use the real thing.


There's one key use case where generating an interface for an object is vital even where you're only ever writing one implementation of it: where you want to be able to generate JDK proxies for the instances of the class (this is useful if you're doing Spring AOP, for example). It's possible to use something like cglib to build proxies for arbitrary things, but it's much more complex; JDK proxies are easy by comparison.

I'm also a little concerned that your IDE isn't helping you as much as it should. I find that with mine (Eclipse) I can create methods in an interface if I just mark them with @Override in the implementation and pick the right auto-fix option. (That's not to say that it will pick the right interpretation of the method, but that's your job.)


I would recommend this:

  • No interfaces for simple Value-style or DTO-style classes, especially the immutable ones
  • No interfaces are required for package-local classes with very limited dependencies on other non-interface classes in their constructors. The non-private methods of the class are its interface by definition.
  • Use interfaces if it is possible to extract sub-interfaces, according to interface segregation principle
  • I still use interfaces between packages and modules, i.e. I prefer that my package only expose public interfaces, and not public classes
  • Despite the power of Mockito and similar libraries, mock of the actual class will still invoke its constructor. So if the constructor uses other actual classes or has some non-trivial exception-throwing logic I would use interface to avoid surprises on writing unit tests to other classes

A couple of useful links on the topic:
https://martinfowler.com/bliki/InterfaceImplementationPair.html
https://rrees.me/2009/01/31/programming-to-interfaces-anti-pattern/


please refer to answer posted here which puts focus on the contract-definition role of interface https://softwareengineering.stackexchange.com/a/179265/74889

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜