How can I avoid the switch statement if the return type is unknown by the calling method?

I read a book called Clean Code. Agile Software Skill Guide . The author in the book motivates that the switch statement should be avoided, and if it cannot be avoided, it should be attributed to factory methods. I have a connection object that accepts various PDUs (protocol data units). PDUs vary and can be obtained in any order. Therefore, if I have a method, for example:

public BasePdu ReceiveNext();

because I cannot determine the type of package until it is fully received. The PDU header contains an identifier of what type it should be. This means that the calling method will determine what the type of PDU is, and based on this call, the corresponding method processes it. This sounds like a great example for a switch statement. An object that contains a connection, I would ideally want to have two threads. One for receiving the PDU and the other for serving the PDU queue for sending.

Now I know that you cannot follow all the advice and that there are only some circumstances that are an exception to the rule. Is this one of them? Or is there a way around this that I just didn't think about.

UPDATE:

I heard many people say when subclassing response handlers. The problem is that the containing object has a lot of context and additional information that handlers need, for example, to search and throttle, etc. Etc. To embed all this information in subclasses of handlers, it would be a rather difficult task to maintain and also a lot of logic when it is better to feel encapsulated in the object in which it is currently located.

+5
source share
5 answers

; switch, . , , factory, PDU , . , , switch,

+4

PDUParserFactory, PDU switch PDU. , :)

:

 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));
           }
      }
 }

:

 pduReceiver.PDUReceived += BaseHandler;
 pduReceiver.PDUReceived += PDUType1Handler;
 ...

 void PDUType1Handler(object sender, PDUReceivedEventArgs e)
 {
      // only care about PDUType1
      if (e.PDU.GetType() != typeof(PDUType1))
            return;
      ....
 }

, pdu , . , PDU.

, PDU, :

 class PDU
 {
      public PDUType PDUType { get; }
      public byte[] PDUData { get }
 }

PDUType , , .

, , .

+4

, swith, , ( , if , ), , OO.

OO , switch.

, , factory, .

+1

, :


PDU, switch.

factory Dictionary<string, Func<PduBase>>


, public BasePdu ReceiveNext();, , switch

RecieveNext. AddPduHandler<T>(IPduHandler<T> handler) where T : PduBase , PDU. : Dictionary<Type, Delegate>

- , .

Update

, switch. , , PDU.

, .

, ( ), .

    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");
    }
+1

I’m not sure that this point, but having different instances, the processing of which differs according to the identifier, is actually an example for creating subclasses (choosing a subclass representing information previously stored in the ID), for example, BasePdu and find out which compiler method to use. if you do this by switching, it means that you are not fully utilizing the structuring of your code by subclassing.

0
source

All Articles