【问题标题】:It this an example of the Single Responsibility Principle?这是单一职责原则的一个例子吗?
【发布时间】:2009-03-18 17:11:52
【问题描述】:

我制作了以下代码示例来学习如何使用泛型方法签名。

为了为 Customer 和 Employee 获得一个 Display() 方法,我实际上开始用 Person 抽象类替换我的 IPerson 接口 >。

但后来我停了下来,想起了一个播客,其中 Bob 叔叔告诉 Scott Hanselman 关于单一职责原则,你应该有很多小班,每个班都做一件特定的事情,即客户类不应该有 Print()Save()CalculateSalary() 方法,但你应该有一个 CustomerPrinter 类和一个 CustomerSaver 类和一个 CustomerSalaryCalculator 类

这似乎是一种奇怪的编程方式。然而,摆脱我的界面也感觉不对(因为有这么多 IoC 容器和 DI 示例固有地使用它们)所以我决定尝试单一职责原则。

所以以下代码与我过去编写的代码不同(我会使用 Display() 方法创建一个抽象类并摆脱接口)但基于我所拥有的听说过解耦和 SOLID原则,这种新的编码方式(界面和 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; }
    }
}

【问题讨论】:

标签: c# design-patterns solid-principles


【解决方案1】:

我喜欢将Single Responsibility Principle 视为separation of duties 的实现。在我像你们一样开始分班之前,我试着想想每个班级应该负责什么。

您的类非常简单,并且很适合具有您提到的Print()Save() 函数的抽象类。我倾向于保留该设计而不是您当前的设计。

但是,如果打印和保存是更复杂的任务,可能以不同的方式执行,那么专用的PrinterSaver 类将是必要的,因为该职责现在更加复杂。创建一个新类的“复杂性”阈值是非常主观的,将取决于具体情况,但最后,代码只是我们低等人类理解的抽象,所以让它最直观。

Container 班级有点误导。它实际上并不“包含”任何东西。它实际上实现了Factory Method Pattern,并且将受益于被命名为工厂。

另外,您的PersonDisplayer 永远不会被实例化,并且可以通过静态方法提供其所有功能,那么为什么不将其设为静态类呢? utility classes (例如打印机或储蓄器)是静态的并不少见。除非您需要具有不同属性的打印机的单独实例,否则请保持静态。

【讨论】:

  • PersonDisplayer 在自己的静态 SimpleDisplay 方法中实例化
  • 对,这是我在 PHP 中经常使用的一种模式:一个具有静态方法的类,它实例化自己的类,然后在其上运行某个内部方法,基本上只是让开发人员能够编写一个-liners 而不是实例化一个类然后调用它的方法。
  • 这是不必要的对象创建,并且会因为没有添加功能而导致性能损失。 PersonDisplayer 没有理由被实例化,所有功能都可以静态提供。
【解决方案2】:

我认为你在正确的轨道上。不过,我并不完全确定 Container 类。我通常会坚持使用更简单的解决方案,即对这些对象使用“new”,除非您对该接口有一些业务驱动的需求。 (从这个意义上说,我不认为“整洁”是业务需求)

但是将“成为”客户责任与“展示客户”分开是很好的。坚持下去,这是对 SOLID 原则的很好的诠释。

就个人而言,我现在已经完全停止在这种代码中使用任何类型的静态方法,我依靠 DI 在正确的时间和地点获取所有正确的服务对象。一旦你开始进一步阐述 SOLID 原则,你会发现你正在制作更多的课程。尝试使用这些命名约定以保持一致。

【讨论】:

  • 有趣,我的“customer”和“employee”类似乎会很小,可能只是属性和一些构造函数,这些类型的类曾经是应用程序的基石
  • 是的,他们的责任是“成为”一名员工。您可能会发现您还将添加其他方法,而不仅仅是属性。没有理由对此深信不疑。
【解决方案3】:

嗯,我以前从未听说过这种“单一责任原则”,但在我看来,你通过拥有这些 CustomerPrinter 类和 CustomerSaver 类所做的只是将类转换回结构,然后 -一切都面向对象。

例如,这意味着如果需要以不同方式打印不同的客户类型,则需要在 CustomerPrinter 类中使用不同的案例。但据我了解,OO 组织以及使用继承树和所有这些的重点之一是消除 CustomerPrinter 知道如何打印所有内容的需要:客户知道如何打印自己。

我不相信在任何情况下都严格遵循这些范式。例如,我不确定在您的情况下接口和抽象类之间的区别是什么。但话说回来,我是 C++ 程序员而不是 C# 程序员......

【讨论】:

  • 使用接口我可以强制我的类具有某些方法签名,但我不能为它们提供具有功能的方法,因为我需要一个抽象类,我将接口视为“轻量级合同” " 我的课程需要在应用程序中智能传递
【解决方案4】:

一些注意事项:

  • 一般而言,SRP 都很好,显示格式与数据的分离也是如此。
  • 考虑显示等。我宁愿从服务方面考虑,即 PersonDisplayer 是单一的、无状态的并提供字符串 Display(IPerson) 功能。恕我直言,仅提供显示的特殊类包装器没有任何优势。
  • 但是,如果您对 wpf 使用数据绑定,则您可能有一个 DisplayablePerson 类,如果 Person 发生更改,该类将传播 PropertyChanged。您可以将 DisplayablePerson 对象放入 ObservableCollection 并将其用作某些列表控件的 ItemsSource。
  • 您需要 Container 做什么,是否仅用于实例化和配置实例?然后尝试 Customer customer1 = new Customer{FirstName="Jim", LastName="Smith"};

  • 顺便说一句,我尝试了几次 object.Method (...) 调用,因为它似乎是最快和最简单的解决方案。但是,一段时间后,我总是在那个问题上遇到麻烦,最终得到了 object.Method(Type someTypeType, ...)

【讨论】:

    【解决方案5】:

    您可以看看IFormattableIFormatProvider

    框架具有支持的格式化类。

    【讨论】:

      猜你喜欢
      • 2011-04-19
      • 2012-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多