Создание экземпляров разных классов в зависимости от опции в меню

Я работаю над «простой» игрой для развлечения, вроде Factorio, но не основанной на сетке. Теперь мне нужна какая-то система, чтобы отслеживать все мои различные типы зданий и позволять игроку выбирать тип в меню. Меню обрабатывается классом UI, и этот класс вызывает методы класса Game для проверки денег и прочего. Затем класс Game вызывает класс World, чтобы добавить любое здание, выбранное игроком. По крайней мере, это важный момент, я думаю.

Как у меня сейчас, для хранения различных типов зданий у меня есть класс с именем BuildInstruction с подклассами BuildingBuildInstruction и ConnectionBuildInstruction. Эти классы просто содержат Action‹> thingamabobs, которые возвращают здание/соединение при задании мировой координаты/двух точек соединения.

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

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

Благодарю вас!

И кстати, вот код, если это поможет понять:

Это то, что я использую для передачи опций в виде иерархии в меню:

public class Category<T>
{
    public string Label { get; private set; }

    public T Me { get; private set; }

    List<Category<T>> children = new List<Category<T>>();

    public IEnumerable<Category<T>> Children => children;

    public bool HasChildren => children.Count() > 0;

    public Category(string label, List<Category<T>> _children)
    {
        Label = label;
        children = _children;
    }

    public Category(string label, T me)
    {
        Label = label;
        Me = me;
    }
}

Это класс BuildInstruction с подклассами и статическим списком‹> объектов BuildInstruction, которые я затем передаю в меню.

public abstract class BuildInstruction
{
    public static Category<BuildInstruction> BuildInstructions;

    static BuildInstruction()
    {
        BuildInstructions = new Category<BuildInstruction>("Build", new List<Category<BuildInstruction>>()
        {
            new Category<BuildInstruction>("Powergrid", new List<Category<BuildInstruction>>()
            {
                new Category<BuildInstruction>("Powerline", new ConnectionBuildInstruction(
                    (start, end) =>
                    new Powerline(start as IPowerlineConnectable, end as IPowerlineConnectable)
                ))
            }),
            new Category<BuildInstruction>("Logistics", new List<Category<BuildInstruction>>()
            {
                new Category<BuildInstruction>("Pipeline", new ConnectionBuildInstruction(
                    (start, end) =>
                    new Pipeline(start as IPipelineConnectable, end as IPipelineConnectable)
                )),
                new Category<BuildInstruction>("Stockpile", new BuildingBuildInstruction(
                    (worldPos) =>
                    new Stockpile(worldPos)
                ))
            })
        });
    }

    public string Name { get; protected set; }
}

public class BuildingBuildInstruction : BuildInstruction
{
    Func<PointF, Building> BuildFunction;

    public BuildingBuildInstruction(Func<PointF, Building> buildFunction)
    {
        BuildFunction = buildFunction;

    }

    public Building Build(PointF worldPos)
    {
        return BuildFunction(worldPos);
    }
}

public class ConnectionBuildInstruction : BuildInstruction
{
    Func<IConnectable, IConnectable, Connection> BuildFunction;

    public ConnectionBuildInstruction(Func<IConnectable, IConnectable, Connection> buildFunction)
    {
        BuildFunction = buildFunction;
    }

    public Connection Build(IConnectable start, IConnectable end)
    {
        return BuildFunction(start, end);
    }
}

И, наконец, класс Game:

public class Game
{
    World World = new World();

    public Game()
    {

    }

    public void Update()
    {

    }

    public void Draw(Graphics g)
    {
        World.Draw(g);
    }

    //-----------------------------------------------------

    public void BuyBuilding(Building building)
    {
        if (true) //Check money and whatnot...
        {
            if (ConstructBuilding(building))
            {
                //Success, use money and stuff
            }
            else
            {
                //Fail. Feedback!
            }
        }
        else
        {
            //Not enough money or whatnot. Feedback!
        }
    }

    bool ConstructBuilding(Building building)
    {
        return World.AddWorldObject(new ConstructionSite(building));
        //This ConstructionSite class is just a thing that makes it so that 
        //the actual building-process takes some time in-game
    }
}

Я думаю, что класс UI будет просто сбивать с толку и содержать слишком много кода (уже слишком много?), но он просто вызывает метод BuyBuilding() в Game. На данный момент у меня есть метод BuyBuilding(), который принимает объект Building, но я действительно не знаю, хороший ли это способ сделать это...

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

Еще раз спасибо! :D


person Evil_Bengt    schedule 26.01.2018    source источник
comment
Я не уверен, что понял вопрос, но, возможно, взгляните на шаблон проектирования Abstract Factory.   -  person Klitos Kyriacou    schedule 26.01.2018
comment
@KlitosKyriacou Спасибо за ранний ответ, я проверю! ????   -  person Evil_Bengt    schedule 26.01.2018


Ответы (1)


Есть много способов сделать это

одним из вариантов будет класс сборки

public class BuildBuilding
{
    public Point Location{get;set;}
    public BuildingType Type{get;set;}

    public void Create(){
        ///logic for creation here
    }
}

это позволит вам подключить INotifyPropertyChanged или какую-либо другую функцию мониторинга, затем ваш пользовательский интерфейс можно будет использовать для установки свойств класса, например, мыши для местоположения и меню для ввода

если вам не нужен такой уровень сложности, может работать простая статическая функция создателя, это то, что вы пробовали

public abstract class BuildingType 
{

    public static Building Create(Point location, BuildingType type){
        ///logic for creation here
    }
}

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

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

public abstract class BuildingType 
{
    public abstract Building Create(Point location);//logic in child
}

public interface IBuildable
{
    void Create(Point location)
}

любой из них будет применять абстрактный дескриптор, который вы можете использовать для определения дескриптора метода без поведения, здесь вы можете подключить свой пользовательский интерфейс к каждому типу, а затем вызвать общую функцию при активации

person MikeT    schedule 26.01.2018