Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Это в свою очередь нарушает SRP на микроуровне, так как изначально не зависящие ни от чего (и простые в реализации) свойства теперь вынуждены иметь знания о существовании других свойств и деталей их реализации для того, чтобы иметь возможность в нужные моменты правильно эти свойства обновлять.
С технической точки зрения не все хорошо, потому что могут существовать достаточно сложные связи между свойствами, поддерживать вручную которые достаточно трудоемко (и багоемко). Примерами таких связей являются:
зависимости от свойств, которые сами являются зависимыми;
зависимости от свойств вложенных объектов (цепочки свойств), как в случае DiscountSum = Order.Sum * Order.Discount.Percent / 100;
зависимости от свойств элементов коллекции (TotalQuantity = Orders.Sum(o => o.Quantity)).
//Определяем статическую "карту зависимостей", которая будет хранить зависимости для класса private static readonly IDependenciesMap<Order> _dependenciesMap = new DependenciesMap<Order>(); static Order() { //Определяем и добавляем в карту зависимости _dependenciesMap.AddDependency(o => o.Cost, o => o.Price * o.Quantity, o => o.Price, o => o.Quantity) } private IDisposable _tracker; public Order() { //Начинаем отслеживать зависимости для текущего экземпляра модели _dependenciesMap.StartTracking(this); }
Метод возвращает IDisposable, который может быть использован для остановки отслеживания изменений на любом этапе жизненного цикла модели.
зависимости от свойств, которые сами являются зависимыми;
public class Order : ModelBase
{
private decimal _price;
private int _quantity;
public decimal Price
{
get { return _price; }
set { Set(ref _price, value, "Price", "Cost"); }
}
public int Quantity
{
get { return _quantity; }
set { Set(ref _quantity, value, "Quantity", "Cost"); }
}
public int Cost
{
get { return _price * _quantity; }
}
// ...
}зависимости от свойств вложенных объектов (цепочки свойств)
зависимости от свойств элементов коллекции
read-only свойство использовать нельзя
добавляется поле типа IDisposable
Например, хотелось бы, чтобы код можно было оставить в свойстве, а трекер только кидал уведомления об изменении.
Если коллекция одна, то можно обойтись какой-нибудь коллекцией «NotifyingCollection where T: INPC»
где у коллекции бросается какое-нибудь событие при изменении любого элемента.
ОК, если проперти меняется, значит, его кто-то поменял, значит, должен быть сеттер.
Но вообще-то можно это поле и не добавлять — хранить его нужно только в случае, если хочется в какой-то из моментов жизненного цикла view model трекинг остановить.
Кеширование вычисленного значения. TotalCost = Orders.Sum(o => o.Price * o.Quantity) вообще-то имеет O(n) сложность вычисления.
Рейз события только когда значение поменялось
Иногда проверка на новое значение не нужна, а иногда проверка очень даже нужна — когда зависимости настолько нетривиальны, что появляются циклы, когда A --> B, а B --> A через какие-нибудь сложные цепочки.
public string FullName
{
get { _tracker.Get(() => FirstName + " " + LastName); }
}Разве эту коллекцию не нужно реализовать также, как трекер? Чем подход проще, когда уже есть трекер?
А если инстанс коллекции поменяли?
public class ViewModel : ReactiveObject
{
public ViewModel()
{
_fullName = this.WhenAny(x => x.FirstName, y => y.LastName,
(firstName, lastName) => firstName + " " + lastName)
.ToProperty(this, x => x.FullName);
}
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { this.RaiseAndSetIfChanged(ref _firstName, value); }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { this.RaiseAndSetIfChanged(ref _lastName, value); }
}
private ObservableAsPropertyHelper<string> _fullName;
public string FullName
{
get { return _fullName.Value; }
}
}
public void RegisterPropertiesDependencies(string propertyName, List<string> dependenciesProperties)
{
foreach (var dependencyProperty in dependenciesProperties)
{
this.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == dependencyProperty) RaisePropertyChanged(propertyName);
};
}
}
...
RegisterPropertiesDependencies("FullName", new List<string> { "FirstName", "LastNameName"});
Вы думаете, что незначительное увеличение cohesion окупает усложнение кода путём добавления нового проекта?
Автоматическая калькуляция вычислимых свойств моделей представлений в .NET