2009-03-18 9 views
6

Ich machte das folgende Codebeispiel, um zu lernen, wie man eine Generics-Methodensignatur verwendet.Ist dies ein Beispiel für das Prinzip der einfachen Verantwortung?

Um Display() -Methode sowohl für Kunden und Mitarbeiter zu bekommen, begann ich tatsächlich meinen IPerson Schnittstelle mit einer Person abstrakten Klasse zu ersetzen.

Aber dann hörte ich auf, um einen Podcast, in dem Onkel Bob Erinnerung erzählt Scott Hanselman über das Einzel Prinzip Verantwortung, in dem Sie viele kleinen Klassen jeder tut eine bestimmte Sache haben sollten, dh dass eine Customer-Klasse sollte nicht ein Print() und Save() und CalculateSalary() Methode aber, dass Sie sollten eine CustomerPrinter Klasse und eine CustomerSaver Klasse und ein KundeSalaryCalculator Klasse.

Das scheint eine seltsame Art zu programmieren. Allerdings loswerden meiner Schnittstelle fühlte auch falsch (da so viele IoC-Container und DI-Beispiele sie inhärent verwenden), so beschloss ich, die Single-Verantwortung-Prinzip zu versuchen.

Also der folgende Code ist anders als ich in der Vergangenheit programmiert habe (ich hätte eine abstrakte Klasse mit einer Display() -Methode gemacht und die Schnittstelle los) aber basierend auf was ich über Entkopplung und gehört habe der SOLIDE Prinzipien, diese neue Art der Codierung (die Schnittstelle und die PersonDisplayer-Klasse) Ich denke, das ist der richtige Weg zu gehen.

Ich würde gern hören, wenn andere auf diese Weise zu diesem Thema denken oder positive oder negative Auswirkungen davon erfahren haben (z. B. eine unhandliche Anzahl von Klassen, die jeweils eine bestimmte Sache tun, usw.).

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

Ähnliche Fragen http://stackoverflow.com/questions/1215442/confused-about-single-responsibility-principle-in-the-following-example –

Antwort

8

Ich denke gerne an die Single Responsibility Principle als eine Implementierung von separation of duties. Bevor ich anfange, meine Klassen zu teilen, versuche ich darüber nachzudenken, wofür jede Klasse verantwortlich sein soll.

Ihre Klassen sind recht einfach und eignen sich gut für eine abstrakte Klasse mit einem implementiert Print() und Save() Funktionen wie Sie erwähnt. Ich würde dazu neigen, dieses Design über dein jetziges zu behalten.

Wenn das Drucken und Speichern jedoch kompliziertere Aufgaben sind, die auf unterschiedliche Weise ausgeführt werden können, wäre eine dedizierte Klasse Printer oder Saver gerechtfertigt, da diese Aufgabe nun komplexer ist. Die "Komplexitätsschwelle" für die Erstellung einer neuen Klasse ist sehr subjektiv und wird von der genauen Situation abhängen, aber am Ende ist der Code nur eine Abstraktion für uns Menschen, die wir verstehen müssen, also mach sie so, dass sie am intuitivsten ist.

Sie Container Klasse ist ein wenig irreführend. Es enthält eigentlich nichts. Es implementiert tatsächlich die Factory Method Pattern und würde davon profitieren, eine Fabrik genannt zu werden.

Auch Ihre PersonDisplayer wird nie instanziiert und kann alle ihre Funktionalität durch statische Methoden bieten, warum also nicht zu einer statischen Klasse machen? Es ist nicht ungewöhnlich für utility classes wie Drucker oder Sparer statisch zu sein. Wenn Sie keine separaten Druckerinstanzen mit unterschiedlichen Eigenschaften benötigen, sollten Sie sie statisch halten.

+0

PersonDisplayer ist in seiner eigenen statischen SimpleDisplay-Methode installiert – user76035

+0

Richtig, das ist ein Muster, das ich viel in PHP verwendet: eine Klasse mit statischen Methoden, die ihre eigene Klasse instanziieren, dann eine bestimmte interne Methode ausführen, im Grunde nur dem Entwickler die Möglichkeit geben Ein-Liner schreiben statt eine Klasse instanziieren und dann eine Methode aufrufen. –

+3

Dies ist eine unnötige Objekterstellung und führt zu einer Leistungseinbuße ohne zusätzliche Funktionalität. Der PersonDisplayer hat keinen Grund instanziiert zu werden, alle Funktionen können statisch bereitgestellt werden. –

1

Nun, ich habe noch nie von dieser ‚einzigen Verantwortung Prinzip‘ gehört, aber was es mir scheint, dass einfach das, was Sie tun, um diese CustomerPrinter Klasse und CustomerSaver Klassen, indem sich Klassen zurück zu structs Umwandlung und alles zu de-objektorientieren.

Dies würde beispielsweise bedeuten, dass unterschiedliche Kundentypen in der CustomerPrinter-Klasse andere Fälle benötigen, wenn sie anders gedruckt werden müssen. Aber wie ich es verstehe, ist einer der Punkte der OO-Organisation und der Verwendung von Vererbungsbäumen und all dem, den Bedarf dieses CustomerPrinters zu beseitigen, um zu wissen, wie man alles druckt: Kunden wissen, wie sie selbst drucken.

Ich glaube nicht, diese Paradigmen in jedem Fall streng zu befolgen. Zum Beispiel bin ich nicht sicher, was der Unterschied zwischen einer Schnittstelle und einer abstrakten Klasse in Ihrem Fall ist. Aber dann bin ich wieder ein C++ Programmierer, kein C# Programmierer ...

+0

mit einer Schnittstelle kann ich meine Klassen bestimmte Methodensignaturen haben, aber ich kann ihnen Methoden mit Funktionalität nicht geben, dafür brauche ich eine abstrakte Klasse, ich sehe Schnittstellen als nur einen "leichten Vertrag", dass meine Klassen intelligent um die Anwendung herumgereicht werden müssen –

4

Ich denke du bist auf dem richtigen Weg. Ich bin mir nicht ganz sicher über die Container-Klasse. Ich würde im Allgemeinen bei der einfacheren Lösung bleiben, einfach nur "neu" für diese Objekte zu verwenden, es sei denn, Sie haben eine geschäftliche Notwendigkeit für diese Schnittstelle.(Ich halte "ordentlich" nicht für eine Geschäftsanforderung in diesem Sinne)

Aber die Trennung von "Sein" als Kundenverantwortung von "Anzeigen eines Kunden" ist schön. Bleiben Sie dabei, es ist eine schöne Interpretation der SOLID-Prinzipien.

Persönlich habe ich jetzt völlig gestoppt in dieser Art von Code, jede Art von statischen Methoden verwendet, und ich verlasse mich auf DI alle richtigen Service-Objekte am richtigen Ort & Zeit zu bekommen. Sobald Sie mit der Ausarbeitung der SOLID-Prinzipien beginnen, werden Sie feststellen, dass Sie viel mehr Kurse machen. Versuchen Sie, an diesen Namenskonventionen zu arbeiten, um konsistent zu bleiben.

+0

interessant, es scheint nur, dass mein "Kunde" und "Mitarbeiter" -Klassen werden sehr klein sein, vielleicht nur Eigenschaften und ein kleiner Konstrukteur, diese Arten von Klassen waren früher solche Eckpfeiler der Anwendung. –

+0

Ja, ihre Verantwortung ist es, ein Angestellter zu sein. Sie werden feststellen, dass Sie auch andere Methoden hinzufügen, nicht nur Eigenschaften. Es gibt keinen Grund, religiös zu sein. – krosenvold

1

Ein paar Anmerkungen:

  • Generell SRP ist alles gut, als Trennung von Anzeige von Datenformatierung ist.
  • Betrachten Anzeige usw. Ich würde eher in Bezug auf Dienstleistungen denken, d. H. Ein PersonDisplayer ist Single, Staatenlos und bietet eine String-Display (IPerson) -Funktion. IMHO, eine spezielle Klasse Wrapper nur zur Verfügung stellen Display bietet keinen Vorteil.
  • Wenn Sie jedoch die Datenbindung für wpf verwendet haben, verfügen Sie möglicherweise über eine DisplayablePerson-Klasse, die PropertyChanged propagiert, wenn Person geändert wurde. Sie würden DisplayablePerson-Objekte in ObservableCollection einfügen und als ItemsSource eines Listensteuerelements bereitstellen.
  • Was benötigen Sie Container für, ist es nur zum Instanziieren und Konfigurieren der Instanz? Versuchen Sie es dann: Kunde customer1 = new Customer {FirstName = "Jim", LastName = "Smith"};

  • Nebenbei habe ich versucht object.Method < SomeType> (...) Aufruf ein paar Mal, wie es schien schnellste und einfachste Lösung. Aber nach einiger Zeit habe ich immer Probleme damit gehabt und endete mit object.Method (Type someTypeType, ...

    )
Verwandte Themen