Как поддерживать связь между двумя классами? - PullRequest
0 голосов
/ 24 марта 2012

наверняка это не лучший заголовок. Я создаю систему для генерации математических задач. Разработчик должен реализовать два интерфейса:

  • Problem: содержит свойства, для которых требуется сгенерированная проблема.
  • Configuration: это параметры диапазона для генерации Problem.

Это интерфейсы:

public abstract class Problem
{
}

public abstract class Configuration
{
}

enter image description here

А вот один пример для Бинарной проблемы.

public class BinaryProblem : Problem
{
    public decimal X { get; set; }
    public decimal Y { get; set; }

    public BinaryProblem(decimal x, decimal y)
    {
        this.X = x;  // Number 1
        this.Y = y;  // Number 2
    }
}

public class BinaryProblemConfiguration : Configuration
{
    // Range for X
    public int XMin { get; set; }
    public int XMax { get; set; }

    // Range for Y
    public int YMin { get; set; }
    public int YMax { get; set; }

    public BinaryProblemConfiguration() { }
}

Видите ли вы эту грань между проблемой и конфигурацией? Мне нужно поставить много модулей, которые реализуют эти два интерфейса.

Итак, мне нужен способ их генерирования. Я думал в создании абстрактного класса, где содержится:

  • protected static Random: почти всем конфигурациям необходим случайный класс для создания чисел (т.е. Е. random.Next(X1, Y1);). И нужно быть статичным, потому что нужно всегда создавать числа, используя одно и то же семя.
  • public abstract TProblem Generate(TConfiguration config); // where : TProblem : Problem, new(), TConfiguration : Configuration

И реализовать этот абстрактный класс в каждом типе проблемы.

У меня вопрос: это хороший способ начать решать это решение или какое другое решение мне нужно сделать?

РЕДАКТИРОВАТЬ: Один пример, который я пытался это:

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

public interface IProblemFactory
{
    Problem CreateProblem();
}

public abstract class ProblemBaseFactory<TProblem, TConfiguration> : IProblemFactory
    where TProblem : Problem
    where TConfiguration : Configuration
{
    private const int SEED = 100;
    protected TConfiguration _config;
    protected static Random _random;

    public ProblemBaseFactory(TConfiguration config)
    {
        _config = config;

        if (_random == null) _random = new Random(SEED);
    }

    public void SetSeed(int newSeed)
    {
        _random = new Random(newSeed);
    }

    public Problem CreateProblem()
    {
        return CreateProblem(_config);
    }

    public abstract TProblem CreateProblem(TConfiguration config);
}

public class BinaryProblemFactory : ProblemBaseFactory<BinaryProblem, BinaryProblemConfiguration>
{
    public override BinaryProblem CreateProblem(BinaryProblemConfiguration config)
    {
        var x = GenerateValueInRange(_config.Range1);
        var y = GenerateValueInRange(_config.Range2);
        return new BinaryProblem(x, y, Operators.Addition); 
    }

    private decimal GenerateValueInRange(Range<int> range)
    {
        return _random.Next(range.MinValue, range.MaxValue);
    } 
}

Ответы [ 2 ]

2 голосов
/ 25 марта 2012

Я думаю, что хорошо собирать значения, которые использовались вместе. В вашем случае будет полезен объект Range. Что-то вроде (без проверки min <= max): </p>

public class Range<T>
{
    public Range(T min, T max)        
    {
        Min = min;
        Max = max;
    }

    public T Min { get; private set; }
    public T Max { get; private set; }
}

После этого BinaryProblemConfiguration будет выглядеть так:

public class BinaryProblemConfiguration
{
    public Range<int> XRange { get; set; }
    public Range<int> YRange { get; set; }
}

На самом деле вы реализуете фабрику продуктов. Так что я посредник не очень описательное имя для него:

public interface IProblemFactory
{
    Problem CreateProblem();
}

Создание фабрики для каждого вида продукции. Каждый завод знает, какой тип продукта он создает и какой тип конфигурации ему необходим. Например:

public class BinaryProblemFactory : IProblemFactory
{
    private BinaryProblemConfiguration _config;
    private Random _random;

    public BinaryProblemFactory(BinaryProblemConfiguration config)
    {
        _config = config;
        // or you can use const seed here
        _random = new Random(DateTime.Now.Millisecond); 
    }

    public override Problem CreateProblem()
    {
        var x = GenerateValueInRange(_config.XRange);
        var y = GenerateValueInRange(_config.YRange);
        return new BinaryProblem(x, y);
    }

    private decimal GenerateValueInRange(Range<int> range)
    {
        return _random.Next(range.Min, range.Max);
    }
}

Создание фабрики:

BinaryProblemConfiguration config = new BinaryProblemConfiguration();
config.XRange = new Range<int>(0, 100);
config.YRange = new Range<int>(-50, 50);

IProblemFactory problemFactory = new BinaryProblemFactory(config);

Передайте созданную проблемную фабрику где-нибудь как IProblemFactory:

Problem problem = problemFactory.CreateProblem();

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

 BinaryProblemFactory binaryProblemFactory = new BinaryProblemFactory();
 binaryProblemFactory.XRange = new Range<int>(0, 100);
 binaryProblemFactory.YRange = new Range<int>(-50, 50);
0 голосов
/ 24 марта 2012

Не совсем уверен, если я понял ваш вопрос, но я все равно постараюсь высказать свое мнение по этому вопросу. Надеюсь, это поможет.

Я полагаю, вы выбрали неправильный подход. - у вас есть два класса, но они не общаются вообще (хотя они должны быть) - да, вы создали абстрактные классы для наследования, но я не вижу причины в данном конкретном случае, ПОЧЕМУ. Они ничего не определяют, они не применяют никакой логики к созданию классов. Когда вы создаете интерфейс, вы хотите, чтобы кто-то, кто будет его использовать, реализовал его. Вы не можете реально реализовать пустой класс / интерфейс.

Во-первых, я бы предложил исправить эту проблему. Создайте интерфейсы, которые требуют определенных методов или свойств, когда вы хотите их реализовать. Таким образом, вы сможете легче разрабатывать свое основное приложение, потому что вы будете знать, что какую бы проблему ни создавал какой-либо другой разработчик, ему нужно будет реализовать ваш интерфейс с помощью определенных методов, которые вы затем сможете использовать. Если вы позволите реализовать пустой интерфейс, вы не сможете быть точно уверены, каковы будут результаты и чего ожидать. Вы не можете полагаться на них, чтобы они вели себя хорошо (или, по крайней мере, не должны).

Во-вторых, не уверен, что вы это знаете, но когда вы используете модули (Problem + Config как один модуль), поместите каждый в отдельный файл .cs. Таким образом, вы можете иметь практически бесконечное их количество и при этом легко поддерживать свое приложение.

В-третьих, я бы предложил некоторую связь (и связь) между этими классами, используя некоторый интерфейс, подобный этому:

public interface IProblem
{
   IProblemConfiguration Config;

   // other needed methods or properties
}
...