Abstract Factory: When a concrete factory needs additional data

253 views Asked by At

Consider the following architecture designed for logging operations Add/Update that are being done on an instance of Entity.

LogFactory is an Abstract Factory and receives two factories for Add and Update via constructor using DI. Both concrete factories AddLogFactory and UpdateLogFactory implement ILogFactory in order to produce an ILog. They both need an Entity to collect log-able data. Well, I hope everything is clear and you admit Abstract Factory pattern is the right solution so far.

enter image description here

Problem:

UpdateLogFactory is an exception because it needs additional data: ModifiedProperties to create a before/after string (meanwhile its siblings i.e. other types implementing ILogFactory don't need this kind of data; an Entity suffices them). In order to not break the ILogFactory, I decided to give ModifiedProperties to UpdateLogFactory via its constructor. It solves my problem but I feel I'm cheating somehow; I'm pretending UpdateLogFactory is similar to others while it is actually not.

If someone wants to replace UpdateLogFactory with another concrete type how does he know it needs ModifiedProperties?

2

There are 2 answers

4
Matías Fidemraizer On

Actually ModifiedProperties is an implementation detail. Do any ILogFactory need to produce that before and after format? I don't think so.

Anyway, I believe that ModifiedProperties shouldn't be injected but they should be part of an interface like ICanTrackPropertyChanges:

public interface ICanTrackPropertyChanges
{
      IEnumerable<string> ModifiedPropertyNames { get; }
}

And ICanTrackPropertyChanges should be implemented by your entities. Thus, UpdateLogFactory can make the following assertion:

ICanTrackPropertyChanges trackable = entity as ICanTrackPropertyChanges;

// Design by contract wins!
Contract.Assert(trackable != null);

// And now access trackable.ModifiedProperties and produce the 
// customized message for entity updates

This way, you don't need to provide ModifiedProperties as an implementation detail of ILogFactory, but you believe that entities can track property changes if they implement an interface.

You can enforce this using generic constraints:

public interface IUpdateLogFactory<TEntity> : ILogFactory
    where TEntity : class, ICanTrackPropertyChanges
{
}

...and use this interface to implement loggeable entity updates.

In addition, you get an improvement: IUpdateLogFactory<TEntity> implementations won't need to check if an entity has ModifiedProperty because handled entities are guaranteed to has the whole IEnumerable<string> of modified properties at compile time!

5
Fuhrmanator On

Are you confusing Abstract Factory and Factory Method?

Your diagram isn't Abstract Factory as defined by GoF:

Abstract Factory GoF

You're missing:

  • Two or more abstract Product types (A, B)
  • Two or more createProduct methods (A, B).

If you were to apply the pattern to your problem, you'd be able to have the extra parameter on the createUpdateLog method (as shown below), but then you will have problems with several classes that don't fit (indicated in pink):

PlantUML diagram of Abstract Factory in this context

I suspect you're trying to use the Factory Method pattern, which only has one signature and is why you're having trouble with the "exceptional" parameter in the case of UpdateLogs.

Edit

Here's the Factory Method pattern:

Factory Method Pattern from GoF

As you see, each concrete factory only deals with one concrete product.

Now I'll plug your design from the question to this pattern:

Factory Method applied