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.
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
?
Actually
ModifiedProperties
is an implementation detail. Do anyILogFactory
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 likeICanTrackPropertyChanges
:And
ICanTrackPropertyChanges
should be implemented by your entities. Thus,UpdateLogFactory
can make the following assertion:This way, you don't need to provide
ModifiedProperties
as an implementation detail ofILogFactory
, but you believe that entities can track property changes if they implement an interface.You can enforce this using generic constraints:
...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 hasModifiedProperty
because handled entities are guaranteed to has the wholeIEnumerable<string>
of modified properties at compile time!