0

I have the following pattern:

An interface for serializing files, called IFileSerializer, which has multiple concrete implementations. Not every serializer can handle every file type, so we need to tell the application to know which one to use.

I also have a type from which all files that can be serialized extend from, let's call it IStorableFile. I have a requirement to use a different concrete implementation of IFileSerializer based on the type of IStorableFile.

Currently, I solve this problem by using a static factory class with a Build() method that accepts a file type and returns the appropriate serializer, but I'd rather do it using dependency injection.

What I'd ideally like is to be able to register different file types to use different serializer implementations.

The ultimate goal here is for the class injecting a serializer to be able to do something like this with DI:

public class MyConsumingClass 
{
    // DI will inject these, and will know what concrete implementation of serializer to use.
    public MyConsumingClass(
        IFileSerializer<FileTypeA> serializerForTypeA,
        IFileSerializer<FileTypeB> serializerForTypeB) 
    {
        // TODO use the serializers in the class
    }
}

I'd also like to throw a nice error if you try to inject a serializer for a file type that has not been registered. How can I accomplish something like this? I'm not really sure what to call this pattern in order to search it up, but I know I've seen stuff like this before in .NET.

Bassinator
  • 1,682
  • 3
  • 23
  • 50
  • 3
    So, what is stopping you from doing `services.AddSingleton, FileTypeASerializer>()`? (Or whatever Scope you need) – Fildor Feb 01 '23 at 19:42

1 Answers1

1

// I have a requirement to use a different concrete implementation of IFileSerializer based on the type of IStorableFile //

You might combine both thoughts.

You create a Factory, but you INJECT items the factory can produce.

I will "build upon" my answer here:

https://stackoverflow.com/a/74692116/214977

If you substitute your IFileSerializer for IShipper, and your "type of IStorableFile" for "string zipcode", then you can follow the example.

public interface IShipperFactory()
  public IShipper GetAnIShipper(string zipcode)

..

public class ShipperFactoryConcrete : (implements) IShipperFactory
    
    private readonly IEnumerable<IShipper> allShippers;

    public ShipperFactoryConcrete(IEnumerable<IShipper> allShippers)
    {
       this.allShippers = allShippers;  /* check for null or empty here , not shown */
    }

    public IShipper GetAnIShipper(string zipcode)
    {
        // let's say we have a crazy business rule where 
        // east coast is UspsShipper
        // west coast is FedExShipper
        // and if i can't determine if it is east-coast or west-coast.. provider a 'default' backup-plan concrete of UpsShipper
        // look through the injected IShippers to find a match, or else throw exception.

    }

So now you will IoC register your IShipperFactory, ShipperFactoryConcrete

and you'll inject the IShipperFactory anywhere you need an IShipper.

but you call IShipperFactory.GetAnIShipper.. and supply it a zip code value.

......

Basically, your ShipperFactoryConcrete becomes your encapsulated logic to handle your "Which IFileSerilizer do I give back for a specific IStorableFileType"

which is the learning example equivalent of:

the ShipperFactoryConcrete becomes the encapsulated logic to handle the question of "Which IShipper do I give back for a specific zipcode"

granadaCoder
  • 26,328
  • 10
  • 113
  • 146