2009-03-18 3 views
6

제네릭 메서드 서명을 사용하는 방법을 배우려면 다음 코드 예제를 작성했습니다. 모두 고객과 직원을위한 표시() 메소드를 얻기 위하여이것은 단일 책임 원칙의 예입니까?

, 나는 실제로 사람 추상 클래스IPerson에 인터페이스를 대체하기 시작했다.

하지만 그때 내가있는 삼촌 밥은 당신이 각각 하나의 특정 일을 작은 클래스를 많이해야하는 단일 책임 원칙에 대해 스콧 Hanselman은 이야기 한 팟 캐스트를 기억, 중지, 고객 클래스가 안된다고 즉 인쇄()저장()CalculateSalary() 방법하지만 당신은 CustomerPrinter 클래스CustomerSaver 클래스과을해야한다고CustomerSalaryCalculator 클래스.

그건 이상하게 생각합니다. 그러나 내 인터페이스를 제거하는 것은 잘못된 (너무 많은 IoC 컨테이너와 DI 예제가 본질적으로 사용하기 때문에)로 느껴져서 단일 책임 원칙을 시도하기로 결정했습니다.

그래서 다음 코드는 이전에 프로그래밍 한 것보다(나는 Display() 메서드로 추상 클래스를 만들고 인터페이스를 제거했을 것입니다.)하지만 디커플링에 대해 단단한 원리, 이 새로운 코딩 방법 (인터페이스와 PersonDisplayer 클래스) 이것이 올바른 방법이라고 생각합니다..

다른 사람들이이 문제에 대해 동일한 방법으로 생각하거나이 문제에 긍정적 또는 부작용이있는 경우 (예 : 하나의 특정 작업을 수행하는 클래스가 다루기 힘든 경우 등) 듣고 싶습니다.

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

비슷한 질문 http://stackoverflow.com/questions/1215442/confused-about-single-responsibility-principle-in-the-following-example 인터페이스가있는 –

답변

8

나는 Single Responsibility Principleseparation of duties의 구현으로 생각합니다. 수업을 시작하기 전에 각 수업의 책임을 생각해 봅니다.

클래스는 매우 간단하며 위에서 설명한대로 Print()Save() 함수가 구현 된 추상 클래스에 적합합니다. 나는 현재의 디자인보다 그 디자인을 유지하는 경향이 있습니다.

그러나 인쇄 및 저장이 다른 방법으로 수행 될 수있는 더 복잡한 작업 인 경우 해당 책임이 이제는 더 복잡하기 때문에 전용 Printer 또는 Saver 클래스가 보증됩니다. 새로운 클래스를 만드는 데 필요한 '복잡성'임계 값은 매우 주관적이며 정확한 상황에 따라 달라 지지만 결국 코드는 저조한 인간이 이해할 수있는 추상화에 불과하므로 가장 직관적으로 만듭니다.

Container 클래스는 약간의 혼동을줍니다. 실제로 아무것도 포함하지 않습니다. 실제로는 Factory Method Pattern을 구현하며 공장 이름을 얻는 것이 좋습니다.

또한 PersonDisplayer은 결코 인스턴스화되지 않으며 정적 메서드를 통해 모든 기능을 제공 할 수 있으므로 정적 클래스로 만들지 않겠습니까? 프린터 또는 보호기와 같은 utility classes이 정적 인 경우는 흔합니다. 속성이 다른 프린터 인스턴스를 별도로 보유 할 필요가 없다면 정적으로 유지하십시오.

+0

PersonDisplayer는 정적 SimpleDisplay 메서드로 불려갑니다. – user76035

+0

맞아요, PHP에서 많이 사용되는 패턴입니다. 정적 메서드가있는 클래스는 자체 클래스를 인스턴스화 한 다음 특정 내부 메서드를 실행합니다. 기본적으로 개발자에게 능력을 부여합니다. 클래스를 인스턴스화하는 대신 하나의 라이너를 작성한 다음 클래스에 메소드를 호출합니다. –

+3

이것은 불필요한 개체 생성이며 추가 기능이없는 경우 성능이 저하됩니다. PersonDisplayer는 인스턴스화 될 이유가 없기 때문에 모든 기능이 정적으로 제공 될 수 있습니다. –

1

글쎄, 내가 전에이 '단일 책임 원칙'들어,하지만 결코 당신이이 CustomerPrinter 클래스와 CustomerSaver 클래스를 필요로하고있는 것은 단순히 구조체에 다시 클래스로 변환되는 것을 나에게 표시되는 내용 , 그리고 모든 객체 지향을 해제합니다.

예를 들어, 다른 고객 유형을 다르게 인쇄해야하는 경우 CustomerPrinter 클래스에서 다른 경우가 필요할 수 있습니다. 그러나 OO 조직의 요점 중 하나이며 상속 나무와 그 모든 것을 사용하여 모든 것을 인쇄하는 방법을 알기 위해이 CustomerPrinter의 필요성을 없애는 것입니다. 고객은 자신을 인쇄하는 방법을 알고 있습니다.

필자는 이러한 패러다임을 엄격하게 따르지 않는다고 생각합니다. 예를 들어 인터페이스와 추상 클래스의 차이점이 무엇인지 확신 할 수 없습니다. 그렇지만 다시 C++ 프로그래머가 아닌 C++ 프로그래머입니다 ...

+0

클래스에 특정 메서드 서명이 있어야하지만 강제로 그 (것)들에게 기능을 제공 할 수는 없습니다. 왜냐하면 추상적 인 클래스가 필요하기 때문에, 인터페이스가 애플리케이션을 중심으로 지능적으로 전달 될 필요가있는 "가벼운 계약"이라고 만 볼 수 있습니다. –

4

나는 당신이 올바른 길에 있다고 생각합니다. 컨테이너 클래스에 대해서는 완전히 확신하지 못합니다. 나는 일반적으로 이러한 인터페이스에 대한 비즈니스 중심의 필요가 없다면 이러한 객체에 "새"를 사용하는 단순한 솔루션을 계속 고수 할 것입니다.(나는이 의미에서 "깔끔한"것을 비즈니스 요구 사항이라고 생각하지 않는다.)

그러나 "고객을 표시하는 것"에서 "존재하는 것"과 고객 책임을 분리하는 것이 좋습니다. 솔리드 원리에 대한 좋은 해석입니다.

는 개인적으로 나는 완전히는 이러한 종류의 코드에서 정적 메서드의 어떤 종류를 사용 중지 지금이, 나는 적절한 장소 & 시간에 모든 적절한 서비스 객체를 얻기 위해 DI에 의존하고 있습니다. 솔리드 원리를 더 자세히 설명하기 시작하면 더 많은 수업을 받게 될 것입니다. 일관성을 유지하기 위해 이러한 이름 지정 규칙을 사용해보십시오.

+0

흥미 롭습니다. 단지 "고객" "직원"클래스는 매우 작고 속성과 약간의 생성자가 될 것입니다. 이러한 유형의 클래스는 응용 프로그램의 기초가되었습니다. –

+0

예, 책임은 직원이되는 것입니다. 속성뿐만 아니라 다른 방법도 추가 할 수 있습니다. 그것에 대해 종교적 이유가 없습니다. – krosenvold

1

몇 가지 참고 사항 : 데이터의 서식 디스플레이의 분리가 그대로

  • 일반적으로 SRP는 모두 좋다.
  • 디스플레이 등을 고려하면 서비스의 측면에서 PersonDisplayer는 단일, 상태 비 저장 및 문자열 표시 (IPerson) 기능을 제공한다고 생각합니다. IMHO, 그냥 디스플레이를 제공하는 특별한 클래스 래퍼 어떤 이점을 제공하지 않습니다.
  • 그러나 wpf에 대한 데이터 바인딩을 사용하는 경우 Person이 변경된 경우 PropertyChanged를 전파하는 DisplayablePerson 클래스가있을 수 있습니다. DisplayablePerson 객체를 ObservableCollection에 넣고이를 일부 목록 컨트롤의 ItemsSource로 제공합니다.
  • Container는 인스턴스를 인스턴스화하고 구성하기위한 용도로만 필요합니까? 고객 customer1 = new Customer {FirstName = "Jim", LastName = "Smith"};

  • 사이드 노트에서 객체를 시도했습니다. SomeType> (...) 호출이 가장 빠르고 간단한 솔루션처럼 보였습니다. 그러나, 얼마 후 나는 항상 그 중 하나에 문제가 생겨서 object.Method (type someTypeType, ...)로 끝났다.)

관련 문제