2

My Factory has dependency on Repository. It calls many methods from Repository. Lets say something like this:

class CarFactory {
    private Repository repository;

    Car create() {
        detail = repository.findSomeDetail();
        if(detail.large()) {
          anotherDetail = repository.findAnotherDetail();
          return new Car(detail, anotherDetail);
        } else {
          anotherDetail = repository.findYetAnotherDetail();
          if(anotherDetail.expensive()) {
              cheapDetail = repository.findCheapDetail();
              return new SecondHandCar(cheapDetail);
          } else {
              smallDetail = repository.findSmallDetail();
              return new SmallCar(smallDetail);
          }
    }
}

It is hard to test, I need to mock lots of Repository methods. One problem is that Repository takes a lot of time to find something, so I can not find everything and then pick whatever I need. I need to call its methods only after certain criteria was met. Repository manages only single entity type, so I don't think it is a God Object. I tried to use Builder and ended up with:

class CarFactory {
    private Repository repository;
    private CarBuilder builder;

    Car create() {
        detail = repository.findSomeDetail();
        return builder.buildBasedOn(detail);
    }
}

However, it introduces new dependency in CarFactory and there is still problems inside Builder itself.

  • Would it be possible to refactor all `find` methods into something like one `Find(SomeFindParams)` method? – Guru Stron May 13 '22 at 13:46
  • 1
    Looks like you have working code, so https://codereview.stackexchange.com might be more appropriate for this question. – jaco0646 May 13 '22 at 14:02
  • @GuruStron Yes, it is possible, but I'm not sure if it helps. It will make it even harder to mock those methods – Viktor Klyestov May 13 '22 at 14:43
  • @jaco0646 you probably right. Didn't know about it. Can I move it there or I need to create new question? – Viktor Klyestov May 13 '22 at 14:45
  • @ViktorKlyestov I'm not very familiar with java mocking tools, but at least some in other languages allow easily to define the responses in succession which can be much easier/automatable in some cases. In the nutshell you will need either to mock all needed methods or setup underlying storage in correct way (i.e. moving this more to the realm of integration testing) if you need to test the scenario. So I would say that in this case you need to come up with some way for easier setup. – Guru Stron May 13 '22 at 14:53
  • @GuruStron yes it is easy to mock sequential calls. I thought about argument based mocking, that produces different results depending on argument, it is kind of pain. If there is no other magical trick to simplify my code I might use your suggestion. Could you post your comment as an answer, so that people could vote for it? – Viktor Klyestov May 13 '22 at 15:12
  • 1
    @GuruStron oh actually some of the methods have different return types... Collection and Optional. That is not visible in my oversimplified example – Viktor Klyestov May 13 '22 at 15:37
  • @ViktorKlyestov If your question is only regarding about mocking tool, then try to use [jfixture](https://github.com/FlexTradeUKLtd/jfixture). JFixture is an open source library based on the popular .NET library, [AutoFixture](https://github.com/AutoFixture/AutoFixture) – StepUp May 13 '22 at 16:03
  • [How do I move my own question to another Stack Exchange site?](https://meta.stackexchange.com/questions/85017) I guess you're supposed to flag it and ask the moderators to move it for you. – jaco0646 May 13 '22 at 21:32

1 Answers1

1

In my view, what can be done is creation of cache. You will preload all data from database. If there are many data, then preload only most recently used data. Take a look at LRU caching strategy. Read more about caching strategies.

If count of rows from database is not very big, you can create hashtables for each entity and it will be your cache. Then while creating an object, you can take your object from cache. It will be fast as gettting value from HashTable is O(1)

UPDATE:

If you want to have testable code, then you can use Dependency inversion Principle.

So you need to create interfaces of your dependencies and inject concrete objects to your factory.

Let me show an example. At first you need abstraction:

public interface ICarBuilder
{ 

}

Then implement interface in concrete type:

public class CarBuilder : ICarBuilder
{
}

And the next step is to introduce abstraction in your CarFactory:

class CarFactory
{
    private ICarBuilder _carBuilder;
    // other code is omitted for the brevity

    public CarFactory(ICarBuilder carBuilder) 
    {
        _carBuilder = carBuilder;
    }


    public Car create()
    {   
        return _carBuilder.buildBasedOn(detail);
        // other code is omitted for the brevity
    }
}

Then you can use like this:

CarFactory carFactory = new CarFactory(new CarBuilder());
StepUp
  • 36,391
  • 15
  • 88
  • 148
  • My question is not about performance. I have acceptable performance. I struggle here with code coupling and unit testing. – Viktor Klyestov May 13 '22 at 09:37
  • @ViktorKlyestov please, see my updated answer – StepUp May 13 '22 at 09:56
  • I don't think it helps. You just moved problem from CarFactory to CarBuilder. – Viktor Klyestov May 13 '22 at 10:38
  • I still need to mock all of that methods. I have never used UML, but let me try to explain https://imgur.com/a/9ogx9wY oops there should be CarBuilder in the middle – Viktor Klyestov May 13 '22 at 10:41
  • @ViktorKlyestov your question was about decreasing code coupling and unit testing. [Dependency Inversion Principle is about it](https://stackoverflow.com/questions/62539/what-is-the-dependency-inversion-principle-and-why-is-it-important). So this is a link from my answer. – StepUp May 13 '22 at 10:43
  • Well there are many methods to decrease coupling and yes DI is one of them. But it is still not clear which one to choose and how to apply it – Viktor Klyestov May 13 '22 at 10:47
  • @ViktorKlyestov In addition, you can use Factory pattern. I've already posted how to use Dependency Inversion Principle for your use case. Please, see the attached link to my reply. There are examples too. – StepUp May 13 '22 at 10:50