2009-03-18 15 views
6

Hice el siguiente ejemplo de código para aprender a usar una firma de método genérico.¿Es este un ejemplo del Principio de Responsabilidad Individual?

el fin de obtener un métodoPantalla() tanto para clientes y empleados, que en realidad comenzó a reemplazar mi interfaz IPerson con una clase abstracta persona.

Pero entonces se detuvo, recordando un podcast en el que el tío Bob estaba diciendo a Scott Hanselman sobre el Principio Responsabilidad Individual en el que usted debe tener un montón de pequeñas clases cada uno haciendo una cosa específica, es decir, que una clase de atención al cliente no debería tener un Imprimir() y Save() y CalculateSalary() método, sino que debe tener una clase CustomerPrinter y una clase CustomerSaver y una CustomerSalaryCalculator class.

Parece una forma extraña de programar. Sin embargo, deshacerse de mi interfaz también se sentía mal (ya que tantos contenedores IoC y ejemplos DI los usan inherentemente), así que decidí probar el Principio de Responsabilidad Individual.

Así el siguiente código es diferente de lo que he programado en el pasado (yo hubiera hecho una clase abstracta con un método de visualización() y se deshizo de la interfaz) pero en base a lo que he oído hablar de desacoplamiento y el SOLIDO principios, esta nueva forma de codificación (la interfaz y la clase PersonDisplayer) Creo que este es el camino correcto para ir.

Deseo me gustaría escuchar si otros piensan lo mismo sobre este tema o si han experimentado efectos positivos o negativos (por ejemplo, un número difícil de clases cada uno haciendo una cosa en particular, etc.).

using System; 

namespace TestGeneric33 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Container container = new Container(); 
      Customer customer1 = container.InstantiateType<Customer>("Jim", "Smith"); 
      Employee employee1 = container.InstantiateType<Employee>("Joe", "Thompson"); 
      Console.WriteLine(PersonDisplayer.SimpleDisplay(customer1)); 
      Console.WriteLine(PersonDisplayer.SimpleDisplay(employee1)); 
      Console.ReadLine(); 
     } 
    } 

    public class Container 
    { 
     public T InstantiateType<T>(string firstName, string lastName) where T : IPerson, new() 
     { 
      T obj = new T(); 
      obj.FirstName = firstName; 
      obj.LastName = lastName; 
      return obj; 
     } 
    } 

    public interface IPerson 
    { 
     string FirstName { get; set; } 
     string LastName { get; set; } 
    } 

    public class PersonDisplayer 
    { 
     private IPerson _person; 

     public PersonDisplayer(IPerson person) 
     { 
      _person = person; 
     } 

     public string SimpleDisplay() 
     { 
      return String.Format("{1}, {0}", _person.FirstName, _person.LastName); 
     } 

     public static string SimpleDisplay(IPerson person) 
     { 
      PersonDisplayer personDisplayer = new PersonDisplayer(person); 
      return personDisplayer.SimpleDisplay(); 
     } 
    } 

    public class Customer : IPerson 
    { 
     public string FirstName { get; set; } 
     public string LastName { get; set; } 
     public string Company { get; set; } 
    } 

    public class Employee : IPerson 
    { 
     public string FirstName { get; set; } 
     public string LastName { get; set; } 
     public int EmployeeNumber { get; set; } 
    } 
} 
+0

Pregunta similar http://stackoverflow.com/questions/1215442/confused-about-responsibility-principle-in-the-following-example –

Respuesta

8

Me gusta pensar en el Single Responsibility Principle como una implementación de separation of duties. Antes de comenzar a dividir mis clases como lo haces, trato de pensar en lo que cada clase debería ser responsable.

Sus clases son bastante simples y se prestan bien a una clase abstracta con las funciones Print() y Save() implementadas como usted mencionó. Tiendo a mantener ese diseño sobre el actual.

Sin embargo, si la impresión y el guardado eran tareas más complicadas que podrían realizarse de diferentes maneras, se justificaría una clase dedicada Printer o Saver, ya que esa responsabilidad ahora es más compleja. El umbral de 'complejidad' para hacer una nueva clase es muy subjetivo y dependerá de la situación exacta, pero al final, el código es solo una abstracción para que los humanos humildes comprendamos, por lo que es más intuitivo.

Usted Container clase es un poco engañoso. En realidad, no "contiene" nada. En realidad implementa el Factory Method Pattern y se beneficiaría de ser nombrado una fábrica.

Además, su PersonDisplayer nunca se crea una instancia y puede proporcionar toda su funcionalidad a través de métodos estáticos, ¿por qué no convertirla en una clase estática? No es raro que utility classes, como impresoras o protectores, sea estático. A menos que tenga la necesidad de tener instancias separadas de una impresora con diferentes propiedades, manténgala estática.

+0

PersonDisplayer se instatiated en su propio método SimpleDisplay estática – user76035

+0

derecho, que es un modelo que utiliza mucho en PHP: una clase con métodos estáticos que crear una instancia de su propia clase a continuación, ejecute un determinado método interno en él, básicamente, sólo da al desarrollador la capacidad escribir frases sencillas en lugar de crear una instancia de una clase y luego llamar a un método sobre ella. –

+3

Esta es una creación de objetos innecesaria e incurrirá en una penalización de rendimiento sin ninguna funcionalidad adicional. El PersonDisplayer no tiene ninguna razón para ser instanciado, toda la funcionalidad se puede proporcionar estáticamente. –

1

Bueno, nunca he oído hablar de este 'principio único responsable' antes, pero lo que me parece que lo que está haciendo por tener estas clases de clase CustomerPrinter y CustomerSaver es simplemente la conversión de las clases de nuevo a estructuras y desorientan el objeto de todo.

Por ejemplo, esto significaría que diferentes tipos de clientes necesitarían casos diferentes en la clase CustomerPrinter si necesitaran imprimirse de manera diferente. Pero, según tengo entendido, uno de los puntos de la organización de OO, y del uso de árboles de herencia y todo eso, es eliminar la necesidad de que esta impresora de clientes sepa cómo imprimir todo: los clientes saben cómo imprimir ellos mismos.

No creo en seguir estos paradigmas rígidamente en cualquier caso. Por ejemplo, no estoy seguro de cuál es la diferencia entre una interfaz y una clase abstracta en su caso. Pero, de nuevo, soy un programador de C++, no un programador de C# ...

+0

con una interfaz Puedo forzar a mis clases a tener ciertas firmas de métodos pero No puedo darles métodos con funcionalidad, para eso necesito una clase abstracta, veo las interfaces como solo un "contrato ligero" que mis clases deben pasarse inteligentemente por la aplicación –

4

Creo que estás en el camino correcto. Aunque no estoy del todo seguro acerca de la clase Container. En general, me quedo con la solución más simple de simplemente usar "nuevo" para estos objetos a menos que tenga alguna necesidad impulsada por la empresa para esa interfaz.(No considero "ordenado" como un requisito comercial en este sentido)

Pero la separación entre "ser" una responsabilidad del cliente y "mostrar un cliente" es agradable. Quédate con eso, es una buena interpretación de los principios SÓLIDOS.

Personalmente tengo ahora completamente parado utiliza cualquier tipo de métodos estáticos en este tipo de código, y se basan en DI para obtener todos los objetos de servicio adecuadas en el lugar correcto & tiempo. Una vez que comiences a profundizar en los principios SÓLIDOS, descubrirás que estás haciendo muchas más clases. Intenta trabajar en las convenciones de nomenclatura para mantenerte constante.

+0

interesante, parece que mi "cliente" y las clases de "empleados" van a ser muy pequeñas, quizás solo propiedades y un pequeño constructor, este tipo de clases solían ser las piedras angulares de la aplicación –

+0

Sí, su responsabilidad es "ser" un empleado. Es posible que también agregue otros métodos, no solo propiedades. No hay razón para ser religioso al respecto. – krosenvold

1

Unas pocas notas:

SRP
  • Generalmente hablando es todo bien, como es la separación de la pantalla de formato de datos.
  • Considerando la visualización, etc. Prefiero pensar en términos de servicios, es decir, un PersonDisplayer es único, sin estado y ofrece una función de visualización de cadena (IPerson). En mi humilde opinión, una envoltura de clase especial solo para proporcionar visualización no proporciona ninguna ventaja.
  • Sin embargo, si utilizó el enlace de datos para wpf, es posible que tenga una clase DisplayablePerson que propague PropertyChanged si la persona cambia. Colocaría objetos DisplayablePerson en ObservableCollection y lo publicaría como ItemsSource de algún control de lista.
  • ¿Para qué sirve el contenedor, solo para instanciar y configurar instancia? Pruebe entonces Cliente customer1 = nuevo Cliente {FirstName = "Jim", LastName = "Smith"};

  • En una nota lateral, he intentado object.Method < SomeType> (...) invocación varias veces, ya que parecía la solución más rápida y simple. Sin embargo, después de un tiempo, siempre tuve problemas con eso y terminé con object.Method (Escriba someTypeType, ...)

Cuestiones relacionadas