Duda con Patrón de diseño (CoR) C#

Publicado en 'Programación' por AiApaec, 21 Jul 2014.





  1. AiApaec

    AiApaec Miembro frecuente

    Registro:
    1 May 2014
    Mensajes:
    58
    Likes:
    14




    Hola foreros, espero que me respondan rápido :paz:.
    Verán, estoy usando el patrón Chain Of Responsability (CoR) mediante interfaces pero veo que el código se repite en cada implementación, justamente ese código no va a cambiar por lo que no sería necesario reescribir lo mismo en cada implementación.
    Primero les mostraré el código usando interface:
    (El código está en C#)
    Código:
    public interface IOperation
    {
        bool CanProcess(string data);
        int ProcessOperation(string data);
    }
    public class Operation_1 
    {
        private IOperation _successor;
        public Operation_1(IOperation successor)
        {
            this._successor = successor;
        }
        public bool CanProcess(string data)
        {
            return data.equals(this.GetType().Name);
        }
        public int ProcessOperation(string data)
        {
            return this.CanProcess(data) ? 1*1 : this._successor.ProcessOperation(data);
        }
    }
    
    El código que se repetiría sería el de CanProcess y también el de ProcessOperation, bueno en realidad en este último lo único que no se repitiría y sería diferente en cada
    implementación sería esa parte del 1*1, lo demás sería igual, así que se me ocurre usar compartir el comprtamiento mediante herencia usando una clase abstracta...esta sería la nueva forma:
    Código:
    public abstract class BaseOperation
    {
        private BaseOperation _successor;
        public BaseOperation(BaseOperation successor)
        {
            this._successor = successor;
        }
        protected virtual bool CanProcess(string data)
        {
            return data.equals(this.GetType().Name);
        }
        //only this method will be visible to clients:
        public virtual bool ProcessOperation(string data)
        {
            return this.CanProcess(data) ? ProcessOperation(data): this._successor.ProcessOperation(da
        }
        //this method must be implemented but will not be visible.
        protected abstract int Process(string data)
    }
    public class Operation_1 : BaseOperation
    {
        public Operation_1(BaseOperation successor): base(successor){}
        protected override int Process(string data)
        {
            return 1*1;
        }
    }
    public class Operation_2 : BaseOperation
    {
        public Operation_2(BaseOperation successor): base(successor){}
        protected override int Process(string data)
        {
            return 1*2;
        }
    }
    public class DefaultOperation : BaseOperation
    {
        public DefaultOperation(BaseOperation successor): base(successor){}
        public override int ProcessOperation(string data)
        {
            return Process(data);
        }
        protected override int Process(string data)
        {
            return 1*-1;
        }
    }
    //CLIENT:
    public class Client
    {
        public int ProcessOperation(string data)
        {
            BaseOperation undefined = new DefaultOperation(null);
            BaseOperation op2 = new Operation_2(undefined);
            BaseOperation op1 = new Operation_1(op2);
            return op1.ProcessOperation(data);
        }
    }
    
    Como ven,para cada nueva clase Operation que herede de BaseOperation(tiene que heredar) solo será necesario escribir el código que cambia, nada de repeticiones como en el primer caso...pero aquí vienen mis dudas, yo creo que no estoy violando el patrón, puesto que siempre es uan cadena, tiene su "successor", qué opinan Uds, tal vez se me está pasando algo.

    PD: Debería poderse embeber el código con pastebin.
    Gracias.
     
    Última edición: 21 Jul 2014


  2. gnox

    gnox Miembro maestro

    Registro:
    3 Ene 2013
    Mensajes:
    792
    Likes:
    252
    Esto :
    Código:
    //only this method will be visible to clients:
        public virtual bool ProcessOperation(string data)
        {
            return this.CanProcess(data) ? ProcessOperation(data): this._successor.ProcessOperation(da
        }
    
    deberia ser :
    Código:
     public virtual bool ProcessOperation(string data)
        {
           return this.CanProcess(data) ? [B]Process[/B](data): this._successor.ProcessOperation(da
    }
    
    El resto no rompe el CoR, pero seria preferible que el Handler (BaseOperation) no maneje la lógica sino que la delegue , ya que 1 cambio de clase request (Data) implicaría que afecta a todo el resto de handlers (BaseOperation, Operation_*), en tu ejemplo no se ve pero cuando Data sea una clase con distintos atributos/métodos ahí ves la diferencia entre agrupar en 1 solo lado la lógica o segregarla en el resto de handlers.
     
    Última edición: 21 Jul 2014
  3. AiApaec

    AiApaec Miembro frecuente

    Registro:
    1 May 2014
    Mensajes:
    58
    Likes:
    14
    Código corregido:
    Usando una interface:
    Código:
    public interface IOperation
    {
        bool CanProcess(string data);
        int ProcessOperation(string data);
    }
    public class Operation_1 : IOperation
    {
        private IOperation _successor;
        public Operation_1(IOperation successor)
        {
            this._successor = successor;
        }
        public bool CanProcess(string data)
        {
            return data.equals(this.GetType().Name);
        }
        public int ProcessOperation(string data)
        {
            return this.CanProcess(data) ? 1*1 : this._successor.ProcessOperation(data);
        }
    }
    
    Con clase abstracta:
    Código:
    
    public abstract class BaseOperation
    {
        private BaseOperation _successor;
        public BaseOperation(BaseOperation successor)
        {
            this._successor = successor;
        }
        protected virtual bool CanProcess(string data)
        {
            return data.equals(this.GetType().Name);
        }
        //only this method will be visible to clients:
        public virtual bool ProcessOperation(string data)
        {
            return this.CanProcess(data) ? Process(data): this._successor.ProcessOperation(data);
        }
        //this method must be implemented but will not be visible.
        protected abstract int Process(string data)
    }
    public class Operation_1 : BaseOperation
    {
        public Operation_1(BaseOperation successor): base(successor){}
        protected override int Process(string data)
        {
            return 1*1;
        }
    }
    public class Operation_2 : BaseOperation
    {
        public Operation_2(BaseOperation successor): base(successor){}
        protected override int Process(string data)
        {
            return 1*2;
        }
    }
    public class DefaultOperation : BaseOperation
    {
        public DefaultOperation(BaseOperation successor): base(successor){}
        public override int ProcessOperation(string data)
        {
            return Process(data);
        }
        protected override int Process(string data)
        {
            return 1*-1;
        }
    }
    //CLIENT:
    public class Client
    {
        public int ProcessOperation(string data)
        {
            BaseOperation undefined = new DefaultOperation(null);
            BaseOperation op2 = new Operation_2(undefined);
            BaseOperation op1 = new Operation_1(op2);
            return op1.ProcessOperation(data);
        }
    }
    
    El resto no rompe el CoR, pero seria preferible que el Handler (BaseOperation) no maneje la lógica sino que la delegue , ya que 1 cambio de clase request (Data) implicaría que afecta a todo el resto de handlers (BaseOperation, Operation_*), en tu ejemplo no se ve pero cuando Data sea una clase con distintos atributos/métodos ahí ves la diferencia entre agrupar en 1 solo lado la lógica o segregarla en el resto de handlers.[/quote]
    Sí, gracias, ya lo corregí. Creo que has visto mi pregunta en stackoverflow :), por lo del tipo Request (el equivalente a "data")...esa clase no va a cambiar y la lógica de que se encuentra en la calse abstracta tampoco, aunque entiendo tu punto.
    En realidad estoy tratando de desacoplar un servicio web (qeu consumiría al cliente del código que puse) de la capa de negocios, voy por partes, por el momento el objetivo era eso, pero tenía dudas...al final yo está en pruebas la implementación y seguramente va para productivo.
    Gracias por tu aporte :hi:.
     
    Última edición: 22 Jul 2014