是否有很好的方法来解耦通过继承c#中的相同接口而获得的相似代码?

时间:2019-06-21 16:51:05

标签: c# oop interface polymorphism decoupling

比方说,在代码中,我们有一个IEnemy接口,上面有一个称为Attack()的方法。还要说,我们有五个来自IEnemy界面的敌人。在这三个类中,我们使用Attack方法的完全相同的实现。在其中之一中,我们还使用相同的代码,但在方法中的某处更改了一行或两行代码。并且,在上一类中,我们仍然具有相同的实现,但在方法中的某处添加/删除了一行或两行代码。您有解耦此代码的建议吗?

我尝试覆盖该方法,以防万一我们在方法中间进行某些更改。 我尝试使用委托作为参数,当我们想在方法中的其他地方进行更改时,该参数将不起作用。 我尝试在接口上使用扩展方法来进行默认实现,但是其中两个类仍具有解耦的代码。

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");
    }
}

2 个答案:

答案 0 :(得分:2)

我将使用具有默认实现的抽象基类替换该接口,然后将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实现它。

答案 1 :(得分:0)

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

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类,它覆盖了该方法。这可以通过使Virtual抽象类中的方法Enemy来实现。

此重写允许仍调用类Attack中的基类方法Enemy加上它还会显示一个自定义值。因此它非常灵活。

请注意,这对于所提供的示例非常有效,但是通过按类的名称进行操作,我可以轻松地假设实际项目是什么,而这不是正确的方法。您不应该为每个敌人创建一个不同的类,但这超出了问题的范围。