0

I am designing a system where two modules, one which gestions files and another users. For certain logic operations, they need the services offered by each other.

Each module is represented by a singleton which implements a interface offering some services to each other, with abstract factories to provide them, like so:

public class UserMain implements UserInternalService {

/*
 * Internal interfaces
 */
/**
 * Allows interaction with the projects database.
 */
FilesInternaService fileSystem;

/**
 * Constructor is private, as this is a singleton.
 */
protected UserMain() {
}

private static UserMain singleton = null;

/**
 * Singleton factory. Returns a reference to the singleton. If there is no
 * reference yet, creates it.
 */
protected static synchronized UserMain getReference() {
    if (singleton == null) {
        singleton = new UserMain();
        singleton.fileSystem = FileMain.getInternalService();
    }
    return singleton;
}

/**
 * Factory method for the singleton as a UserInternalService
 */
public static UserInternalService getUserInternalService() {
    return getReference();
}

}

And the file module main class is like so:

public class FileMain implements FilesInternaService{

/**
 * Interface to user subsystem for request validation, etc.
 */
UserInternalService userSystem;

/**
 * Creation of instances aside from singleton disallowed.
 */
protected FileMain(){};

private static FileMain singleton = null;

/**
 * Singleton factory.
 * Returns a reference to the singleton.
 * If there is no reference yet, creates it.
 */
protected synchronized static FileMain getReference(){
    if(singleton == null)
        singleton = new FileMain();
        singleton.userSystem = UserMain.getUserInternalService();
    return singleton;
}

/**
 * Abstract factory for Internal Services singleton.
 * @return
 */
public static FilesInternaService getInternalService(){
    return getReference();
}
}

I am not enterely sure that I am correctly handling the circular dependency. Is there any way this may break unexpectedly?

EDIT: As it has been answered below, the correct way to handle this is injection. However, the correct way to handle this is not what I am asking here, but rather how could this specific solution blow up.

Jsevillamol
  • 2,425
  • 2
  • 23
  • 46
  • 1
    Why not write some unit tests and test it out? – Minh Kieu May 25 '16 at 18:44
  • @MinhKieu I did, and it seems to work. But I am not too confident in my foresight to have done the pertinent tests. – Jsevillamol May 25 '16 at 18:49
  • Where is the circular reference you talking about? – Minh Kieu May 25 '16 at 18:50
  • @MinhKieu UserMain has a field FileInternalService which it fills calling the singleton factory method FileMain.getInternalService(). FileMain does likewise with UserMain. So we end up with each singleton instance referencing each other. – Jsevillamol May 25 '16 at 18:52
  • Why the two modules HAVE to depend on each other? Can you explain a bit? – Nazar Merza May 25 '16 at 19:28
  • @NazarMerza Files Module needs to guarantee that when someone solicits a request the requester has privileges to do so, which is Users job. Users Module can grant privileges to users to edit specific files, but he has to make sure that the file exist in first place, and thus needs files. – Jsevillamol May 25 '16 at 20:02
  • 2
    The issue is not that there is a risk that the design might "break" but rather that to manage this relationship requires constant awareness. Most cyclic dependencies can be refactored in this way: 1) Say, you have classes A and B depending on each other, pick any class, 2) identify the methods in this class that both A and B depend on, 3) extract these methods into a class C, 4) make both A and B depend on C, 5) one of classes A and B will still depend on the other, but the cyclic dependency will have been eliminated. –  May 25 '16 at 20:53

1 Answers1

1

The clean way to handle this is using dependency injection, to keep the dependencies at the interface level.

It's ok for UserMain to depend on FilesInternaService and it's ok for FileMain to depend on UserInternalService; but it's not ok for UserMain to depend on FileMain or for FileMain to depend on UserMain. In other words, it's not ok to depend on concrete implementation.

An instance of FilesInternaService should be injected into UserMain and an instance of UserInternalService should be injected into FileMain.


References

  1. Are circular dependencies considered bad design?
  2. Why are circular references considered harmful?
  3. https://softwareengineering.stackexchange.com/questions/11856/whats-wrong-with-circular-references
  4. https://softwareengineering.stackexchange.com/questions/306483/how-to-solve-circular-dependency
Community
  • 1
  • 1
jaco0646
  • 15,303
  • 7
  • 59
  • 83
  • I should have specified that I am actively avoiding injection, as it is rather unclean in java without libraries and I am forced to rely only on the standard library. I do agree that injection should be the way to do it, but it is not what I am looking for here. – Jsevillamol May 25 '16 at 19:01
  • Mark Seemann has written an illuminating article on [When to use a DI Container](https://blog.ploeh.dk/2012/11/06/WhentouseaDIContainer/). – jaco0646 Jan 27 '21 at 17:30