Структура контроллера / Шаблон / Шаблон Вопрос - PullRequest
1 голос
/ 21 октября 2019

Введение

Резюме:

В настоящее время я пытаюсь реорганизовать свои ObjectControllers и хочу добиться определенного поведения. То есть любой дочерний класс этого контроллера абстрактных объектов наследует все поведение от своих суперклассов. Значение Controllable ObjectController должно реализовывать поведение Selectable. И измените цель Controllable, а также сам Controllable (его мастер) в соответствии с указанным поведением (предположим, это меняет bool).

Чтобы уточнить: private void method start - это крюк, специфичный для единицы, весьЦель этого проекта состоит в том, чтобы перехватить все специфичные для пользовательского интерфейса хуки и поместить эти специфичные для хука функции поверх друг друга, используя наследование.

Код

using System.Collections;
using System.Collections.Generic;

namespace example
{

    public class Example
    {
        public double value;
        public Example(double d) { this.value = d; }
    }

    public abstract class ObjectController
    {
        public Example target;

        internal ObjectController master;
        internal List<System.Delegate> startList;

        public void Initialize(ObjectController o, Example target)
        {
            this.master = o;
            this.target = target;
            startList = new List<System.Delegate>() { };
            master.startList.Add(new System.Action(() => StartPreHook(o)));
        }

        // called before first frame
        public void Start()
        {
            Initialize(this, target);
            foreach (System.Delegate o in startList.ToArray())
            {
                o.DynamicInvoke();
            }
        }

        // abstract funtions to be implemented in derived classes:
        public abstract void StartWrapper();

        // abstract function pre-hooks:
        public abstract void StartPreHook(ObjectController master);
    }

    public class Selectable : ObjectController
    {
        // internal variables
        internal bool isSelected;

        // ui-controls
        public override void StartWrapper()
        {
            isSelected = true;
        }

        // pre-hooks ui-controls
        public override void StartPreHook(ObjectController master)
        {
            StartWrapper();
        }
    }

    public class Controllable : Selectable
    {
        public bool selectable = true;

        public override void StartWrapper()
        {
            if (selectable) new Selectable().Initialize(this, target);
        }

        // pre-hooks ui-controls
        public override void StartPreHook(ObjectController master)
        {
            base.StartPreHook(master);
            StartWrapper();
        }
    }
}

Пример вызова

using System;
using example;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Example e = new Example(2.0);
            Controllable i = new Controllable
            {
                target = e
            };
            i.Start();
            Console.WriteLine("is selected: " + i.isSelected.ToString());
        }
    }
}

Текущее поведение

Текущий код не перезаписывает основную переменную ObjectControllers, а перезаписывает свою собственную переменную.

Выбираемые перезаписи выбираются равными true, но только внутри себя, поэтомупример показывает ложь.

Преднамеренное поведение

Вызов функции Start () для любого дочернего класса ObjectController предназначен для вызова всех пусковых упаковщиков из всех его родителей (что он не делает, поскольку перезаписывает). старт-оберток). Я не хочу звонить base.StartWrapper() из любого дочернего класса. Значение в показанном примере предполагаемого поведения будет выводить true, а не false.

Исправления

Отражение

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

List

Наиболее очевидным решением было бы реализовать список ObjectControllers в абстрактном классе и циклически выполнять функции, предоставляемые этими,Я почти уверен, что таким образом смогу избавиться и от пары оболочек.

Вопрос:

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

1 Ответ

0 голосов
/ 23 октября 2019

Проблема:

Этот фрагмент кода должен был вызывать StartWrapper базового класса в base.StartPreHook(master):

 public override void StartPreHook(ObjectController master)
 {
     base.StartPreHook(master);
     StartWrapper();
 }

Определение base.StartPreHook(master) было:

// pre-hooks ui-controls
public override void StartPreHook(ObjectController master)
{
    StartWrapper();
}

Но StartWrapper() здесь перезаписан StartWrapper, а не StartWrapper из его базового класса.

Решение:

Общий интерфейс:

using System;
using System.Collections.Generic;
using System.Text;

namespace controllertest
{
    public interface IController<T>
    {
        void functionwrapper(T master);
        void initialize();
        T getobj(IController<T> master);
    }
}

Классы контроллера:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;

namespace controllertest
{
    public class MyExampleBaseClass{
        public double value;
}
    public abstract class Controller<T> : MyExampleBaseClass, IController<T>
    {
        public Queue<Controller<T>> registredcontrollers;
        public T master;
        public void wrapperfunction()
        {
            registredcontrollers = new Queue<Controller<T>>() { };
            this.master = getobj(this);
            registerController(this);
            _functionPreHook(this);
        }

        public void registerController(Controller<T> newController)
        {
            newController.master = master;
            newController.initialize();
            registredcontrollers.Enqueue(newController);
        }

        public abstract void initialize();
        public abstract void _functionPreHook(Controller<T> master);
        public abstract void functionwrapper(T master);
        public abstract T getobj(IController<T> master);
    }

    public abstract class AbstractControllerImplementation<T>:Controller<T>
    {
        public sealed override void _functionPreHook(Controller<T> master)
        {
            // iterate through the registred controllers dynamically:
            var enumerator = registredcontrollers.GetEnumerator();
            int position = -1;
            // enumerators start at index -1 so move to first element:
            enumerator.MoveNext();
            position++;
            // execute first element
            var currentController = enumerator.Current;
            currentController.functionwrapper(this.master);
            int length = registredcontrollers.Count;
            while (position < length)
            {
                // since functionwrapper can modify the queue we have to get a new enumerator:
                enumerator = registredcontrollers.GetEnumerator();
                // advance itterator:
                position++;
                int newEnumPos = -1;
                newEnumPos++;
                enumerator.MoveNext();
                while (newEnumPos < position && newEnumPos < length-1)
                {
                    newEnumPos++;
                    enumerator.MoveNext();
                }
                // call functionwrapper:
                currentController = enumerator.Current;
                currentController.functionwrapper(this.master);
                // adjust length
                length = registredcontrollers.Count;
            }
        }

        public sealed override T getobj(IController<T> master)
        {
            return (T)master;
        }

        public void setAttr<T2>(string name, T obj, T2 value)
        {
            try
            {
                obj.GetType().GetField(name).SetValue(obj, value);
            }
            catch
            {
                this.GetType().GetField(name).SetValue(this, value);
            }
        }

        public void setAttrThisAndObj<T2>(string name, T obj, T2 value)
        {
            try
            {
                obj.GetType().GetField(name).SetValue(obj, value);
            }
            catch
            {
            }
            try
            {
                this.GetType().GetField(name).SetValue(this, value);
            }
            catch
            {
            }
        }

        public T2 getAttr<T2>(string name, T obj)
        {
            return (T2)obj.GetType().GetField(name).GetValue(obj);
        }
        public T2 getAttrElseSetDefault<T2>(string name, T obj, T2 preset)
        {
            try
            {
                var res = getAttr<T2>(name, obj);
                if (res != null) return res;
                else setAttr(name, obj, preset);
                return preset;
            }
            catch
            {
                setAttr(name, obj, preset);
                return preset;
            }
        }
        public T2 getAttrElseSetDefaultBoth<T2>(string name, T obj, T2 preset)
        {
            try
            {
                var res = getAttr<T2>(name, obj);
                if (res == null) res = preset;
                this.GetType().GetField(name).SetValue(this,res);
                return res;
            }
            catch
            {
                this.GetType().GetField(name).SetValue(this, preset);
                setAttr(name, obj, preset);
                return preset;
            }
        }
    }
    public abstract class ControllerImplementation : AbstractControllerImplementation<ControllerImplementation>
    {
    }
    public class Selectable:ControllerImplementation
    {
        public bool isclicked;
        public bool isselected;

        public override void functionwrapper(ControllerImplementation master)
        {
            if (isSelected()) Console.WriteLine(">>> SELECTABLE FUNCTIONWRAPPER WAS CALLED!");
            if (isClicked()) Console.WriteLine("--- SELECTABLE IS CLICKED");
        }

        public override void initialize()
        {
            this.isclicked=getAttrElseSetDefault<bool>("isclicked", master, false);
            this.isselected = getAttrElseSetDefault<bool>("isselected", master, false);
            if(isSelected()) Console.WriteLine("INITIALIZING SELECTABLE!");
        }
        public bool isClicked()
        {
            try
            { 
                return getAttr<bool>("isclicked", master);
            }
            catch
            {
                return this.isclicked;
            }
        }
        public bool isSelected()
        {
            try
            {
                return getAttr<bool>("isselected", master);
            }
            catch
            {
                return this.isselected;
            }
        }
    }

    public class Controllable : Selectable
    {
        public bool canBeSelected;
        public bool canBeControlled;
        public override void functionwrapper(ControllerImplementation master)
        {
            if (isSelected()) Console.WriteLine(">>> CONTROLLABLE FUNCTIONWRAPPER WAS CALLED!");
            if (CanBeSelected()) {
                if (isSelected()) Console.WriteLine("--- CONTROLLABLE IS SELECTABLE, REGISTERING COMPONENT!");
                var s = new Selectable();
                master.registerController(s);
            }

        }
        public override void initialize()
        {
            this.canBeSelected = getAttrElseSetDefault("canBeSelected", master, true);
            this.canBeControlled = getAttrElseSetDefault("canBeControlled", master, true);
            if (isSelected()) Console.WriteLine("INITIALIZING CONTROLLABLE!");
        }
        public bool CanBeSelected()
        {
            try
            {
                return getAttr<bool>("canBeSelected", master);
            }
            catch
            {
                return this.canBeSelected;
            }
        }
        public bool CanBeControlled()
        {
            try
            {
                return getAttr<bool>("canBeControlled", master);
            }
            catch
            {
                return this.canBeControlled;
            }
        }
    }
    public class presetMaskExample : Controllable
    {
        public override void initialize()
        {
            if (isSelected()) Console.WriteLine("INITIALIZING PRINTSTUFF");
            if (isSelected()) Console.WriteLine("setting canBeControlled and canBeSelected to true");
            setAttrThisAndObj("canBeSelected", master, true);
            setAttrThisAndObj("canBeControlled", master, true);
        }
    }
    public class PrintStuff : presetMaskExample
    {
        public string printstuff;
        public override void functionwrapper(ControllerImplementation master)
        {
            if (isSelected()) Console.WriteLine(">>> PRINTSTUFF FUNCTIONWRAPPER WAS CALLED!");
            if (CanBeControlled())
            {
                if (isSelected()) Console.WriteLine(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> PRINTSTUFF CAN BE CONTROLLED!");
                Console.WriteLine(this.printstuff);
            }
        }
        public override void initialize()
        {
            if (isSelected()) Console.WriteLine("INITIALIZING P_PRINTSTUFF");
            var mask = new presetMaskExample();
            master.registerController(mask);
            if(printstuff==null) printstuff = getAttrElseSetDefaultBoth("printstuff", master, "LALALALALA!!!");
        }
    }

    public class PrintMe : ControllerImplementation
    {
        public bool allowOthersToPrint;
        private Dictionary<string, string> values;
        public override void functionwrapper(ControllerImplementation master)
        {
            Console.WriteLine(">>> PRINT_ME FUNCTIONWRAPPER WAS CALLED!");
            if (AllowOthersToPrint())
            {
                foreach(KeyValuePair<string,string> kvp in values)
                {
                    var i = new PrintStuff();
                    i.printstuff=(kvp.Key + "   |    " + kvp.Value);
                    i.canBeControlled = true;
                    master.registerController(i);
                }
            }

        }

        public override void initialize()
        {
            values = new Dictionary<string, string>();
            Random rng = new Random();
            for (int i = 0; i < 15; i++)
            {
                bool succeded = false;
                var keybuffer = new byte[7];
                var valuebuffer = new byte[55];
                while (!succeded)
                {
                    succeded = true;
                    rng.NextBytes(keybuffer);
                    rng.NextBytes(keybuffer);
                    rng.NextBytes(valuebuffer);
                    rng.NextBytes(valuebuffer);
                    var k = new string(UTF8Encoding.UTF8.GetChars(keybuffer));
                    var v = new string(UTF32Encoding.UTF8.GetChars(valuebuffer));
                    try
                    {
                        values.Add(k, v);
                    }
                    catch
                    {
                        succeded = false;
                    }
                }

            }
        }
        public bool AllowOthersToPrint()
        {
            try
            {
                return getAttr<bool>("allowOthersToPrint", master);
            }
            catch
            {
                return this.allowOthersToPrint;
            }
        }
    }
}

Invoke Solution:

using System;

namespace controllertest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Basic Example using inheritance:");
            var test = new Controllable
            {
                // initialize in controllable does not preset it's values, so all bools are false by default
                canBeControlled = true,
                canBeSelected = true,
                isselected = true
            };
            test.wrapperfunction();
            Console.WriteLine("------------------------------------------------------------------------------------------");

            Console.WriteLine("Basic Example partial-hierachy / masking parents:");
            var mask = new presetMaskExample();
            mask.isselected = true;
            mask.wrapperfunction();
            Console.WriteLine("------------------------------------------------------------------------------------------");

            Console.WriteLine("Basic Example using a mask as base to build a new controller:");
            var DoesPrint = new PrintStuff();
            DoesPrint.wrapperfunction();
            Console.WriteLine("------------------------------------------------------------------------------------------");

            Console.WriteLine("Basic Example registering not derived components:");
            var print = new PrintMe();
            print.allowOthersToPrint = true;
            print.wrapperfunction();
            Console.WriteLine("------------------------------------------------------------------------------------------");

        }
    }
}

Проблемы:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...