Как подписывать объекты, не зная класса их вызывающего? - PullRequest
0 голосов
/ 12 марта 2020

У меня есть Сценарий , в котором используется A или несколько Как . A создается вне этого сценария , но B , который создается внутри A , не имеет представления о сценарии его экземпляра , И это идея, A и B не должны знать, как работает сценарий, они просто должны работать, когда UpdateEvent вызывается / вызывается.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {

            var sce1 = new Scenario();
            sce1.Add(new A(1));
            sce1.Add(new A(3));
            sce1.Update(2);
            sce1.Update(5);

            var sce2 = new Scenario();
            sce2.Add(new A(6));
            sce2.Update(0.3);

            Console.ReadKey();

        }
    }

    public class BasicEvent : EventArgs
    {
        public double Number { get; set; }
    }

    public abstract class toBeUpdated
    {
        public abstract void OnUpdate(object sender, BasicEvent e);
    }

    public class C : toBeUpdated
    {
        public double Number { get; set; }
        public C(double num)
        {
            Number = num * num;
        }
        public override void OnUpdate(object sender, BasicEvent e)
        {
            Number = Math.Pow(e.Number, 2);
        }
    }

    public class B : toBeUpdated
    {
        public double  Number { get; set; }
        public B(double num)
        {
            Number = num;
        }
        public override void OnUpdate(object sender, BasicEvent e)
        {
            Number *= e.Number;
        }
    }

    public class A : toBeUpdated
    {
        private B B1 { get; set; }
        private B B2 { get; set; }
        private C C1 { get; set; }
        public A(double number)
        {
            B1 = new B(number);
            B2 = new B(number);
            C1 = new C(number);
        }
        public override void OnUpdate(object sender, BasicEvent e)
        {
            Console.WriteLine($"Updating A.. Data = {B1.Number + B2.Number * C1.Number}");
        }
    }

    public class Scenario
    {
        private event EventHandler<BasicEvent> UpdateEvent;
        private List<toBeUpdated> toKeepTrack { get; set; }
        public Scenario()
        {
            toKeepTrack = new List<toBeUpdated>();
        }
        public void Add(toBeUpdated obj)
        {
            toKeepTrack.Add(obj);
            UpdateEvent += obj.OnUpdate;
        }
        public void doSomethingWithTheList()
        {
            ....
        }
        public void Update(double num)
        {
            UpdateEvent?.Invoke(this, new BasicEvent() { Number = num });
            Console.WriteLine();
        }
    }
}

Мне нужно создать несколько Scenar ios Я не могу сделать это Stati c.

1 Ответ

0 голосов
/ 12 марта 2020

Для этого варианта использования, я думаю, лучший подход - создать интерфейс IScenario и позволить Scenario расширить этот интерфейс. Это скроет детали реализации Scenario и позволит вам передать IScenario непосредственно в конструктор A.

public interface IScenario
{
    void Add(toBeUpdated obj);
    void Update(int num);
}

, который вы теперь можете расширить Scenario вроде так:

public class Scenario : IScenario
{
    ...
}

Обновите A соответственно:

public class A : toBeUpdated
{
    ...
    public A(int number, IScenario scenario)
    {
        B1 = new B(number);
        scenario.Add(this);
        scenario.Add(B1);
    }
    ...
}

Как использовать будет что-то вроде:

var sce = new Scenario();
A a1 = new A(1, sce);
A a3 = new A(3, sce);
sce.Update(2);
sce.Update(5);

Этот подход гарантирует, что A не знает Scenario, что оно обновляется с минимальными усилиями, и вполне тестируемо. Другой подход заключается в добавлении метода к A, который регистрирует a Scenario (полезно, если A необходимо добавить новый Scenario после создания):

public void Register(IScenario scenario)
{
    scenario.Add(this);
    scenario.Add(B1);
}

Примечание: Создание IScenario не требуется , но это хорошо, когда вы попадаете в unit testing и хотите mock объектов.

Редактировать: Вы всегда можете использовать отражение, чтобы получить все свойства типа toBeUpdated из A:

// In Scenario
public void Add(toBeUpdated obj)
{
    UpdateEvent += obj.OnUpdate;

    // Get all properties of type toBeUpdated
    var properties = obj.GetType()
        .GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
        .Select(x => x.GetValue(obj))
        .OfType<toBeUpdated>();

    foreach (var property in properties)
    {
        UpdateEvent += property.OnUpdate;
    }
}
...