Skip to main content
3 of 3
deleted 1 character in body
Flater
  • 56.3k
  • 8
  • 106
  • 157

Responding to your questions

How should I tell the factory which strategy must be used?

Usually, you straight up tell the factory what you want, there's not much intricacy here. Whoever calls the factory is expected to either know what they're asking for (and they have a way to explicitly ask for what they want), or they don't know at all and rely on the factory to tell them what they must use.

I assume you're in the former scenario since you're trying to tell the factory what to use, which means I'd expect you to either have a method for every strategy you can create:

circularStrategyFactory.createFooStrategy();
circularStrategyFactory.createBarStrategy();
circularStrategyFactory.createBazStrategy();

Or you have a single method with a way to explicitly ask for what you want, e.g. using an enum:

circularStrategyFactory.createStrategy(StrategyTypes.Foo);
circularStrategyFactory.createStrategy(StrategyTypes.Bar);
circularStrategyFactory.createStrategy(StrategyTypes.Baz);

Is it ok to inject things to a class? from where?

Injecting things into a class is perfectly fine. This may sound obvious, but you inject something into a class from whever you're handling the class. That's the whole point of injection, it allows the caller to decide what they want to do.

I would set the info about which strategy must be used up in the GsonBuilder* and inject it to the abstract CLASS TypeAdapter (as a static field, to the class, not to the instances) but there is something that stops me from doing that, a spider-alert...

Injecting something into a static field make my spidey sense flare up as well. Injection tends to imply that different possible strategies can be used, but a static field is a global value. This seems to contradict itself.

What happens if you have two parts of your application which both use this adapter, but want to use different strategies? Using a static field, when one caller sets their preferred strategy, everyone now has to (unexpectedly) deal with this strategy instead the one they had set for themselves beforehand.

I'm not quite sure what it is you're trying to achieve here. Based on your intention to inject, I would avoid using statics. But maybe your particular use case makes sense for statics (e.g. a largely fixed and inherently global strategy, such as an application-wide setting).


My suggested answer

This depends on whether you want to set the strategy when initializing the object, or just before you actually serialize some data. The latter is relevant if you want the same adapter to use different strategies on a per-request basis.


If you want to do it during initialization, this is a prime example of dependency injection. Pass the strategy as a constructor argument:

public abstract class TypeAdapter<T> 
{
    private readonly CircularReferenceStrategy<T> circularStrategy;

    public TypeAdapter(CircularReferenceStrategy<T> circularStrategy)
    {
        this.circularStrategy = circularStrategy;
    }
}

This means your derived class (which you'll always have since this is an abstract class) can decide what the strategy is. The choice of strategy can either be dynamic or fixed.

public class DynamicStrategyTypeAdapter<T> : TypeAdapter<T>
{
    //Uses whatever strategy it's told to use.

    public DynamicStrategyTypeAdapter(CircularReferenceStrategy<T> circularStrategy) : base(circularStrategy)
    {
    }
}

public class FixedStrategyTypeAdapter<T> : TypeAdapter<T>
{
    //Always uses FooStrategy<T> as a strategy

    public FixedStrategyTypeAdapter() : base(new FooStrategy<T>())
    {
    }

    //You could also use a factory method here:

    public FixedStrategyTypeAdapter() : base(new CircularStrategyFactory().CreateCircularStrategyFor<T>())
    {
    }
}

If you want to set the strategy just before writing, then that suggests that either the strategy decision is made externally to the class, or that the strategy decision hinges on the internal state of the class.

If it's an external decision, then you simply need a method parameter. This is still a form of dependency injection, albeit on a method level.

public void write(JsonWriter out, T value, CircularReferenceStrategy<T> circularStrategy) throws IOException 
{
    circularStrategy.write(this, out, value);
}

Note: you could also pass a parameter of a different type (e.g. an enum) which you then use to fnd the correct strategy via e.g. a factory). The principle is the same.

If it hinges on the internal state of the class, create a private method that chooses the correct strategy (or, alternatively, a factory. If it fits your use case):

public void write(JsonWriter out, T value) throws IOException 
{
    var circularStrategy = chooseStrategy();

    circularStrategy.write(this, out, value);
}

private CircularReferenceStrategy<T> chooseStrategy()
{
    // ...
}
Flater
  • 56.3k
  • 8
  • 106
  • 157