I've got a library which has two input formats for the object model described by the library. I currently use an event subscription model for raising errors/warnings/verbose messages to the end user of the library. This hasn't proven to be the cleanest model, and I was wondering if there was a relevant design pattern or something similar found in the .Net Framework (when in Rome) to better handle this situation.
// Rough outline of the current code
public abstract class ModelReader : IDisposable
{
public abstract Model Read();
public event EventHandler<MessageAvailableEventArgs> MessageAvailable;
protected virtual void RaiseError(string message)
{
var handler = this.MessageAvailable;
if (handler != null)
{
handler(this, new MessageAvailaibleEventArgs(
TraceEventType.Error, message);
}
}
}
Edit: some clarification. The Read routine will already fail fast on all Fatal errors using an exception. The messages are being logged to potentially multiple sources on the user's end, thus any pattern should avoid limiting the number of potential sources.
I can give you a real world example. The Html Agility Pack library is a parsing library. It simply defines a list of parsing errors on the the reader class. Extending your example, it would be something like:
public abstract class ModelReader
{
private List<ParseError> _errors = new List<ParseError>();
private bool _throwOnError;
public ModelReader()
:this(true)
{
}
public ModelReader(bool throwOnError)
{
_throwOnError = throwOnError;
}
// use AddError in implementation when an error is detected
public abstract Model Read();
public virtual IEnumerable<ParseError> Errors
{
get {return _errors;}
}
protected virtual void AddError(ParseError error)
{
if (_throwOnError) // fail fast?
throw new ParseException(error);
_errors.Add(error);
}
}
public class ParseError
{
public ParseError(...)
{
}
public ParseErrorCode Code { get; private set; }
public int Line { get; private set; }
public int LinePosition { get; private set; }
public string Reason { get; private set; }
public string SourceText { get; private set; }
public int StreamPosition { get; private set; }
}
public enum ParseErrorCode
{
InvalidSyntax,
ClosingQuoteNotFound,
... whatever...
}
public class ParseException: Exception
{
...
}
And you still can add an event if the library caller wants on-the-fly events.
You seem to want a composable validator where your users can plug in their own logic to flag specific non fatal errors as either fatal, warning or informational messages. Throwing an exception does not fit the bill since you have left the method already if another part wanted to transform it into a warning but wanted to continue parsing. This sounds much like the two pass exception handling model in Windows for Structured Exception Handling.
It basically goes like
Register as many exception handlers as you wish
When a problem is detected in pass all handlers are asked (no exeption throwing yet!) which one wants to handle the error. The first one which says yes will become the actual handler which decides what to do.
When a handler was found that can handle we come to pass 2 and call it. This time it is exception throwing time or not. It is entirely up to the handler.
The beauty of this two pass model is that during the first pass all handlers are asked but you still are able to continue parsing where you left. No state has been destroyed yet.
In C# you have of course much more freedom to turn this into a more flexible error transformation and reporting pipeline where you can transform for a strict reader e.g. all warnings to an error.
Which route you need to go depends on how your library is beeing used and how skilled the users of your library are and how many service requests you want to handle because some users make dumb errors. There is always a tradeoff between beeing as strict as possible (possibly too strict and you get a hard to use library) or too relaxed (nice parser but skips silently half of my input due to internal errors).
The Windows SDK Libraries are sometimes pretty hard to use because the engineers there optimize more towards less serivce calls. They throw a Win32 error code or HResult at you and you have to figure out which principle (memory alignment, buffer size, cross threading, ...) you have violated.
I think event subscription mode is OK. But you can consider interface instead. This might give you more flexibility. Something like this:
public interface IMessageHandler
{
void HandleMessage(object sender, MessageAvailaibleEventArgs eventArgs);
}
public abstract class ModelReader : IDisposable
{
private readonly IMessageHandler handler; // Should be initialized somewhere, e.g. in constructor
public abstract Model Read();
public event EventHandler<MessageAvailableEventArgs> MessageAvailable;
protected virtual void RaiseError(string message)
{
MessageAvailaibleEventArgs eventArgs =
new MessageAvailaibleEventArgs(TraceEventType.Error, message);
this.handler.HandleMessage(this, eventArgs);
}
}
So now you can use any implementation of message handler, for exmaple your event subscription one:
public class EventMessageHandler : IMessageHandler
{
public event EventHandler<MessageAvailaibleEventArgs> MessageAvailable;
public void HandleMessage(object sender, MessageAvailaibleEventArgs eventArgs)
{
var handler = this.MessageAvailable;
if (handler != null)
{
handler(this, new MessageAvailaibleEventArgs(
TraceEventType.Error, message);
}
}
}
Your current approach isn't the cleanest model because it has two contradictory execution styles, pull (reading the input) and push (handling error callbacks), and that means both your reader and its clients require more state management to provide a coherent whole.
I recommend you avoid the XmlReader-like interface, and use the visitor pattern to push the input data and errors to the client application.
If there are error that stop the library from doing it's work I would use exception.
If this is strictly for tracing and instrumentation I would either go with your pattern or a TextWriter pattern, where the consumer would pass in a text writer and you would write your trace info to that, the only problem with that is you can only have one external subscriber. but it results in a slightly cleaner code.
public TextWriter Log { get; set; }
private void WriteToLog(string Message)
{
if (Log != null) Log.WriteLine(message);
}
I know you mentioned that you may have several subscribers, so Event handlers and an Injected interface call are both good solutions as already mentioned above. For completeness I will also offer providing an optional Read() parameter as a Func. for example:
void Read(Func<string, bool> WarningHandler)
{
bool cancel = false;
if (HasAWarning)
cancel = WarningHandler("Warning!");
}
Then of course you could do whatever you wanted in the Func delegate, like divy the warning out to several sources. But when coupled with the Event model allows you to handle all Warnings in a general way (like a logger perhaps), and use the Func for individual specialized actions and control flow (if necessary).
Related
I'm building a retry system that allows me to attempt code multiple times before giving up (useful for things like establishing connections over the network). With this, the basic code I usually copy and paste everywhere as a base is:
for (int i = 0; i < attemptThreshold; i++) {
try {
...
break;
} catch (Exception ex) { ... }
}
There's quite a bit of logging code within the try and catch blocks that can be delegated by refactoring to ensure consistency. It's straightforward to refactor it and delegate the work of retrying:
public static class DelegateFactory {
public static bool DelegateWork<TIn, TOut>(Func<TIn, TOut> work, int attemptThreshold, TIn input, out TOut output) {
if (work == null)
throw new ArgumentException(...);
for (int i = 0; i < attemptThreshold; i++) {
try {
OnMessageReceived?.Invoke(work, new FactoryEventArgs("Some message..."));
output = work(input);
return true;
} catch (Exception e) { OnExceptionEncountered?.Invoke(work, new FactoryEventArgs(e)); }
}
return false;
}
public static event EventHandler<FactoryEventArgs> OnMessageReceived;
public static event EventHandler<FactoryEventArgs> OnExceptionEncountered;
}
Calling it is also straightforward:
DelegateFactory.DelegateWork((connectionString) => {
using (SqlConnection conn = new SqlConnection(connectionString))
conn.Open();
}, 10, "ABC123", out bool connectionMade);
Console.WriteLine($"Connection Made: {(connectionMade ? "Yes" : "No")}");
Keep in mind, the above code excludes the definition for FactoryEventArgs, but it's just a class that takes an object as an argument (for simplicity in prototyping). Now, what I have above works just fine, but I wanted to add a way to allow the caller to post messages using an event for the factory's subscriber to log (the whole single responsibility thing, which I'm still learning, by the way, so be gentle). The idea is to create an event called OnMessageReceived and a public method called PostMessage that can only be called from code being executed by the factory. If the call is made from any other place then it would throw an InvalidOperationException to signify that the call was invalid. My first though to accomplish this is to use the call stack to my advantage:
using System.Diagnostics; // Needed for StackFrame
...
public static void PostMessage(string message) {
bool invalidCaller = true;
try {
Type callingType = new StackFrame(1).GetType();
if (callingType == typeof(DelegateFactory))
invalidCaller = false;
} catch { /* Gracefully ignore. */ }
if (invalidCaller)
throw new InvalidOperationException(...);
OnMessageReceived?.Invoke(null, new FactoryEventArgs(message));
}
However, I don't know for sure that this would prove to be reliable. The idea though is to allow the work to also send messages to the subscriber, but that may be a moot point because the object containing the work could just raise it's own OnMessageReceived event instead. I just don't like the idea of the exceptions being sent to the subscriber one way, and messages going out another. Maybe I'm just being picky? Starting to have a smell, the more I think on it.
EXAMPLE USE CASE
public class SomeObjectUsingTheFactory {
public bool TestConnection() {
DelegateFactory.DelegateWork((connectionString) => {
// Completely valid.
DelegateFactory.PostMessage("Attempting to establish a connection to SQL server.");
using (SqlConnection conn = new SqlConnection(connectionString))
conn.Open();
}, 3, "ABC123", out bool connectionMade);
// This should always throw an exception.
// DelegateFactory.PostMessage("This is a test.");
return connectionMade;
}
}
public class Program {
public static void Main(string[] args) {
DelegateFactory.OnMessageReceived += OnFactoryMessageReceived;
var objNeedingFactory = new SomeObjectUsingTheFactory();
if (objNeedingFactory.TestConnection())
Console.WriteLine("Connected.");
}
public static void OnFactoryMessageReceived(object sender, FactoryEventArgs e) {
Console.WriteLine(e.Data);
}
public static void OnFactoryExceptionOccurred(object sender, FactoryEventArgs e) {
string errorMessage = (e.Data as Exception).Message;
Console.WriteLine($"An error occurred. {errorMessage}");
}
}
In the example above, if we assume the connection continues to fail, the output should be:
Attempting to establish a connection to SQL server.
An error occurred. {errorMessage}
Attempting to establish a connection to SQL server.
An error occurred. {errorMessage}
Attempting to establish a connection to SQL server.
An error occurred. {errorMessage}
If it succeeds on the second attempt it should be:
Attempting to establish a connection to SQL server.
An error occurred. {errorMessage}
Attempting to establish a connection to SQL server.
Connected.
How can I ensure the method PostMessage is only called by code being executed by the factory?
NOTE: I'm not against changing the design if it's introducing bad practice. I'm completely open to new ideas.
COMPILER ERRORS: Also, any compilation errors in here are strictly oversights and typos. I manually typed this question up as I tried my best to work my way through the problem. If you encounter any issues, let me know and I'll fix them promptly.
You can do away with the stack-based security by introducing a context object that provides access to the event.
But first, a few notes. I'm not going to speak to the merits of this design because that's subjective. However, I will address some terminology, naming, and design matters.
.NET's naming convention for events does not includethe "On" prefix. Rather, the method that raises the event (marked private or protected virtual, depending on whether you can inherit the class) has the "On" prefix. I've followed this convention in the code below.
The name "DelegateFactory" sounds like something that create delegates. This does not. It accepts a delegate and you're using it to perform an action within a retry loop. I'm having a tough time word-smithing this one, though; I've called the class Retryer and the method Execute in the code below. Do with that what you will.
DelegateWork/Execute return a bool but you never check it. It's unclear if that's an oversight in the example consumer code or a flaw in this thing's design. I'll leave it to you to decide, but because it follows the Try pattern to determine if the output parameter is valid, I'm leaving it there and using it.
Because you're talking about network-related actions, consider writing one or more overloads that accept an awaitable delegate (i.e. returns Task<TOut>). Because you can't use ref or out parameters with async methods, you'll need to wrap the bool status value and the return value of the delegate in something, such as a custom class or a tuple. I will leave this as an exercise to the reader.
If an argument is null, make sure you throw ArgumentNullException and simply pass it the name of the argument (e.g. nameof(work)). Your code throws ArgumentException, which is less specific. Also, use the is keyword to ensure you're doing a reference equality test for null and not accidentally invoking overloaded equality operators. You'll see that in the code below, as well.
Introducing a Context Object
I'm going to use a partial class so that the context is clear in each snippet.
First, you have the events. Let's follow the .NET naming convention here because we want to introduce invoker methods. It's a static class (abstract and sealed) so those will be private. The reason for using invoker methods as a pattern is to make raising an event consistent. When a class can be inherited and an invoker method needs to be overridden, it has to call the base implemention to raise the event because the deriving class doesn't have access to the event's backing storage (that could be a field, as in this case, or perhaps the Events property in a Component-derived type where the key used on that collection is kept private). Although this class is uninheritable, it's nice to have a pattern you can stick to.
The concept of raising the event is going to go through a layer of semantic translation, since the code that registers the event handler may not be the same as the code that calls this method, and they could have different perspectives. The caller of this method wants to post a message. The event handler wants to know that a message has been received. Thus, posting a message (PostMessage) gets translated to notifying that a message has been received (OnMessageReceived).
public static partial class Retryer
{
public static event EventHandler<FactoryEventArgs> MessageReceived;
public static event EventHandler<FactoryEventArgs> ExceptionEncountered;
private static void OnMessageReceived(object sender, FactoryEventArgs e)
{
MessageReceived?.Invoke(sender, e);
}
private static void OnExceptionEncountered(object sender, FactoryEventArgs e)
{
ExceptionEncountered?.Invoke(sender, e);
}
}
Side note: You might want to consider defining a different EventArgs-derived class for ExceptionEncountered so you can pass the whole exception object for that event rather than whatever string data you cobble together from it.
Now, we need a context class. What will be exposed to the consumer is either an interface or an abstract base class. I've gone with an interface.
The semantic translation from "post a message" to "a message was received" is aided by the fact that FactoryEventArgs is unknown to the lambda that's posting the message. All it has to do is pass the message as a string.
public interface IRetryerContext
{
void PostMessage(string message);
}
static partial class Retryer
{
private sealed class RetryerContext : IRetryerContext
{
public void PostMessage(string message)
{
OnMessageReceived(this, new FactoryEventArgs(message));
}
}
}
The RetryerContext class is nested in the Retryer class (and private) for two reasons:
It needs access to at least one of the invoker methods that's private to the Retryer class.
Given the first point, it simplifies things by not exposing a nested class to the consumer.
Generally speaking, nested classes should be avoided, but this is one of those things that they're exactly designed to do.
Also note that the sender is this, i.e. the context object. The original implementation was passing work as the sender, which is not what's raising (sending) the event. Because it's a static method in a static class, there was no instance to pass before and passing null probably felt dirty; strictly speaking, the context is still not what's raising the event, but it's a better candidate than a delegate instance. It will also be passed as the sender when being used inside of Execute.
The implementation needs to be modified just slightly to include the context when invoking work. The work argument is now a Func<TIn, IRetryerContext, TOut>.
static partial class Retryer
{
public static bool Execute<TIn, TOut>(Func<TIn, IRetryerContext, TOut> work, int attemptThreshold, TIn input, out TOut output)
{
if (work is null)
throw new ArgumentNullException(nameof(work));
DelegationContext context = new DelegationContext();
for (int i = 0; i < attemptThreshold; i++)
{
try
{
OnMessageReceived(context, new FactoryEventArgs("Some message..."));
output = work(input, context);
return true;
}
catch (Exception e)
{
OnExceptionEncountered(context, new FactoryEventArgs(e.Message));
}
}
output = default;
return false;
}
}
OnMessageReceived is called from two different places: Execute and PostMessage, so if you ever need to change how the event is raised (maybe some add logging), it only needs to be changed in one place.
At this point, the problem of preventing unwanted message posting is solved because:
The event can't be raised arbitrarily since anything that calls it is private to the class.
A message can only be posted by something that has been given the ability to do so.
Small nit-pick: Yes, the caller could capture a local variable and assign the context to the outer scope, but then someone could also use reflection to find the event delegate's backing field and invoke it whenever they want, too. There's only so much you can reasonably do.
Finally, the consumer code needs to include the context in the lambda's parameters.
Here's your example use case, modified to use the implementation above. The lambda returns a string, the connection's current database, as the result of the operation. That's distinct and separate from the true/false returned to indicate whether it was a success after attemptThreshold attempts, which is now what's assigned to connectionMade.
public class SomeObjectUsingTheFactory
{
public bool TestConnection(out string currentDatabase)
{
bool connectionMade = Retryer.Execute((connectionString, context) =>
{
// Completely valid.
context.PostMessage("Attempting to establish a connection to SQL server.");
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
return conn.Database;
}
}, 3, "ABC123", out currentDatabase);
// Can't call context.PostMessage here because 'context' doesn't exist.
return connectionMade;
}
}
public class Program
{
public static void Main(string[] args)
{
Retryer.MessageReceived += OnFactoryMessageReceived;
var objNeedingFactory = new SomeObjectUsingTheFactory();
if (objNeedingFactory.TestConnection(out string currentDatabase))
Console.WriteLine($"Connected to '{currentDatabase}'.");
}
public static void OnFactoryMessageReceived(object sender, FactoryEventArgs e)
{
Console.WriteLine(e.Data);
}
public static void OnFactoryExceptionOccurred(object sender, FactoryEventArgs e)
{
string errorMessage = (e.Data as Exception).Message;
Console.WriteLine($"An error occurred. {errorMessage}");
}
}
As further exercises, you could also implement other overloads. Here are some examples:
An overload for a lambda that doesn't need to call PostMessage and therefore doesn't need the context. This has the same type for the dowork parameter as your original implementation.
public static bool Execute<TIn, TOut>(Func<TIn, TOut> work, int attemptThreshold, TIn input, TOut output)
{
return Execute((arg, _ /*discard the context*/) => work(arg), attemptThreshold, input, out output);
}
Overloads for lambdas that don't need need to return a value in an output parameter, and therefore use Action delegates instead of Func delegates.
public static bool Execute<TIn>(Action<TIn, IRetryerContext> work, int attemptThreshold, TIn input)
{
// A similar implementation to what's shown above,
// but without having to assign an output parameter.
}
public static bool Execute<TIn>(Action<TIn> work, int attemptThreshold, TIn input)
{
return Execute((arg, _ /*discard the context*/) => work(arg), attemptThreshold, input);
}
I have a class from a third-party assembly (so I can't edit it):
public class MyClass
{
private bool _loggedIn;
public void Login() {_loggedIn = true;}
public void Logout() {
if (!_loggedIn) throw new InvalidOperationException();
_loggedIn = false;
}
}
Now, suppose I have an instance of MyClass (for which I don't know _loggedIn), and I need call LogOut. Which of the following methods of avoiding a fatal exception will generally be faster? (any other method would be fine too):
To call LogOut, and if _loggedIn == false, just catch the exception
To use reflection to check that _loggedIn == true, and only call LogOut if so
It depends on the invariants you expect to see in your application.
1. If you expect to have a lot of MyClass having different state(logged in, logged off), then it is better to avoid overhead of exception (because exception is Exceptional situation) and use some specific public IsLoggedIn property (obviously to avoid Reflection) or some TryXxxxx-like methods.
And even if you can't modify the original code no one stops you from wrapping it:
public class MyWrappedClass
{
public Boolean IsLoggedIn {get; private set;}
private MyClass m_Log;
public MyWrappedClass ()
{
this.m_Log = new MyClass();
this.IsLoggedIn = false;
}
public void Log()
{
try
{
this.m_Log.LogIn();
this.IsLoggedIn = true;
}
catch
{
this.IsLoggedIn = false;
}
}
public void LogOut()
{
try
{
this.m_Log.LogOut();
this.IsLoggedIn = false;
}
catch
{
this.IsLoggedIn = true;
}
}
}
You could even go further and implement IDisposable interface with it to avoid manual LogIn-LogOut management:
public class MyWrappedClass
{
private class LogSessionToken : IDisposable
{
private MyWrappedClass parent;
public LogSessionToken (MyWrappedClass parent)
{
parent.LogIn();
}
public void Dispose()
{
parent.LogOut();
}
}
public IDisposable LogSession()
{
return new LogSessionToken (this);
}
// ...
}
And use it like
using (var logToken = wrappedInstance.LogSession)
{
// do the work.
} // No need to worry about manual LogOut
2. If you expect to use only few of MyClass in a proper fashion, then it would be a better idea to not handle exception at all - if something wrong happened then it is some programming error thus the program shall be terminated.
First, if your class doesn't expose at least a read-only property for LoggedIn, there sounds like a fairly large design flaw.
For speed, using reflection will generally be faster, particularly if you cache the FieldInfo or build a Func<bool> using System.Linq.Expressions. This is because Exceptions collect lots of debug information when thrown, including a StackTrace, which can be expensive.
As with anything, though, it is often best to test such operations, as there are sometime optimizations or other factors that may surprise you.
If the pattern if (CanFoo) Foo(); appears very much, that tends to imply very strongly that either:
A properly-written client would know when it can or cannot call Foo. The fact that a client doesn't know suggest that it's probably deficient in other ways.
The class exposing CanFoo and Foo should also expose a method which will Foo if possible and appropriate (the method should throw if unable to establish expected post-conditions, but should return silently if the post-conditions were established before the call)
In cases where a class one does not control should provide such a method but doesn't, the cleanest approach may be to write one's own wrapper method whose semantics mirror those the missing method should have had. If a later version of the class implements the missing method, changing one's code to use that implementation may be easier than refactoring lots of if (CanFoo) constructs.
BTW, I would suggest that a properly-designed class should allow calling code to indicate whether it is expecting a transition from logged-in state to logged-out state, or whether it wants to end up in logged-out state but it doesn't care how it gets there. Both kinds of semantics have perfectly legitimate uses; in cases where the first kind would be appropriate, having a LogOut method throw an exception if called on a closed session would be a good thing, but in cases where client code merely wants to ensure that it is logged out, having an EnsureLoggedOut method that could be invoked unconditionally would be cleaner than having to add extra client-side code for that purpose.
I have been reading a book called Clean Code A Handbook of Agile Software Craftsmanship. The author in the book motivates that a switch statement should be avoided and if it cannot be avoided it should be relegated to factory methods. I have a connection object which is receiving various PDUs (protocol data units). The PDUs vary and they can be received in any order. So if I have a method for example:
public BasePdu ReceiveNext();
because I cannot tell what the packet type is until it has been fully received. In the header of the PDU is an identifier as to which type it should be. This means that the calling method is going to have figure out what the type of the PDU is and based on that call the relevant method to handle it. This sounds like a perfect example for a switch statement. The object that contains the connection I would ideally like to have two threads. One for receiving PDUs and another for servicing a queue of PDUs to be sent.
Now I know that you cannot follow every bit of good advice and that there are just some circumstances which are the exception to the rule. Is this one of them? Or is there a way around this that I just have not yet thought of.
UPDATE:
I hear what a lot of people are saying by creating subclasses of response handlers. The issue is that the containing object has a lot of context and additional information that the handlers would need for example lookups and throttling etc etc. To inject all of this information into subclasses of handlers would be quite a chore to maintain and would also split a lot of logic up when it feels better to be encapsulated in the object that it is in now.
Personally I wouldn't worry about it too much; if it looks like a good place for a switch statement use one. On the other hand this also looks like a situation where you could use a factory method if each PDU type is handled by a class rather than a method. And, accoding to your book, you're allowed you to use switch statements then
Simply create a PDUParserFactory which create the parser based on a PDU type using switch statements on the PDU type identifier. This is the case where the book says it's ok :)
Update: One possible approach
class BasePDU
{
string Name { get; set; }
...
}
class PDUType1 : BasePDU
{
...
}
...
class PDUReceiver
{
public event EventHandler<PDUReceivedEventArgs> PDUReceived;
private void ParsePDU(byte[] data)
{
BasePDU pdu;
switch (byte[0]) // PDU type selector
{
.... parse PDU based on type
}
OnPDUReceived(pdu);
}
private void OnPDUReceived(BasePDU pdu)
{
var handler = PDUReceived;
if (handler != null)
{
handler(this, new PDUReceivedEventArgs(pdu));
}
}
}
Then you can attach listeners to the event:
pduReceiver.PDUReceived += BaseHandler;
pduReceiver.PDUReceived += PDUType1Handler;
...
void PDUType1Handler(object sender, PDUReceivedEventArgs e)
{
// only care about PDUType1
if (e.PDU.GetType() != typeof(PDUType1))
return;
....
}
Alternatively you could also create a event handler dictionary in the receiver, mapping a pdu type to event handlers and then let handlers register for specific types only. This way not all handlers would be called for each received PDU.
Instead of heaving a PDU type hierarchy you could also just have a:
class PDU
{
public PDUType PDUType { get; }
public byte[] PDUData { get }
}
then register handlers in the receiver for each PDUType and let the handler do whatever it wants with the data.
It's hard to give more concrete advice without knowing what exactly you want to do with your received packets.
If I understand your question correctly you have really two questions:
How to create the correct PDU's when you've received the name without using switch.
Create a simple factory by using a dictionary Dictionary<string, Func<PduBase>>
How the method calling public BasePdu ReceiveNext(); can handle it properly without using switch
Do not use a RecieveNext method. Create a AddPduHandler<T>(IPduHandler<T> handler) where T : PduBase method to the class receiving all PDU's. Store all handlers in a dictionary with the type as key: Dictionary<Type, Delegate>
Storing a delegate is kind of a trick since you can not work with the typed interface in the receiving class.
Update
This solution do not break Liskovs Substitution Principle which all implementations using switch do. This means that this class will work no matter how many different types of PDU's that you have.
It's also easier to test your application since each handler is isolated from everything else.
The bonus side is that everything is Typed (except the reader class) which will make it easier of find errors instead of working with casting magic or such.
public class Receiver
{
Dictionary<Type, MethodMapping> _handlers = new Dictionary<Type, MethodMapping>();
Dictionary<string, Func<PduBase>> _factories = new Dictionary<string, Func<PduBase>>();
// Small container making it easier to invoke each handler
// also needed since different generic types cannot be stored in the same
// dictionary
private class MethodMapping
{
public object Instance { get; set; }
public MethodInfo Method { get; set; }
public void Invoke(PduBase pdu)
{
Method.Invoke(Instance, new[] {pdu});
}
}
// add a method used to create a certain PDU type
public void AddFactory(string name, Func<PduBase> factoryMethod)
{
_factories.Add(name, factoryMethod);
}
// register a class that handles a specific PDU type
// we need to juggle a bit with reflection to be able to invoke it
// hence everything is type safe outside this class, but not internally.
// but that should be a sacrifice we can live with.
public void Register<T>(IPduHandler<T> handler) where T : PduBase
{
var method = handler.GetType().GetMethod("Handle", new Type[] { typeof(T) });
_handlers.Add(typeof(T), new MethodMapping{Instance = handler, Method = method});
}
// fake that we've received a new PDU
public void FakeReceive(string pduName)
{
// create the PDU using the factory method
var pdu = _factories[pduName]();
// and invoke the handler.
_handlers[pdu.GetType()].Invoke(pdu);
}
}
public interface IPduHandler<in T> where T: PduBase
{
void Handle(T pdu);
}
public class TempPdu : PduBase
{}
public class TempPduHandler : IPduHandler<TempPdu>
{
public void Handle(TempPdu pdu)
{
Console.WriteLine("Handling pdu");
}
}
public class PduBase
{ }
private static void Main(string[] args)
{
Receiver r = new Receiver();
r.AddFactory("temp", () => new TempPdu());
r.Register(new TempPduHandler());
// we've recieved a PDU called "temp".
r.FakeReceive("temp");
}
The reason to avoid swith statements are not because if structures are any better (when a switch is used, a bunch of if's will make it worse, not better) it mainly because the problem is not solved in an OO way.
From an OO point of view it is almost always better to use polymorphism then a switch statement.
In your example it's probably better to use a factorymethod to provide the appropriate handler for your type of package.
not sure if that's exactly the point, but having different instances whose treatment differs according to an ID is in fact a case for creating subclasses (the choice of subclass representing the information that was previously stored in the ID) of eg BasePdu and have the compiler figure out which method to use. if you're doing that by switching, it means you're not fully taking advantage of structuring your code by subclassing.
This is more of an architecture/best practices question than anything else, so please feel free to add your two cents. I know i stated status in the title, but this goes for any basic property of an object. I think the account example below will help demonstrate my question a little better than status.
Here is a sample Account object:
public class Account
{
private IList<Transaction> _transactions;
public AddTransaction(trans as Transaction)
{
_transaction.add(trans)
}
}
Now lets say I want to start keeping a history of every time a transaction is added with this object.
public class AccountHistory
{
private DateTime _historyDate;
private String _details;
public AccountHistory(string details)
{
_historyDate = DateTime.Now;
_details = details;
}
}
At this level what I would normally do is add a collection of history events to the account object and also add a line of code to create a history event inside of the AddTransaction() method like this
public AddTransaction(trans as Transaction)
{
_transaction.add(trans);
**_historyEvents.add(new AccountHistory("Transaction Added: " + trans.ToString());**
}
Now the next part is where the problem starts to arise. Suppose I want to do a bulk posting and I want to retain a record of which accounts were changed in this bulk posting for something like a report or if I needed to undo it later. So I would create an object like this.
public class HistoryGroup()
{
private IList<AccountHistory> _events;
}
From here I see a few different options to handle this since it can't be handled by the example code above.
1) Create a function in a Service type object that loops through a list of accounts calling the AddTransaction() method and also creating history records tied to a HistoryGroup
public void AddTransactions(IList<Account> accounts, Transaction trans)
{
HistoryGroup history = new HistoryGroup();
for (int x=0;x <=accounts.Count - 1; x++)
{
accounts(x).AddTransaction(trans);
history.AddEvent(new AccountHistory("Added Transaction: " + trans.ToString();
}
}
2) Pass some type of HistoryManager object into the AddTransaction method along with the transaction to be added. Then the function could use the history manager to create the records.
Ok this post is long enough. If i've not been clear enough let me know. Thanks for you input.
Your method might work just fine, but let me propose an alternative.
Why not add a TransactionAdded Event to the Account class.
You could then subscribe to the Event from (I'm guessing here) the HistoryGroup object so that a new AccountHistory object was added every time the Event fired.
UPDATE
As mentioned in the comments, another method of accomplishing the goal would be to have HistoryGroup implement an interface (ITransactionLogger or something similar) and then modify Account so that the ITransactionLogger dependency can be injected.
Going either of these routes makes things a little easier to manage from the complexity and debugging standpoint, but doesn't allow for multiple Loggers like Events.
That would make your code a little more flexible and at the same time allow other consumers interested in the TransactionAdded Event to subscribe.
I agree with Justin's answer in some ways, but one of the tags on the OP is POCO; adding an event to the Account class would in some ways un-POCO your POCO.
If you're into AOP and other such, you could use interception (most IoC frameworks, including Unity and Castle offer this functionality) to grab transactions of interest.
The benefit of interception is that your Account class has no coupling whatsoever with the AccountHistory class, the interception is highly configurable according to whatever rules you want, and it is easily changed without forcing an application recompile (if you put AccountHistory into a different assembly with the interception handlers). By using interception you are making your code more focused on the business domain rather on what could be considered an infrastructure task (auditing).
Again, this is another alternative for your toolbox; if you don't need to serialize your POCO's over the wire for any reason, then implementing the Observer Pattern (GoF) through events as suggested by Justin may be a more light-weight approach.
The gang of four seem to think so. Transactions, history tracking, and un-doing are all part of a command pattern contract. You can implement history with a stack. Here's a snippet of relevant code including the contract, note that not all methods are or have to be implemented:
public interface ICommand
{
void execute();
void undo();
void store();
void load();
}
public class ManagerMacro : ICommand
{
List<ICommand> Commands;
Stack commandStack;
/// <summary>
/// Use in combination with AddSteps
/// </summary>
//public ManagerMacro()
//{
//}
public ManagerMacro(List<ICommand> commands)
{
this.Commands = commands;
this.commandStack = new Stack();
}
#region ICommand Members
public void execute()
{
for (int i = 0; i < Commands.Count; i++)
{
commandStack.Push(Commands[i]);
Commands[i].execute();
}
}
public void undo()
{
for (int i = 0; i < Commands.Count; i++)
{
if (commandStack.Count > 0)
{
ICommand Command = (ICommand)commandStack.Pop();
Command.undo();
}
}
}
public void store()
{
throw new NotImplementedException();
}
public void load()
{
throw new NotImplementedException();
}
#endregion
public void AddSteps(Steps[] steps)
{
foreach (Steps step in steps)
{
ICommand thisStep = null;
switch (step)
{
case Steps.Manager1: thisStep = new Step1(); break;
case Steps.Manager2: thisStep = new Step2(); break;
case Steps.Manager3: thisStep = new Step3(); break;
case Steps.Manager4: thisStep = new Step4(); break;
}
this.Commands.Add(thisStep);
}
}
}
Note that I also use a factory pattern.
Let's say we have the following piece of code:
public class Event { }
public class SportEvent1 : Event { }
public class SportEvent2 : Event { }
public class MedicalEvent1 : Event { }
public class MedicalEvent2 : Event { }
public interface IEventFactory
{
bool AcceptsInputString(string inputString);
Event CreateEvent(string inputString);
}
public class EventFactory
{
private List<IEventFactory> factories = new List<IEventFactory>();
public void AddFactory(IEventFactory factory)
{
factories.Add(factory);
}
//I don't see a point in defining a RemoveFactory() so I won't.
public Event CreateEvent(string inputString)
{
try
{
//iterate through all factories. If one and only one of them accepts
//the string, generate the event. Otherwise, throw an exception.
return factories.Single(factory => factory.AcceptsInputString(inputString)).CreateEvent(inputString);
}
catch (InvalidOperationException e)
{
throw new InvalidOperationException("Either there was no valid factory avaliable or there was more than one for the specified kind of Event.", e);
}
}
}
public class SportEvent1Factory : IEventFactory
{
public bool AcceptsInputString(string inputString)
{
return inputString.StartsWith("SportEvent1");
}
public Event CreateEvent(string inputString)
{
return new SportEvent1();
}
}
public class MedicalEvent1Factory : IEventFactory
{
public bool AcceptsInputString(string inputString)
{
return inputString.StartsWith("MedicalEvent1");
}
public Event CreateEvent(string inputString)
{
return new MedicalEvent1();
}
}
And here is the code that runs it:
static void Main(string[] args)
{
EventFactory medicalEventFactory = new EventFactory();
medicalEventFactory.AddFactory(new MedicalEvent1Factory());
medicalEventFactory.AddFactory(new MedicalEvent2Factory());
EventFactory sportsEventFactory = new EventFactory();
sportsEventFactory.AddFactory(new SportEvent1Factory());
sportsEventFactory.AddFactory(new SportEvent2Factory());
}
I have a couple of questions:
Instead of having to add factories
here in the main method of my
application, should I try to
redesign my EventFactory class so it
is an abstract factory? It'd be
better if I had a way of not having
to manually add
EventFactories every time I want to
use them. So I could just instantiate MedicalFactory and SportsFactory. Should I make a Factory of factories? Maybe that'd be over-engineering?
As you have probably noticed, I am using a inputString string as argument to feed the factories. I have an application that lets the user create his own events but also to load/save them from text files. Later, I might want to add other kinds of files, XML, sql connections, whatever. The only way I can think of that would allow me to make this work is having an internal format (I choose a string, as it's easy to understand). How would you make this? I assume this is a recurrent situation, probably most of you know of any other more intelligent approach to this. I am then only looping in the EventFactory for all the factories in its list to check if any of them accepts the input string. If one does, then it asks it to generate the Event.
If you find there is something wrong or awkward with the method I'm using to make this happen, I'd be happy to hear about different implementations. Thanks!
PS: Although I don't show it in here, all the different kind of events have different properties, so I have to generate them with different arguments (SportEvent1 might have SportName and Duration properties, that have to be put in the inputString as argument).
I am not sure about the input string question but for the first question you can likely use "convention over configuration"; a combination of reflection, the IEventFActory type and the naming you already have in place, Name.EndsWith("EventFactory") should allow you to instantiate the factories and get them into their Lists with code.
HTH ,
Berryl