Java circular references

2020-04-10 23:25发布

In the project im working on, people wrote services class to access DAO. Almost every business object has it's own service which use it's own DAO. On some services, we are using references to other services. At the moment, people are instantiating needed services inside the constructor.

But now, I have trouble because service A needs service B and service B needs service A so a call to either constructor results in stack overflow ...

Example (pseudo-code) :

//Constructor of OrderService
public OrderService() {
     orderDAO = DAOFactory.getDAOFactory().getOrderDAO();
     itemService = new ItemService();
}

//Constructor of ItemService
public ItemService() {
     itemDAO = DAOFactory.getDAOFactory().getItemDAO();
     orderService = new OrderService();
}

How would you solve this ? using singleton pattern ?

Thanks

4条回答
甜甜的少女心
2楼-- · 2020-04-10 23:32

The Spring Framework solves this problem by using dependency injection. In short, what it does is to instantiate all the DAOs, and then set the dao-dependencies after instantiation, but before main business logic.

If you have to do this manually, here's an example:

/*
  OrderService
 */
public OrderService ()
{
     orderDAO = DAOFactory.getDAOFactory().getOrderDAO();
}

public setItemService (ItemService service)
{
     itemService = service;
}

/*
  ItemService
 */
public ItemService ()
{
     itemDAO = DAOFactory.getDAOFactory().getItemDAO();
}

public setOrderService (OrderService service)
{
     orderService = service;
}

/*
   Bring it together in some other class
 */
...
// instantiate singletons
orderService = new OrderService ();
itemService = new ItemService ();

// inject dependencies
orderService.setItemService (itemService);
itemService.setOrderService (orderService);
查看更多
劳资没心,怎么记你
3楼-- · 2020-04-10 23:38

Can you separate out the "service" from the Constructor?

Or in other words, lets say you have an OrderService and it needs to consult its own personal copy of an ItemService. Will this ItemService instance need it's own copy of the OrderService to fulfill the request from the OrderService calling it?

Thus, it would be a sort of lazy initialization--don't create the new item unless and until you actually need it. And don't link up the additional service unless and until you need it.

Second idea: can you pass a copy as part of the Constructor?

e.g.:

//Constructor of OrderService public OrderService() 
{     orderDAO = DAOFactory.getDAOFactory().getOrderDAO();
      itemService = new ItemService(this); 
}
//Constructor of ItemService public ItemService(OrderService orderService) 
{     itemDAO = DAOFactory.getDAOFactory().getItemDAO();
      this.orderService = orderService; 
}

Or possibly in the reverse direction?

查看更多
太酷不给撩
4楼-- · 2020-04-10 23:44

Let the OrderService just do things with orders. Let the ItemService just do things with items. Then create a OrderItemService which combines the two.

查看更多
在下西门庆
5楼-- · 2020-04-10 23:52

Yes, the "singleton pattern" along with lazy initialisation will do. Don't initialise services in the constructor, but in static getters:

class OrderService {
  private static OrderService instance;
  private OrderDAO orderDAO;

  public OrderService() {
    orderDAO = DAOFactory.getDAOFactory().getOrderDAO();
  }

  public static synchronized OrderService getInstance() {
    if (instance == null) {
      instance = new OrderService();
    }

    return instance;
  }
}

As Jonathan stated, you can also inject services to other services, but that might not be needed. If synchronisation is prone to lead to a memory issue, you can resolve this using volatile. See also this answer here, elaborating on the "double-checked locking pattern" (be careful though, to get this right!)

查看更多
登录 后发表回答