Что является альтернативой статическим производным классам, которые не разрешены в c#

#c# #class

Вопрос:

У меня есть класс покемонов, который является абстрактным классом. Я хочу создать кучу покемонов, которые ведут себя аналогично. В настоящее время в нем есть поля: Имя вида и здоровье. У меня есть несколько классов покемонов, таких как Бульбасавр, Венузавр, Чармелеон и т. Д. Каждый из них имеет разное статическое значение для данного конкретного типа покемонов. Я создаю экземпляры этих покемонов, потому что не все экземпляры одного и того же типа покемонов будут точно одинаковыми. У них будут разные ходы, и я могу добавить функции, такие как псевдонимы или уровни.

 abstract class Pokemon
    {
        string speciesName;
        int health;
        bool Fainted
        {
            get
            {
                if (health == 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
        public Pokemon(string speciesName, int health)
        {
            
        }
        public void TakeDamage(int damage)
        {
            health -= damage;
        }
    }
    class Bublasaur :Pokemon
    {
        Move[] possibleMoves = {new Tackle() };
        static int health = 60;
        static string speciesName = "Bublasaur";
        Bublasaur() :base(speciesName, health)
        {
            
        }
    }
 

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

 abstract class Move
    {
        int damage;
        string name;
        public Move(int damage, string name)
        {
            this.damage = damage;
            this.name = name;
        }
    }
    class Tackle :Move
    {
        static int damage = 20;
        static string name = "Tackle";
        public Tackle():base(damage,name)
        {

        }
    }
    class Ember: Move
    {
        static int damage = 30;
        static string name = "Ember";
        public Ember() : base(damage, name)
        {

        }
    }
 

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

Комментарии:

1. Можете ли вы привести пример того, как Pokemon происходит вызов Move класса? Как он функционирует?

2. Если бы это был я, я бы, вероятно, создал свойства только для чтения с именем Damage и Name в Move базовом классе. В подклассах (например Tackle ) я бы сделал это следующим образом: const int damage = 20; const string name = "Tackle"; и затем использовал бы их при вызове конструктора базового класса. A const -это в основном символическое имя, связанное с неизменяемым значением. Оставив все как есть (с абстрактным базовым классом и подклассами), вы сможете реализовать настраиваемое поведение в подклассах, если вы решите это сделать.

3. «Однако создание экземпляра каждого хода для прикрепления к каждому созданному мной покемону кажется не очень интуитивным». Почему ты так думаешь? На самом деле, я бы избавился от всей вашей иерархии наследования для Move объектов , которые могут быть доступны через статические свойства в классе, а также иметь Tackle и Ember быть Move объектами, к которым можно получить доступ через статические свойства в Move классе. например Move[] possibleMoves = { Move.Tackle };

4. Возможно, вам захочется прочитать посты Эрика Липперта » Волшебники и воины «, в которых он обсуждает трудности моделирования логики и правил игры на C#.

Ответ №1:

Tackle и Ember не расширяйте поведение Move , поэтому иерархия наследования кажется излишней.

У вас может быть один класс Move , содержащий статические ссылки на различные экземпляры:

 class Move
{
    int damage;
    string name;
    private Move(int damage, string name)
    {
        this.damage = damage;
        this.name = name;
    }

    public static readonly Move Tackle = new Move(20, "Tackle");
    public static readonly Move Ember = new Move(30, "Ember");
    // Other moves..
}
 

Ответ №2:

Перемещение — это действие, которое может выполнять покемон. Поэтому я думаю, что будет лучше, если он будет определен как интерфейс и что они не должны быть статичными.

Затем вы можете создавать такие движения, как Снасть, Уголек и т.д.

публичный интерфейс iMove { публичный ввод { get; } публичное имя строки { get; } }

 public class Tackle : IMove
{        
    public Tackle()
    {
    }

    //You can still set defaults per class
    public int Damage { get;  } = 30;  
    public string Name { get;  } = "Tackle";
}
 

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

Я бы также использовал перечисление для названия хода (и покемона), чтобы у вас не было проблем с прописными/строчными буквами, орфографическими ошибками и т. Д.

Я бы также посмотрел на использование фабрики для создания покемонов и, возможно, даже ходов (если вы еще этого не сделали).

Я признаю, что не знаю об игре и персонажах в Pokemon, так что, возможно, я неправильно понимаю, но я не понимаю, почему в вашем Покемоне это здоровье должно быть статичным, так как это означает, что все экземпляры этого Покемона будут иметь одинаковый уровень здоровья. Может быть, вы имеете в виду, что для самого покемона должен быть Синглтон?