Java - correct way to delegate methods
Asked Answered
R

3

6

My program gets information from an external source (can be a file, a database, or anything else I might decide upon in the future).

I want to define an interface with all my data needs, and classes that implement it (e.g. a class to get the data from a file, another for DB, etc...).

I want the rest of my project to not care where the data comes from, and not need to create any object to get the data, for example to call "DataSource.getSomething();"

For that I need DataSource to contain a variable of the type of the interface and initialize it with one of the concrete implementations, and expose all of its methods (that come from the interface) as static methods.

So, lets say the interface name is K, and the concrete implementations are A,B,C.

The way I do it today is:

public class DataSource  {
    private static K myVar = new B();

    // For **every** method in K I do something like this:

    public static String getSomething() {
          return myVar.doSomething();
    }

    ...
}

This is very bad since I need to copy all the methods of the interface and make them static just so I can delegate it to myVar, and many other obvious reasons.

What is the correct way to do it? (maybe there is a design pattern for it?)

**Note - since this will be the backbone of many many other projects and I will use these calls from thousands (if not tens of thousands) code lines, I insist on keeping it simple like "DataSource.getSomething();", I do not want anything like "DataSource.getInstance().getSomething();" **

Edit : I was offered here to use DI framework like Guice, does this mean I will need to add the DI code in every entry point (i.e. "main" method) in all my projects, or there is a way to do it once for all projects?

Rack answered 6/9, 2016 at 14:16 Comment(8)
Sounds a lot like the Repository patternSmallclothes
What about just adding a static getter that returns myVarExuviae
@JornVernee You mean like getInstance() ? Which the OP says he does not want? (I don't think it's useful / meaningful to do it the way to OP wants, but it's still his question)Estrange
@JornVernee again, I want it as simple as DataSource.getSomething() since it will be used many many times, I do not want to have to call a getter and then invoke a method.Rack
Sorry, I'm a little sleepy.Exuviae
This is a textbook case of what dependency injection is all about. Construct the concrete instance up front and then pass it--method up to you, be it constructor argument, mutator, Spring-injected-property--to the clients so that they can record it and then use it. And they only need to know about the base interface. There's no need to replicate your interface as a static distributor.Tinytinya
@GiladBaruchian You ask for "correct" way, but put a constraint (static) that makes any answer invalid. So fix your thoughts, then your question.Sundew
@GiladBaruchian It's going to be hard to learn to be a good developer if you fight against the patterns and methods that are tried and tested. Of course you can create your own improved patterns and methods, but that's easier said than done.Stoneman
N
9

The classes using your data source should access it via an interface, and the correct instance provided to the class at construction time.

So first of all make DataSource an interface:

public interface DataSource {

    String getSomething();
}

Now a concrete implementation:

public class B implements DataSource {

    public String getSomething() {
        //read a file, call a database whatever..
    }
}

And then your calling class looks like this:

public class MyThingThatNeedsData {

    private DataSource ds;

    public MyThingThatNeedsData(DataSource ds) {
        this.ds = ds;
    }

    public doSomethingRequiringData() {
        String something = ds.getSomething();
        //do whatever with the data
    }
}

Somewhere else in your code you can instantiate this class:

public class Program {

    public static void main(String[] args) {
        DataSource ds = new B(); //Here we've picked the concrete implementation 
        MyThingThatNeedsData thing = new MyThingThatNeedsData(ds);  //And we pass it in
        String result = thing.doSomethingThatRequiresData();
    }
}

You can do the last step using a Dependency Injection framework like Spring or Guice if you want to get fancy.

Bonus points: In your unit tests you can provide a mock/stub implementation of DataSource instead and your client class will be none the wiser!

Neuman answered 6/9, 2016 at 14:29 Comment(17)
This means that for ever class that needs data I will need to create an object that implements data source object, I already know I can do that and I specifically said i do not want that.Rack
I want all of my classes to use one class with static methodsRack
No, you don't want that. Read last paragraph of my answer.Fredia
I took my inspiration for this from spring actually that lets you load the beans on startup. but i have many many classes now and adding spring will make everything more complexRack
Would you mind showing an example of a Spring Repository class that uses static classes? I'd like to write the author of that guide/blog a long evil letter.Fredia
@Neuman I don't think CDI is any "fancy". It's standard in any serious bigger application.Fredia
lol, without static classes of course, i am using that because I dont have the spring Context that loads the beansRack
@GiladBaruchian There are CDI frameworks that work outside of application containers. Just search for it. If you want to avoid CDI, define the implementations in your main class (as seen above) or using Factories.Fredia
btw you miss understood my a little bit, I already have an interface K, i dont need DataSource as an interface, it is the class that wraps the interface and assigns it with a concrete implementation and exposes the static methodsRack
I understood that bit. It's an antipattern nonetheless. Think of multithreading and testing.Fredia
@GiladBaruchian so replace "DataSource" with "K" in the example above, the same pattern applies. You don't need to create a new instance for every class that uses it, you can inject the same instance of B into another class that needs it. I don't understand your first comment.Neuman
@Neuman This means I will have to convert all my projects to spring, and in every entry point add code to get the beans from spring, seems like an overkill and even more complicated than DataSource.getInstance().getSomething()Rack
Nope, you don't have to convert to Spring. There are lots of good (much) smaller DI frameworks like Guice, Dagger, HK2, etc. Pick one and test with it. Your design will be simple and efficient. That's the correct way, even if you don't think it is.Sundew
@OlivierGrégoire I read about Guice now, my question is - if I have many entry points (i.e. main methods), I will have to add the binding to each and every one of them?Rack
The simple answer is: yes. You can share a big chunk of the initialization part, though. The real question, however, is: why would you have multiple entry points for a single application?Fredia
@GiladBaruchian Short version: with Guice, you can create a Module concrete class and instanciate the same class in various "entry points". Just do Guice.createInjector(new MyModule()).getInstance(EntryPointA.class). A Module can "install" several "sub-modules" so you define it once for the whole code-base and it just works, even if you use several different entry points.Sundew
Let me redefine - I have many projects that use the project with DataSource classRack
P
2

I want to focus in my answer one important aspect in your question; you wrote:

Note - I insist on keeping it simple like "DataSource.getSomething();", I do not want anything like "DataSource.getInstance().getSomething();"

Thing is: simplicity is not measured on number of characters. Simplicity comes out of good design; and good design comes out of following best practices.

In other words: if you think that DataSource.getSomething() is "easier" than something that uses (for example) dependency injection to "magically" provide you with an object that implements a certain interfaces; then: you are mistaken!

It is the other way round: those are separated concerns: one the one hand; you should declare such an interface that describes the functionality that need. On the other hand, you have client code that needs an object of that interface. That is all you should be focusing on. The step of "creating" that object; and making it available to your code might look more complicated than just calling a static method; but I guarantee you: following the answer from Paolo will make your product better.

It is sometimes easy to do the wrong thing!

EDIT: one pattern that I am using:

interface SomeFunc {
  void foo();
}

class SomeFuncImpl implements SomeFunc {
  ...
}

enum SomeFuncProvider implements SomeFunc {
  INSTANCE;
  private final SomeFunc delegatee = new SomeFuncImpl();
  @Override
  void foo() { delegatee.foo(); }

This pattern allows you to write client code like

class Client {
  private final SomeFunc func;
  Client() { this(SomeFuncProvider.INSTANCE); }
  Client(SomeFunc func) { this.func = func; }

Meaning:

  • There is a nice (singleton-correctway) of accessing an object giving you your functionality
  • The impl class is completely unit-testable
  • Client code uses dependency injection, and is therefore also fully unit-testable
Payable answered 6/9, 2016 at 14:42 Comment(6)
@GostCat I understand and agree with what you are saying, but please understand that this will be the backbone of many many other projects and I will use these calls from thousands if not tens of thousands code lines. So I am hoping I can find a way to keep a good design and minimize the complexity of the callRack
@GiladBaruchian I added one pattern that works nicely for me; maybe it is of help for you, too.Payable
you wrote exactly what I wrote with a differnt way to create a singleton (your SomeFuncProvider is my DataSource). I do not want to duplicate every function just to delegate it.Rack
Well, as said; my solution has quite some advantages. And keep in mind: you said that this is a core interface that will be used in many places. So: it probably will not change very often. Thus: given the many many advantages of that enum-singleton pattern, I wouldn't mind writing down that delegation manually. And you know: if your application is really that large, than writing testable code is essential. As said: in my pattern, almost every part can be unit-tested. Try that with static methods / static singletons!Payable
I love the sentences you wrote above about how simplicity is not measured in characters, but the example implementation of a singleton is rather an example of how not to approach the problem. It adds little value (very leaky abstraction) and creates a lot of potentially buggy boilerplate code. Also I have no idea why you used an Enum here. A factory would be much better suited than the proxy you've written, IMHO.Fredia
@HubertGrzeskowiak Well, you saw his further comments. So, I think the enum thing is a good compromise: on the one hand it is "easy" to grasp and implement; on the other hand, it has the advantages I listed. Probably it is not "the best solution"; but at least some sort of starting point for him!Payable
F
1

My program gets information from an external source (can be a file, a database, or anything else I might decide upon in the future).

This is the thought behind patterns such as Data Access Object (short DAO) or the Repository pattern. The difference is blurry. Both are about abstracting away a data source behind a uniform interface. A common approach is having one DAO/Repository class per business- or database entity. It's up to you if you want them all to behave similarly (e.g. CRUD methods) or be specific with special queries and stuff. In Java EE the patterns are most often implemented using the Java Persistence API (short JPA).

For that I need DataSource to contain a variable of the type of the interface and initialize it with one of the concrete implementations,

For this initialization you don't want to know or define the type in the using classes. This is where Inversion Of Control (short IOC) comes into play. A simple way to archieve this is putting all dependencies into constructor parameters, but this way you only move the problem one stage up. In Java context you'll often hear the term Context and Dependency Injection (short CDI) which is basically an implementation of the IOC idea. Specifically in Java EE there's the CDI package, which enables you to inject instances of classes based on their implemented interfaces. You basically do not call any constructors anymore when using CDI effectively. You only define your class' dependencies using annotations.

and expose all of its methods (that come from the interface)

This is a misconception. You do want it to expose the interface-defined method ONLY. All other public methods on the class are irrelevant and only meant for testing or in rare cases where you want to use specific behavior.

as static methods.

Having stateful classes with static method only is an antipattern. Since your data source classes must contain a reference to the underlying data source, they have a state. That said, the class needs a private field. This makes usage through static methods impossible. Additionally, static classes are very hard to test and do not behave nicely in multi-threaded environments.

Fredia answered 6/9, 2016 at 14:31 Comment(2)
Thank you for the response, I am new to spring and I loved the idea of IOC. I am working on legacy code with a lot of constructors and projects, so converting everything to spring now is a problem. All I need is to have the concrete object created in one place only, and expose it to all my other projects with the most simple syntax possible.Rack
You don't have to switch everything in order to use CDI. You can apply it piece by piece. Other than that Spring has a central main class, where you can set the implementation - or better yet: set it in a factory all other code is going to use.Fredia

© 2022 - 2024 — McMap. All rights reserved.