Project structure opinions?
-
For context, my current pet project is a modern implementation of the IRCv3 client protocol. It offers three layers to use depending on what you're looking for: directly to the IRC socket (raw messages), a broadcaster (contextual messages), or a "framework" of sorts that obscures details into easy-to-use interfaces like
Server
,LocalUser
,Channel
, etc. Anyway, I've realized my broadcaster folder is becoming irritating and slow to navigate. Between the "everything in one file"-type and the "one class per file"-type I very much lean towards the latter. However with roughly 200 distinct messages in the base protocol alone (not to mention CTCP, DCC, XDCC, SASL, etc) the one-class-per-file method is becoming ornery at best. After some thinking, I decided this would be a nice solution that would allow easy navigation since everything related to a message is contained in a single file://Example RPL_WELCOME file
public interface IWelcomeReply : IReply { }public partial class IrcBroadcaster
{
public event EventHandler WelcomeEvent;//Since partial constructors aren't allowed the "base" IrcBroadcaster will simply call //partial setup methods. private partial SetupWelcomeMessage() { \_commandMap.Add( MessageCommands.RPL\_WELCOME, msg => WelcomeEvent?.Invoke(this, new Reply(msg)) ); }
}
//For many other messages, there are unique parameters so they'd have an interface implementation here.
//IWelcomeReply is one of the simplest so that's not needed as Reply handles it.Before you'd have to sift through a minimum of 3 files among what would be over 600 to check/change something and the
IrcBroadcaster
class was a monster. This feels easier to navigate but I was wondering what your thoughts were? And if you've run into a similar situation what was your solution? -
For context, my current pet project is a modern implementation of the IRCv3 client protocol. It offers three layers to use depending on what you're looking for: directly to the IRC socket (raw messages), a broadcaster (contextual messages), or a "framework" of sorts that obscures details into easy-to-use interfaces like
Server
,LocalUser
,Channel
, etc. Anyway, I've realized my broadcaster folder is becoming irritating and slow to navigate. Between the "everything in one file"-type and the "one class per file"-type I very much lean towards the latter. However with roughly 200 distinct messages in the base protocol alone (not to mention CTCP, DCC, XDCC, SASL, etc) the one-class-per-file method is becoming ornery at best. After some thinking, I decided this would be a nice solution that would allow easy navigation since everything related to a message is contained in a single file://Example RPL_WELCOME file
public interface IWelcomeReply : IReply { }public partial class IrcBroadcaster
{
public event EventHandler WelcomeEvent;//Since partial constructors aren't allowed the "base" IrcBroadcaster will simply call //partial setup methods. private partial SetupWelcomeMessage() { \_commandMap.Add( MessageCommands.RPL\_WELCOME, msg => WelcomeEvent?.Invoke(this, new Reply(msg)) ); }
}
//For many other messages, there are unique parameters so they'd have an interface implementation here.
//IWelcomeReply is one of the simplest so that's not needed as Reply handles it.Before you'd have to sift through a minimum of 3 files among what would be over 600 to check/change something and the
IrcBroadcaster
class was a monster. This feels easier to navigate but I was wondering what your thoughts were? And if you've run into a similar situation what was your solution?Providing a simpler yet type safe way of handling messages from internet protocols is why I wrote the code in this article: C# Replacing switch(enum) flow control with Reflection[^]. Don't know if it will suit your goals but it worked for me while experimenting with client/server app development. Certain others didn't see the merit in it but everybody has their opinions.
if (Object.DividedByZero == true) { Universe.Implode(); }
-
Providing a simpler yet type safe way of handling messages from internet protocols is why I wrote the code in this article: C# Replacing switch(enum) flow control with Reflection[^]. Don't know if it will suit your goals but it worked for me while experimenting with client/server app development. Certain others didn't see the merit in it but everybody has their opinions.
if (Object.DividedByZero == true) { Universe.Implode(); }
For my code-base I'd been considering something like this to simplify the mapping code. I liked the article. It's a nice technique for niche situations like this. I'll be running the reflection in the static constructor instead of instance though since the mappings won't change during run-time.
-
For my code-base I'd been considering something like this to simplify the mapping code. I liked the article. It's a nice technique for niche situations like this. I'll be running the reflection in the static constructor instead of instance though since the mappings won't change during run-time.
Jon McKee wrote:
I liked the article.
Thanks.
Jon McKee wrote:
I'll be running the reflection in the static constructor instead
That's how my original code was written. You can approach initializing your method maps in either way.
if (Object.DividedByZero == true) { Universe.Implode(); }