这是单一责任原则的一个例子吗?

发布于 2024-07-14 20:08:21 字数 2758 浏览 7 评论 0 原文

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

为了获得客户和员工的 Display() 方法,我实际上开始用 Person 抽象类替换我的 IPerson 接口

但后来我停了下来,记得鲍勃叔叔在一个播客中告诉斯科特汉塞尔曼关于单一职责原则,其中你应该有很多小类,每个类都做一件特定的事情,即客户类不应该有一个 Print()Save() 以及 CalculateSalary() 方法,但您应该有一个 CustomerPrinter 类< /em>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; }
    }
}

I made the following code example to learn how to use a generics method signature.

In order to get a Display() method for both Customer and Employee, I actually began replacing my IPerson interface with an Person abstract class.

But then I stopped, remembering a podcast in which Uncle Bob was telling Scott Hanselman about the Single Responsibility Principle in which you should have lots of little classes each doing one specific thing, i.e. that a Customer class should not have a Print() and Save() and CalculateSalary() method but that you should have a CustomerPrinter class and a CustomerSaver class and a CustomerSalaryCalculator class.

That seems an odd way to program. However, getting rid of my interface also felt wrong (since so many IoC containers and DI examples use them inherently) so I decided to give the Single Responsibility Principle a try.

So the following code is different than I have programmed in the past (I would have made an abstract class with a Display() method and got rid of the interface) but based on what I have heard about decoupling and the S.O.L.I.D. principles, this new way of coding (the interface and the PersonDisplayer class) I think this is the right way to go.

I would like to hear if others think the same way on this issue or have experienced positive or negative effects of this (e.g. an unwieldy number of classes each doing one particular thing, 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; }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

青春如此纠结 2024-07-21 20:08:21

我喜欢将单一职责原则视为职责分离。 在我像你们一样开始划分班级之前,我会尝试考虑每个班级应该负责什么。

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

但是,如果打印和保存是更复杂的任务,可能以不同的方式执行,那么就需要专用的 Printer 或 Saver 类,因为现在的职责更加复杂。 创建新类的“复杂性”阈值是非常主观的,并且取决于具体情况,但最终,代码只是我们低等人类理解的抽象,因此使其最直观。

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

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

I like to think of the Single Responsibility Principle as an implementation of separation of duties. Before I start splitting my classes as you have, I try to think of what each class should be responsible for.

Your classes are quite simple and lend themselves well to an abstract class with an implemented Print() and Save() functions as you mentioned. I would tend to keep that design over your current one.

However, if printing and saving were more complicated tasks which might be performed in different ways then a dedicated Printer or Saver class would be warranted, since that responsibility is now more complex. The 'complexity' threshold for making a new class is very subjective and will depend on the exact situation, but in the end, the code is just an abstraction for us lowly humans to understand, so make it such that it's the most intuitive.

You Container class is a little misleading. It doesn't actually 'contain' anything. It actually implements the Factory Method Pattern and would benefit from being named a factory.

Also, your PersonDisplayer is never instantiated and can provide all of its functionality through static methods, so why not make it a static class? It's not uncommon for utility classes such as Printers or savers to be static. Unless you have a need to have separate instances of a printer with different properties, keep it static.

〃温暖了心ぐ 2024-07-21 20:08:21

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

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

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

I think you're on the right track. I'm not entirely sure about the Container class though. I'd generally stick with the simpler solution of just using "new" for these objects unless you have some business-driven need for that interface. (I don't consider "neat" to be a business requirement in this sense)

But the separation of "being" a customer responsibility from "displaying a customer" is nice. Stick with that, it's nice interpretation of SOLID principles.

Personally I have now completely stopped used any kind of static methods in this kind of code, and I rely on DI to get all the right service objects at the right place & time. Once you start elaborating further on the SOLID principles you'll find you're making a lot more classes. Try to work on those naming conventions to stay consistent.

血之狂魔 2024-07-21 20:08:21

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

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

无论如何,我不认为应该严格遵循这些范式。 例如,我不确定您的情况下接口和抽象类之间有什么区别。 但话又说回来,我是一个 C++ 程序员,而不是 C# 程序员......

Well, I've never heard of this 'single responsibility principle' before, but what it appears to me that what you're doing by having these CustomerPrinter class and CustomerSaver classes is simply converting classes back to structs, and de-object-orienting everything.

For example, this would mean that different customer types would need different cases in the CustomerPrinter class if they needed to be printed differently. But as I understand it, one of the point of OO organisation, and of using inheritance trees and all that, is to do away with the need of this CustomerPrinter to know how to print everything: Customers know how to print themselves.

I don't believe in following these paradigms rigidly in any case. For example I'm unsure what the difference between an Interface and an Abstract Class is in your case. But then again I'm a C++ programmer not a C# programmer...

情话墙 2024-07-21 20:08:21

几点说明:

  • 一般来说,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, ...)

A few notes:

  • Generally speaking SRP is all good, as is separation of display formatting from data.
  • Considering display etc. I would rather think in terms of services, i.e. a PersonDisplayer is single, stateless and offers a string Display(IPerson) function. IMHO, a special class wrapper just to provide display does not provide any advantage.
  • However, if you used data binding for wpf, you might have a DisplayablePerson class that would propagate PropertyChanged if Person changed. You would put DisplayablePerson objects into ObservableCollection and serve it as ItemsSource of some list control.
  • What do you need Container for, is it only for instantiating and configuring instance?Try then Customer customer1 = new Customer{FirstName= "Jim", LastName= "Smith"};

  • On a side note, I've tried object.Method < SomeType>(...) invocation a few times, as it seemed quickest and simplest solution. However, after some time I've always run into troubles with that one and ended up with object.Method(Type someTypeType, ...)

好多鱼好多余 2024-07-21 20:08:21

您可能会查看 IFormattableIFormatProvider

该框架有格式化类来提供支持。

You might have a look at IFormattable and IFormatProvider.

The framework has formatting classes for support.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文