c#中继承相同接口得到的相似代码有没有好的解耦方法?

Is there a good way of decoupling similar code obtained by inheriting the same interface in c#?

比方说,在代码中,我们有一个 IEnemy 接口,它有一个名为 Attack() 的方法。假设我们有五个派生自 IEnemy 接口的敌人。在其中三个 classes 上,我们使用完全相同的 Attack 方法实现。在其中一个中,我们也使用相同的代码,但在方法的某处更改了一两行代码。而且,在最后一个 class 中,我们仍然有相同的实现,但在方法的某处有一两行代码 added/removed。您对解耦这段代码有什么建议吗?

我已经尝试重写该方法,如果我们在方法中间更改某些内容,该方法将不起作用。 我尝试使用委托作为参数,当我们想要更改方法中其他地方的某些内容时,它不起作用。 我尝试使用接口的扩展方法来创建默认实现,但是 class 中的两个仍然有解耦代码。

interface IEnemy
{
    void Attack();
}

class Enemy1 : IEnemy
{
    public void Attack()
    {
        Console.WriteLine("This is an enemy");
        Console.WriteLine("Enemy is jumping");
        Console.WriteLine("Enemy is attacking");
    }
}

class Enemy2 : IEnemy
{
    public void Attack()
    {
        Console.WriteLine("This is an enemy");
        Console.WriteLine("Enemy is jumping");
        Console.WriteLine("Enemy is attacking");
    }
}

class Enemy3 : IEnemy
{
    public void Attack()
    {
        Console.WriteLine("This is an enemy");
        Console.WriteLine("Enemy is jumping");
        Console.WriteLine("Enemy is attacking");
    }
}

//Let's say this enemy is not capable of jumping, so we want to remove the code that says enemy is jumping.
class Enemy4 : IEnemy
{
    public void Attack()
    {
        Console.WriteLine("This is an enemy");
        Console.WriteLine("Enemy is attacking");
    }
}

//Let's say this is the boss and instead of jumping, it will roar.
//So we want to change the code that says enemy is jumping to enemy is roaring.
class Enemy5 : IEnemy
{
    public void Attack()
    {
        Console.WriteLine("This is an enemy");
        Console.WriteLine("Enemy is roaring");
        Console.WriteLine("Enemy is attacking");
    }
}

这是它的抽象 class 版本的基本示例。

public abstract class Enemy
{
    public bool CanJump { get; set; } = false;
    public bool CanRoar { get; set; } = false;
    public bool CanAttack { get; set; } = false;

    public virtual void Attack()
    {
        Console.WriteLine("This is an enemy");

        if (CanJump)
        {
            Console.WriteLine("Enemy is jumping");
        }

        if (CanRoar)
        {
            Console.WriteLine("Enemy is roaring");
        }

        if (CanAttack)
        {
            Console.WriteLine("Enemy is attacking");
        }
    }
}

public class Enemy1 : Enemy
{
    public Enemy1()
    {
        CanJump = true;
        CanRoar = true;
    }
}

public class Enemy2 : Enemy
{
    public Enemy2()
    {
        CanRoar = true;
        CanAttack = true;
    }
}

public class Enemy3 : Enemy
{
    public Enemy3()
    {
        CanRoar = true;
        CanAttack = true;
    }

    public override void Attack()
    {
        base.Attack();

        Console.WriteLine("Custom thing");
    }
}

您会注意到 Enemy1Enemy2 的工作方式相同,但设置了不同的属性,当您调用 Attack 方法时,这些属性将相应地显示。我希望您注意的有趣部分是覆盖该方法的 Enemy3 class。这可以通过在 Enemy 抽象 class.

中创建方法 Virtual 来实现

此覆盖允许仍然在 class Enemy 中调用基本 class 方法 Attack 并且它还显示自定义值。所以它非常灵活。

请注意,这对于提供的示例非常有效,但是通过使用您的 class 的名称,我可以轻松地假设真正的项目是什么,这不是正确的方法。您不应该为每个敌人创建不同的 class,但这超出了问题的范围。

我会用默认实现的抽象基础 class 替换接口,然后将 Attack 方法分解为单独的可重写步骤。我为完全拥有自己的攻击模式的敌人制作了 Attack 虚拟。

abstract class BaseEnemy {
    public virtual void Attack() {
        AttackIdentify();
        AttackSignal();
        AttackAttack();
    }

    protected virtual void AttackIdentify() {
        Console.WriteLine("This is an enemy");
    }

    protected virtual void AttackSignal() {
        Console.WriteLine("Enemy is jumping");
    }

    protected virtual void AttackAttack() {
        Console.WriteLine("Enemy is attacking");
    }
}

class Enemy1 : BaseEnemy {
    protected override void AttackIdentify() {
        Console.WriteLine("This is an enemy 1");
    }
}

class Enemy2 : BaseEnemy {
}

class Enemy3 : BaseEnemy {
    protected override void AttackIdentify() {
        Console.WriteLine("This is an enemy 3");
    }
}

//Let's say this enemy is not capable of jumping, so we want to remove the code that says enemy is jumping.
class Enemy4 : BaseEnemy {
    protected override void AttackSignal() { }
}

//Let's say this is the boss and instead of jumping, it will roar.
//So we want to change the code that says enemy is jumping to enemy is roaring.
class Enemy5 : BaseEnemy {
    protected override void AttackSignal() {
        Console.WriteLine("Enemy is roaring");
    }
}

如果您仍然需要接口 IEnemy,您可以 BaseEnemy 实现它。