Как я могу обезопасить себя от обновления другого объекта поверх текущего объекта по ошибке?

#c# #java #.net #design-patterns

#c# #java #.net #шаблоны проектирования

Вопрос:

Можете ли вы предложить мне способ, который предотвратил бы подобный случай при работе со ссылочными типами?

 var someCost = new Cost( Price: new Price(1000, "USD")
                        , CostType: "Type-A") ;

var candyCost = new Cost();

// Initialize candyCost.Price
candyCost.Price = someCost.Price; //Now candyCost Price is referencing 
                                 // price of someCost; 

// (.......)
// Some logic and code here
//and I forgot that I was referencing to someCost object's price object
//and I do stupid mistake:

candyCost.Price.Value = 5000; //Now I believe I have updated candyCost price 
                              //but I have also updated someCost!!
  

Остальная часть истории посвящена отладке, чтобы выяснить, почему обновляется цена someCost.

Я хотел упростить проблему с помощью этого примера. Я надеюсь, если вы понимаете, что я имею в виду.

Вопрос: Можете ли вы порекомендовать мне способ обезопасить себя от повторения такой ошибки? любые шаблоны проектирования, когда дело доходит до обновления значений ссылочных типов.

Комментарии:

1. Действительно ли это java связано?

2. Я думал, что это могло бы охватить и мнения Java-парней, не так ли?

3. выполните «OO поверх неизменяемых объектов» . Множество проблем внезапно перестают быть проблемами.

Ответ №1:

Ваш Price объект должен быть неизменяемым — это заставит вас назначить новый Price объект вместо изменения цены существующего, следовательно, избегая побочного эффекта.

Ответ №2:

Зависит от того, чего вы хотите достичь с помощью этой строки:

  candyCost.Price = someCost.Price;
  

Вы хотите сказать, что candyCost и someCost имеют одинаковую цену в данный момент или что они всегда имеют одинаковую цену?

Если вы просто хотите инициализировать candyCost.Price значением, равным someCost.Prize , вам следует клонировать Price экземпляр:

  candyCost.Price = new Price(someCost.Price); // copy constructor pattern inside
  

(конечно, вы должны реализовать конструктор)

Комментарии:

1. 1 за хороший вопрос! Вы правы, мне следовало подчеркнуть основную цель. И цель, как вы сказали, инициализировать candyCost.Price значением, равным someCost.Prize

Ответ №3:

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

Комментарии:

1. Он спрашивает, есть ли способ защиты от этого, с помощью какого-либо шаблона или конструкции, нет?

2. Ну, это превыше всего — вопрос дисциплины кодирования. Это шаблон.

3. В примере OP нет двух локальных переменных, которые представляют одно и то же.

4. Да, он это делает. candyCost и someCost

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

Ответ №4:

Создайте глубокую копию класса Price, внедрив интерфейс IClonnable. Затем, когда вы назначаете цену, вы бы сказали

 a.Price = b.Price.Clone(); // will return a new object of the price after assigning the internal value types 
  

или

   Class Cost{
  private Price _price;

        public Price PriceValue
        {
            get { _price.Clone(); }
            set { _price = value; }
        }
       }
  

таким образом, вы никогда не забудете

следовательно, вы не можете получить доступ к полю _price напрямую, если только вы не вызовете свойство Getter, которое в конечном итоге вернет полную копию Price

Комментарии:

1. Но если вы помните, что вам нужно клонировать, то вам не нужно беспокоиться. Что, если я забуду клонировать? Это то, чем я пытаюсь обезопасить себя от mustafabar.

2. Затем используйте свойство получения по цене и внутри него создайте свой клон .. проверьте редактирование

3. @pencilClone — Не существует универсального шаблона проектирования предотвращения глупых ошибок. К сожалению, я должен сказать.

4. В этом примере есть способы предотвратить возникновение этой ошибки (но не с помощью IClonable одного)

Ответ №5:

В этом случае я бы предложил использовать struct (тип значения) вместо class (ссылочный тип). Таким образом, невозможно ссылаться на тот же экземпляр, потому что нет «ссылки», само значение сохраняется 🙂

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

Комментарии:

1. -1 Извините, мне пришлось это сделать. Это действительно плохо. Изменение дизайна, чтобы вы не допустили ни одной ошибки? То есть вы оплачиваете все накладные расходы на упаковку только за это?

2. @Aliostad — В запросе недостаточно информации, чтобы определить, должна ли цена быть структурой или классом. Но в примере кода нет бокса, даже если это структура. Я не думаю, что ваше значение -1 оправдано по этой причине.

3. @Aliostad — Я не понимаю, о чем ты говоришь. structs существует по какой-то причине, и второй вариант (только для чтения конструктор) используется повсеместно. В самом вопросе спрашивается, какие проекты использовать, чтобы избежать его проблемы, и это именно то, примеры чего я привел. Пожалуйста, пересмотрите.

Ответ №6:

Вам нужно поведение по значению для свойства Price. Средство получения должно вернуть копию объекта Price.

Ответ №7:

Есть несколько способов сделать это.

  1. Используйте неизменяемые типы — например, String в Java и все примитивные оболочки
  2. Используйте конструкторы копирования для копирования объектов вместо ссылок на них

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

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

Ответ №8:

Сделать Cost.Price доступным только для чтения с помощью {get; private set;} Добавьте в Cost новый метод с именем setPrice, который принимает Price и создает новый экземпляр Price

 SetPrice(Price price)
{
    this.Price = price.Clone();
}
  

Вам нужно будет заставить Price реализовать IClonnable

Ответ №9:

В зависимости от языка вы можете выполнить перегрузку оператора. Итак, перегрузите оператор присваивания ( = ) и выполните глубокое клонирование в перегруженном теле оператора.