Дизайн C # для объекта, некоторые свойства которого являются дорогостоящими:оправдание для того, чтобы сделать его изменяемым?

StackOverflow https://stackoverflow.com/questions/987461

  •  13-09-2019
  •  | 
  •  

Вопрос

Да, я знаю, еще один вопрос об изменяемых объектах.Видишь это для общего фона и это для наиболее близкого аналога моему вопросу.(хотя в нем есть некоторые специфические для C ++ обертоны, которые здесь неприменимы)

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

public class FriendlyWrapper
{
    public FriendlyWrapper(UglyDatum u)
    {
        Foo = u.asdf[0].f[0].o.o;
        Bar = u.barbarbar.ToDooDad();
        Baz = u.uglyNameForBaz;
        // etc
    }

    public Widget Foo { get; private set; }
    public DooDad Bar { get; private set; }
    public DooDad Baz { get; private set; }
    // etc
    public WhizBang Expensive1 { get; private set; }
    public WhizBang Expensive2 { get; private set; }

    public void Calculate()
    {
        Expensive1 = Calc(Foo, Bar);
        Expensive2 = Calc(Foo, Baz);
    }

    private WhizBang Calc(Widget a, DooDad b) { /* stuff */ }

    public override void ToString()
    {
        return string.Format("{0}{1}{2}{3}{4}", Foo, Bar, Baz, Expensive1 ?? "", Expensive2 ?? "");                             
    }
}

// Consumer 1 is happy to work with just the basic wrapped properties
public string Summarize()
{
    var myStuff = from u in data
                  where IsWhatIWant(u)
                  select new FriendlyWrapper(u);

    var sb = new StringBuilder();
    foreach (var s in myStuff)
    {
        sb.AppendLine(s.ToString());
    }
    return sb.ToString();
}

// Consumer 2's job is to take the performance hit up front.  His callers might do things 
// with expensive properties (eg bind one to a UI element) that should not take noticeable time. 
public IEnumerable<FriendlyWrapper> FetchAllData(Predicate<UglyDatum> pred)
{
    var myStuff = from u in data
                  where pred(u)
                  select new FriendlyWrapper(u);

    foreach (var s in myStuff)
    {
        s.Calculate();  // as written, this doesn't do what you intend...
    }

    return myStuff;
}

Какой здесь самый лучший маршрут?Варианты, которые я вижу:

  1. Изменяемый объект с явным методом Calculate(), как указано выше
  2. Изменяемый объект, где дорогостоящие вычисления выполняются в геттерах (и, вероятно, кэшируются)
  3. Разделить на два объекта, где один наследует (или, возможно, создает?) от другого
  4. Какой-то механизм статической + блокировки, как в вопросе C ++, приведенном выше

Я сам склоняюсь к № 2.Но на каждом маршруте есть потенциальные подводные камни.

Если вы выберете # 1 или # 2, то как бы вы четко и корректно реализовали цикл Consumer2 над изменяемыми параметрами?

Если вы выберете # 1 или # 3, как бы вы справились с будущими ситуациями, когда вы хотите вычислить только некоторые свойства, но не другие?Хотите создать N вспомогательных методов / производных классов?

Если вы выберете № 4, я думаю, что вы сумасшедший, но не стесняйтесь объяснить

Это было полезно?

Решение

В вашем случае, поскольку вы используете LINQ, вы собираетесь создавать эти объекты только в тех случаях, когда вам нужны вычисления.

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

Что касается изменчивости - изменяемые объекты со ссылочным синтаксисом и идентификатором (т.е.:классы в C #) действительно в порядке - это скорее проблема, когда вы имеете дело с изменяемыми типами значений (т.Е.:структуры).В .NET BCL существует множество изменяемых классов - и они не вызывают проблем.Проблема, как правило, возникает чаще одной, когда вы начинаете иметь дело с типами значений.Изменяемые типы значений приводят к очень неожиданному поведению.

В общем, я бы перевернул этот вопрос с ног на голову - как и где вы собираетесь использовать этот объект?Как вы можете сделать этот объект наиболее производительным (если он был определен как проблемный), не влияя на удобство использования?Все ваши варианты 1), 3) и 4) ухудшили бы удобство использования, поэтому я бы избегал их.В этом случае выполнение 2) не поможет.Я бы просто поместил его в конструктор, чтобы ваш объект всегда находился в допустимом состоянии (что очень хорошо для удобства использования и ремонтопригодности).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top