具有两个不同接口的构造函数注入(单一职责和接口隔离)

Constructor injection with two different interfaces (Single Responsibility and Interface Segregation)

我正在学习 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());
    }
}

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

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。

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 class?是否可能,或者我对 SOLID 原则的理解是错误的。如果您发现我做错了什么或不好的做法,请纠正我:)

有时会混淆 far you can follow 单一职责原则,所以没关系。

在回答您的问题之前,我想指出一些关于代码的问题。

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

终于到了你的实际问题

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 

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

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

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

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

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

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

通过上述更改,您可以依靠单个界面(不带 违反 单一职责原则) :

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