6

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

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

但后来我停了下来,想起了一个播客,其中 Bob 叔叔告诉 Scott Hanselman单一职责原则,其中你应该有很多小类,每个类都做一件特定的事情,即 Customer 类不应该有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; }
    }
}
4

5 回答 5

8

我喜欢将单一职责原则视为职责分离的一种实现。在我像你一样开始分班之前,我试着想想每个班级应该负责什么。

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

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

Container的课有点误导。它实际上并不“包含”任何东西。它实际上实现了工厂方法模式,并将受益于被命名为工厂。

此外,您PersonDisplayer的永远不会被实例化,并且可以通过静态方法提供其所有功能,那么为什么不将其设为静态类呢?诸如打印机或保存程序之类的实用程序类是静态的并不少见。除非您需要具有不同属性的打印机的单独实例,否则请使其保持静态。

于 2009-03-18T17:39:10.517 回答
4

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

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

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

于 2009-03-18T17:28:45.987 回答
1

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

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

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

于 2009-03-18T17:22:23.197 回答
1

几点注意事项:

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

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

于 2009-03-18T17:44:14.717 回答
0

您可能会看看IFormattableIFormatProvider

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

于 2009-03-18T18:15:45.080 回答