3 способа расчета - PullRequest
       9

3 способа расчета

7 голосов
/ 15 января 2009

У меня есть объект данных с тремя полями, A, B и C. Проблема в том, что пользователь может установить любое из них, потому что:

A * B = C

Таким образом, если пользователь начинает с настройки A & B, будет вычислено значение C. но затем, если пользовательский набор C, A пересчитывается, то есть неявная привязка, которая происходит на основе последнего поля, установленного пользователем.

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

public class Object
{
     private double _A;
     private double _B;
     private double _C;

     private bool _enteredA;
     private bool _enteredB;
     private bool _enteredC;

public double A
{
get
    {
     if (_enteredC)
     {
          return _C / _B;
     }
     else
     {
        return _A;
     }
  }
}

если логика установки A, B и C всегда будет на «get» или «set».

Есть ли более чистый способ сделать это?

Ответы [ 6 ]

4 голосов
/ 15 января 2009

Итак, ваши правила:

A = C / B
B = C / A
C = A * B

Что вам нужно сделать, это запомнить последние два поля, которые пользователь ввел, и из этого рассчитать третье. Поэтому, если они вводят A, а затем B, то вы вычисляете C. Если они затем меняют C, затем пересчитывают A и т. Д.

Если вы хотите закодировать его без использования логических флагов, есть несколько способов сделать это. Будет работать очередь или просто простой массив / карта:

editOrder = {A : 0, B : 0, C : 0 }  // (or just use 0, 1, 2)
editCount = 0;

every time any field is edited :
    editCount = editCount + 1
    editOrder[field] = editCount
    if two fields have an editOrder > 0
        recalculate the field with the lowest editOrder

Пример использования:

Field changed    editOrder        recalculate field
    A            1,0,0            -
    A            2,0,0            -
    B            2,3,0            C
    C            2,3,4            A
    C            2,3,5            A
    B            2,6,5            A
    A            7,6,5            C
3 голосов
/ 16 января 2009

Вы утверждаете, что ваши свойства имеют отношение "A * B = C". Итак, ваш класс должен это выразить. A и B являются независимыми переменными, поэтому у них есть простые установщики и получатели, которые просто получают и устанавливают поле. C является зависимой переменной, поэтому она должна иметь только геттер и выполнять вычисления на основе A и B:

class Foo
{
    private double m_a;
    public double A
    {
        get { return m_a; }
        set { m_a = value; }
    }

    private double m_b;
    public double B
    {
        get { return m_b; }
        set { m_b = value; }
    }

    public double C
    {
        get { return A * B; }
    }
}

Можно написать редактор, который позволяет редактировать A и B тривиально. Он также может позволить пользователю редактировать C, если (и только если) пользователь уже отредактировал A или B. Представление будет хранить некоторое состояние, чтобы указать, какое (если таковое имеется) поле было отредактировано последним, и, следовательно, может применить соответствующее изменение к A или B при редактировании концептуального C.

Теперь, если View не знает о вычислении C, вы можете добавить методы к классу Foo в «SetAGivenC ()» и «SetBGivenC ()»:

class Foo
{
    ... as above ...

    public void SetAGivenC( double newC )
    {
        A = newC/B;
    }

    public void SetBGivenC( double newC )
    {
        B = newC/A;
    }
}

Теперь ваш GUI просто вызывает один из этих методов при редактировании "C".

Я не совсем понимаю, что означает последнее поле, заданное пользователем. Представьте, что на экране есть два редактора (оба связаны с одним экземпляром Foo), и пользователь редактирует A в первом редакторе, затем B во втором, а затем C в первом. Что настраивается при установке C? А или Б? Если вы можете ответить на этот вопрос, вы будете знать, какой класс должен отслеживать «последнее поле, заданное пользователем» - это должна быть модель или представление? Я ожидаю, что A будет скорректирована в этом случае и вид отслеживает последнее отредактированное поле. Но это проблема для другого дня!

Edit:

Джиперс - я даже собрал собственный патологический случай!

Я попробую последний абзац еще раз:

Я не совсем понимаю, что означает последнее поле, заданное пользователем. Представьте, что на экране есть два редактора (оба связаны с одним и тем же экземпляром Foo), и пользователь редактирует A, затем B в первом редакторе, затем пользователь редактирует B, затем A во втором, затем пользователь редактирует C в первом , Что настраивается при установке C? А или Б? Если вы можете ответить на этот вопрос, вы будете знать, какой класс должен отслеживать «последнее поле, заданное пользователем» - это должна быть модель или представление? Я ожидаю, что A будет скорректирована в этом случае и вид отслеживает последнее отредактированное поле. Но это проблема для другого дня!

0 голосов
/ 20 января 2009

@ Jonathan

Моя точка зрения на ошибки в вашем методе расчетов выглядит следующим образом:

В написанном вами коде происходит следующая последовательность событий.

given a valid state at some point of time.
[a = 1, b = 3, c = 3]

вызовите ваш установщик с b = 4, который по вашему коду затем вычислит a и c:

1. first sets b          b = 4
2. first calculates a    a = c / b =  3  / 4  =  3/4
3. then calculates  c    c = a * b = 3/4 * 4  =  3

последний кортеж

[a = 3/4, b = 4, c = 3]

По сути, для вашего класса (как написано в настоящее время)

setting b alters a  ->  leaves c unaffected.
setting c alters a  ->  leaves b unaffected.
setting a alters b  ->  leaves c unaffected

Вычисление (3) бесполезно, поскольку, если A уже получено из C и B, повторное получение C из A и B не изменит значение C никогда .

Настройка A или B должна вычислять C, и существует особый случай, когда настройка C вычисляет A или B без использования логического значения, чтобы узнать, какое из них. (вопрос опубликован)

0 голосов
/ 15 января 2009

Вы ищете что-то подобное (спасибо nickf за то, что потрудились найти уравнение для каждого значения):

class Foo
{
    // Mark these as obsolete so that
    // you don't use them by mistake
    // in the class.
    [Obsolete]
    private double _a;
    [Obsolete]
    private double _b;
    [Obsolete]
    private double _c;

    #pragma warning disable 0612
    public double A
    {
        get
        {
            return _a;
        }
        set
        {
            _a = value;
            // You may want to change
            // this to simply _a.
            _b = _c / (_a == 0 ? 1 : _a);
            _c = _a * _b;
        }
    }

    public double B
    {
        get
        {
            return _b;
        }
        set
        {
            _b = value;
            _a = _c / (_b == 0 ? 1 : _b);
            _c = _a * _b;
        }
    }

    public double C
    {
        get
        {
            return _c;
        }
        set
        {
            _c = value;
            _a = _c / (_b == 0 ? 1 : _b);
            _b = _c / (_a == 0 ? 1 : _a);
        }
    }
    #pragma warning restore 0612
}
0 голосов
/ 15 января 2009

Короткий ответ - нет.

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

0 голосов
/ 15 января 2009

создать приватный метод для выполнения следующих расчетов

private void calculateFields(){
  if (_b != INIT_VALUE && _c != INIT_VALUE)
     _a =  _c / _b;
  if (_a != INIT_VALUE && _c != INIT_VALUE)
     _b = _c / _a;
  if (_a != INIT_VALUE && _b != INIT_VALUE)
     _c = _a * _b;
}

public double A
{
get{
        return _a;
  }
set{
   _a = value;
   calculateFields();
}
}

Сделайте то же самое для свойств B и C.

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