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

时间:2017-01-12 09:45:11

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

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

我的问题是我如何向上面的DisplayPerson类注入IWeaponv1?是否有可能或我对SOLID原则的理解是错误的。如果你发现我做错了什么或做错了,请纠正我。)

2 个答案:

答案 0 :(得分:1)

从理论的角度来看,你在问题中提出的实现看起来很好;但是,它确实在现实世界中存在一些设计问题。

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

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

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

    1. display方法添加到IWeapon接口和Person类。
    2. 使用display课程的Person方法打印此人的媒体资源。同样,在单个武器实施的display方法中打印武器的特性
    3. 通过以上更改,您可以依靠单个界面直接在PersonIWeaponDisplayPerson对象上调用显示(不会违反单一责任原则):

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

答案 1 :(得分:0)

有时令人困惑的是你可以遵循单一责任原则,所以没关系。

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

  1. WeaponName,Damage和Range是数据,它们不是功能。它们应该被声明为财产而不是方法。
  2. 枪应该实施IWeaponv1。我认为你错误地错过了它。
  3. 所有属性都在构造函数中设置,即。构造函数注入。在这种情况下,对于Person类或任何其他类
  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 
    

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