Да, я знаю, еще один вопрос о изменяемых объектах. См. this для общего фона и this для ближайшего аналога моего вопроса. (хотя он имеет некоторые специфические обертоны C++, которые здесь не применимы)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;
}
Каков наилучший маршрут здесь? Параметры можно увидеть:
- изменяемый объект с помощью метода явного Calculate(), как описано выше
- изменяемый объект, где дорогие расчеты производятся в добытчиками (и, вероятно, кэшированных)
- Разделить на два объекта, где один наследуется (или, возможно, сочиняет?) из другой
- какого-то статический + запирающего механизма, как в вопросе C++ связаны выше
Я склоняюсь к # 2 самим. Но у каждого маршрута есть потенциальные ловушки.
Если вы выберете # 1 или # 2, то каким образом вы бы поняли, правильно ли вы используете цикл Consumer2 в отношении mutables?
Если вы выберете # 1 или # 3, как бы вы справлялись с будущими ситуациями, когда вы хотите только вычислить некоторые свойства, но не другие? Хотите создать N вспомогательных методов/производных классов?
Если вы выбрали # 4, я думаю, что ты сумасшедший, но вы можете объяснить
Если свойство дорогое, то оно работает вопреки рекомендациям MS относительно свойств против методов (http://msdn.microsoft.com/en-us/library/bzwdh01d(VS.71).aspx), но даже как метод у вас все же есть те же проблемы, конечно – annakata