【问题标题】:Constructor injection with two different interfaces (Single Responsibility and Interface Segregation)具有两个不同接口的构造函数注入(单一职责和接口隔离)
【发布时间】:2017-01-12 09:45:10
【问题描述】:

我正在学习 SOLID 原则。我现在正在使用依赖注入和接口隔离原则。我已经掌握了这两者的基础知识,但是当我将它们结合起来时,我感到困惑。这是我的实现..

class Person
{
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public string Name { get; set; }
    public int Age { get; set; }
}

class DisplayPerson
{
    private readonly IWeapon iwep;
    private readonly Person pers;
    public DisplayPerson(IWeapon iwep, Person perss)
    {
        this.iwep = iwep;
        this.pers = perss;
    }

    public void DisplayAll()
    {
        Console.WriteLine("Name: " + pers.Name + "\nAge: " + pers.Age + "\n" + "Weapon: "
            + iwep.weaponName() + "\nDamage: " + iwep.damage().ToString());
    }
}

我创建了另一个类来显示信息,因为 SOLID 中的“S”规则表示一个类不应该做它不应该做的事情。我认为显示这些信息不是它的任务。 (如有错误请指正)

class Sword : IWeapon
{
    private string weaponNames;
    private int damages;
    public Sword(string weaponName, int damage)
    {
        this.weaponNames = weaponName;
        this.damages = damage;
    }

    public string weaponName(){ return this.weaponNames; }

    public int damage(){ return this.damages; }
}

public interface IWeapon
{
    string weaponName();
    int damage();
}

有了这个界面 IWeapon,我现在可以添加许多具有武器名称和伤害的武器。但是如果我想再添加一个具有附加功能的武器,我们必须遵循 ISP 的原则,对吗?所以我在下面创建了这个接口和类。

class Gun : IWeaponV1
{
    private string weaponNames;
    private int damages;
    private int range;

    public Gun(string weaponName, int damage, int range)
    {
        this.weaponNames = weaponName;
        this.damages = damage;
        this.range = range;
    }

    public string weaponName() { return this.weaponNames; }
    public int damage(){ return this.damages; }
    public int ranges() { return this.range; }
}

public interface IWeaponv1 : IWeapon
{
    int range();
}

我是这样实现的..

    static void Main(string[] args)
    {
        DisplayPerson disp = new DisplayPerson(new Sword("Samurai Sword", 100), new Person("Jheremy", 19));
        disp.DisplayAll();
        Console.ReadKey();
    }

我的问题是如何将 IWeaponv1 注入到上面的 DisplayPerson 类中?有可能还是我对 SOLID 原则的理解是错误的。如果您发现我做错了什么或不正确的做法,请纠正我:)

【问题讨论】:

标签: c# design-patterns dependency-injection solid-principles single-responsibility-principle


【解决方案1】:

从理论上讲,您在问题中提出的实现看起来不错;但是,它在现实世界中确实存在一些设计问题。

  1. 虽然单一职责原则确实促进了更具凝聚力的类,但不应将其扩展到将所有功能都移至新类的级别。一个典型的例子是 DisplayPerson 类,它根本不需要(或者应该以不同的方式实现,我将在稍后讨论)。类代表真实世界的对象。在现实世界中,如果你问一个人他们的名字,他们会告诉你他们的名字,而不是将你重定向到另一个告诉你他们名字的人。因此,Person 对象被允许有一种方法来打印它自己的属性,这不会以任何方式违反单一责任原则。武器也是如此。

  2. 如果武器支持此功能/行为,请考虑要打印武器的range。就目前而言,DisplayPerson has-a IWeapon 参考但IWeapon 没有ranges 作为其合同的一部分。那么如何打印武器的range? (range 将无法通过DisplayAll 方法中的iwep 引用访问)。您必须使用typeOfcheck,然后将IWeapon 向下转换为IWeaponV1,以便您可以调用ranges 方法。这种方法的问题是DisplayPerson 现在必须使用两个不同的接口,而不是使用单个接口。 (更不用说违背使用通用接口目的的条件检查)

牢记以上几点,您可以进行以下代码更改:

  1. display 方法添加到IWeapon 接口和Person 类。
  2. Person 类的display 方法中打印人员的属性。同样,在单个武器实现的 display 方法中打印武器的属性

通过上述更改,您可以通过依赖单一接口直接在DisplayPerson 类中的PersonIWeapon 对象上调用display(不违反单一职责原则) :

public void DisplayAll() {
        Console.WriteLine("Person " + pers.display() + "\n has weapon: " + iwep.display());
}

【讨论】:

  • 你好@CKing,你是说DisplayPerson类没用吗?我应该删除它吗?
  • 最后,在这种实现中使用接口是个坏主意,我应该使用简单继承(抽象类),因为它是不可注入的newables?抱歉问了太多问题。你们有不同的论点,我想知道你的观点是什么,因为我很欣赏你的回答。提前致谢。
  • @Jhe DisplayPerson 如果您按照我的建议进行更改就可以了。实际上,该类可以称为PersonView 而不是DisplayPerson。在现实世界中,PersonView 将负责调用相关的 GUI 库来绘制人物及其武器。 Newable 没什么大不了的。它可以通过使用接口来实现,方法是删除DisplayPerson 构造函数并将PersonIWeapon 传递给DisplayAll 方法。我不明白为什么抽象类是必须的。正如我的回答中所解释的那样,接受的答案是错误的。
【解决方案2】:

有时你可以遵循单一责任原则会让人感到困惑,所以没关系。

在回答您的问题之前,我想指出代码中的几个问题。

  1. WeaponName、Damage 和 Range 是数据,它们不是功能。它们应该被声明为属性而不是方法。
  2. 枪应该实现 IWeaponv1 。我认为你误会了。
  3. 所有属性都在构造函数中设置,即构造函数注入。在这种情况下,对于 Person 类或任何其他类,您不需要私有集

最后是你的实际问题

DisplayPerson disp = new DisplayPerson(new Sword("Samurai Sword", 100), new Person("Jheremy", 19));

DisplayPerson disp1 = new DisplayPerson(new Gun("Shotgun Axe", 100,100), new Person("Matt", 22);
//Assuming you have implemented Gun:IWeaponv1 

这看起来不错,它不违反单一责任原则。在这个简单的例子中,你想多了。在上面的示例中,只有一个具有功能的类(显示人员)存在,其余的只是保存数据。当您将数据与功能分开时,这是一件好事。检查单一职责是否被破坏的一种简单方法是检查方法名称与类名称。如果它不合适,那么您就违反了 SRP。

【讨论】:

  • 非常感谢您的回答。也感谢您纠正我的错误 :) 但我认为您在“DisplayPerson disp1 ...”上方的示例无法正常工作,因为您将“枪类”注入 IWeapon。到目前为止,这是我的大问题。
  • 当您执行Gun:IWeaponv1 时,您的问题就会消失。这样做有什么问题?
  • 天啊!你们是天才。对不起,我在 StackOverflow 中编辑了我的错误,我想我也更改了我的应用程序中的代码。再次抱歉。
  • @CarbineCoder 我不同意。代码违反单一职责原则,因为它过度使用误用原则。详细解释见我的回答。
猜你喜欢
  • 2022-11-09
  • 2015-11-03
  • 2019-02-26
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 2021-12-29
  • 1970-01-01
  • 2018-06-08
相关资源
最近更新 更多