Currently I'm working within a framework that was developed by someone else at a company I'm doing work for so I have some restrictions with regards to the architecture. I was hoping there is a design pattern or some way I can handle my logging with the way it's set up now, without breaking abstractions.
So, there is a TestCase
class. This class is the base for all test cases I write, and any test case I develop must inherit from it. The test case has logging methods, such as LogInfo
, LogConfirmation
etc.
Now I have another class called SignalGroup
which contains one or more Signals. The SignalGroup
has a Set method which abstracts the logic behind how the one or more values are set. It does this by knowing which mode the system is in etc etc. So for instance, keeping it simple, a SignalGroup
's set method may just be the following in a mode that will result in setting all signals in a group to a value:
public void Set(int value){
foreach (Signal signal in Signals){
signal.Set(value)
}
}
Now, in that set method, I need to log the value that each signal was set to, using the LogInfo
method from the TestCase
base class. My problem is, now I either need to
Signal
or SignalGroup
know about TestCase
base so it can keep its abstractions around setting the value but also call the LogInfo
methodSignalGroup
so I can implement the logging at its layer. But this means I have to make the TestCase
include logic about the individual signals that I really would like to be abstracted away.Is there any way I can make the logging available to the SignalGroup
with a bit looser coupling so it doesn't need to directly know about the TestCase
class?
As a cross-cutting concern, logging should be implemented using the Decorator design pattern.
Make the Set
method virtual, preferably by defining it as an interface method, e.g.
public interface ISignalGroup
{
void Set(int value);
}
Then implement a LoggingSignalGroup
like this:
public class LoggingSignalGroup : ISignalGroup
{
public LoggingSignalGroup(TestCase logger, ISignalGroup inner)
{
Logger = logger;
Inner = inner;
}
public TestCase Logger { get; }
public ISignalGroup Inner { get; }
public void Set(int value)
{
Logger.LogInfo(value);
Inner.Set(value);
}
}
Use LoggingSignalGroup
to decorate the 'real' SignalGroup
class, e.g. new LoggingSignalGroup(logger, new SignalGroup())
...
If you can't define an interface like ISignalGroup
, you can instead define Adapters that work this way.