Есть ли хороший способ развязки аналогичного кода, полученного наследованием того же интерфейса в c #? - PullRequest
0 голосов
/ 21 июня 2019

Допустим, в коде у нас есть интерфейс 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 ]

2 голосов
/ 21 июня 2019

Я бы заменил интерфейс на абстрактный базовый класс с реализациями по умолчанию, а затем разбил бы метод 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 реализовать его.

0 голосов
/ 21 июня 2019

Вот базовый пример его абстрактной версии класса.

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

Вы заметите, что Enemy1 и Enemy2 работают одинаково, но задают разные свойства, которые при вызове Attack метод будет отображаться соответственно.Интересная часть, на которую я хочу обратить ваше внимание, это класс Enemy3, который переопределяет метод.Это выполнимо, сделав метод Virtual в абстрактном классе Enemy.

Это переопределение позволяет по-прежнему вызывать метод базового класса Attack в классе Enemy PLUS, также он отображает пользовательское значение,Так что это очень гибкий.

Обратите внимание, что это прекрасно работает для предоставленного примера, но, подойдя к названию вашего класса, я легко могу предположить, каков реальный проект, и это не правильный способ сделать это.Вы не должны создавать разные классы для каждого врага, но это выходит за рамки вопроса.

...