Разница между неглубокой копией и назначением и глубокой копией и созданием нового объекта с использованием нового ключевого слова в шаблоне проектирования прототипа - PullRequest
0 голосов
/ 04 августа 2020

Итак, я изучаю шаблоны проектирования, и сегодня я наткнулся на шаблон проектирования «Прототип». Я создал интерфейс Prototype, который содержит 2 метода (CloneShallow и CloneDeep). Я создал тривиальный класс Employee, который реализует интерфейс Prototype и имеет 2 свойства (Id и FullName). Код довольно прост.

public interface Prototype
{
    public Prototype CloneShallow();

    public Prototype CloneDeep();
}

public class Employee : Prototype
{
    public int Id { get; set; }

    public string FullName { get; set; }


    public Employee(int id,string fullName)
    {
        this.Id = id;
        this.FullName = fullName;
    }
    public Prototype CloneShallow()
    {
        return this;
    }

    public Prototype CloneDeep()
    {
        return new Employee(Id, FullName);
    }

    public void printInfo()
    {
        Console.WriteLine($"Id: {Id}");
        Console.WriteLine($"Full name: {FullName}");

    }
}

В основном

class Program
    {
        static void Main(string[] args)
        {
            Employee original = new Employee(1, "First Employee");
            Employee copyPrototype = (Employee)original.CloneShallow();
            Employee copyAssigned = original;


            Employee deepCopy = (Employee)original.CloneDeep();
            Employee newDeepCopy = new Employee(original.Id, original.FullName);
            

        }
    }

Я создал объект с именем original. После этого я выполнил поверхностное копирование с помощью метода CloneShallow (), в то время как у нас есть объекты original и copyPrototype, указывающие на одно и то же место адреса. После этого я назначил объекту copyAssigned адрес объекта с именем original (shallow copy aswel).

Чтобы сделать глубокую копию исходного объекта, объект deepCopy использует метод CloneDeep (), чтобы выделить для него новое пространство памяти и newDeepCopy использует новое ключевое слово.

Я немного запутался. Объект copyAssigned неглубоко копируется так же, как copyProtoype (оба они указывают на один и тот же адрес памяти), а объект newDeepCopy глубоко копируется так же, как объект deepCopy (при создании каждый из них имеет уникальную ячейку памяти с соответствующими копируемыми свойствами). Я, должно быть, чего-то упускаю, потому что не вижу в этом пользы.

Может ли кто-нибудь дать реальный пример этого паттерна или хотя бы объяснить, в чем его польза?

1 Ответ

2 голосов
/ 04 августа 2020

Шаблон прототипа - это шаблон творческого проектирования. Это шаблон, который позволяет копировать / клонировать (создавать) экземпляры.

Клонирование, т.е. неглубокое копирование, означает: создается новый экземпляр объекта, но все его поля / ссылки повторно используются / копируются. Данные исходного экземпляра копируются в новый экземпляр. Каждая клонированная ссылка указывает на то же самое место в памяти, что и его исходная ссылка (для ссылочных типов).

В отличие от глубокого клона, т.е. глубокой копии: создается новый экземпляр объекта, но все его поля / ссылки инициализируются новыми экземплярами. Данные исходного экземпляра заменены новыми экземплярами. Каждая клонированная ссылка указывает на другую ячейку памяти, чем ее исходные ссылки.

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

Глубокое клонирование / глубокая копия полезны, когда вы хотите использовать прототип как форму фабрики (Абстрактная фабрика - это также шаблон создания), чтобы удалить зависимость от (конкретного) типа. Если у вас есть класс Employer, которому необходимо динамически создавать экземпляры, например, Appointment, вы можете использовать прототип IAppointment для их создания. Преимущество состоит в том, что вы можете создавать экземпляры, не вводя тесную связь с реализующим типом Appointment: вместо new Appointment(), используя конкретный тип, вы теперь вызываете IAppointment.DeepClone() для абстрактного типа. В этом случае, когда цель состоит в том, чтобы избежать тесной связи с реализациями, вы можете предложить глубокий клон, то есть глубокую копию прототипа.

Ваш пример неверно реализует клонирование. Текущая реализация CloneShallow возвращает тот же экземпляр (сам):

public Prototype CloneShallow()
{
  // Returns itself
  return this;
}

Employee prototype = new Employee();
Employee clone = prototype.CloneShallow() as Employee;

// Returns 'true' => the original instance was not copied to a new instance.
bool isTheSameInstance = object.ReferenceEquals(prototype, clone); 

Ваш текущий CloneDeep фактически делает то, что должен делать CloneShallow: копировать данные в новый экземпляр:

public Prototype CloneDeep()
{
  // Creates a new instance that references the same data (wrong)
  return new Employee { Appointment = this.Appointment };
}

Employee prototype = new Employee();
Employee clone = prototype.CloneDeep() as Employee;

// Returns 'false' => the original instance was successfully copied to a new instance, ...
bool isTheSameInstance = object.ReferenceEquals(prototype, clone); 

// ...but the data is still the same => shallow copy or clone
isTheSameInstance = object.ReferenceEquals(propotype.Appointment, clone.Appointment); // true

Чтобы класс мог создать неглубокую копию / клон самого себя, рекомендуется использовать object.MemberwiseClone() (ссылка также объясняет разницу между clone / deep clone еще раз).

Правильная и улучшенная реализация шаблона Prototype может выглядеть следующим образом:

public interface IPrototype<T>
{
  // Shallow copy. 
  // Use this if you want to use the prototype to create new instances while saving memory or resources 
  // by reusing/copying all internal references
  public T Clone();

  // Use this to create a new instance where all internal references are also new instances.
  public T DeepClone();
}

public class Employee : IPrototype<Employee>
{
  public IAppointment Appointment { get; set; }
  public string FullName { get; set; }

  public Employee()
  {
    this.Appointment = new Appointment();
    this.FullName = "Tony Montana";
  }

  public Employee Clone()
  {
    // Create a new instance which is a shallow copy of 'this'
    return object.MemberwiseClone() as Employee;
  }

  public Employee DeepClone()
  {
    // Create a new instance using Clone(). 
    // This way we only need to replace all reference types as value types linke 'int' are new references by language design
    var newEmployee = Clone();

    // Create new instances of the internal references too
    newEmployee.Appointment = new Appointment();
    newEmployee.FullName = String.Copy(this.FullName);
    return newEmployee;
  }
}

Доказательство:

Employee prototype = new Employee();
Employee deepClone = prototype.DeepClone();

// Returns 'false' => the original instance was successfully copied to a new instance, ...
bool isTheSameInstance = object.ReferenceEquals(prototype, deepClone); 

// ...and also the data is not the same => deep copy or deep clone
isTheSameInstance = object.ReferenceEquals(prototype.Appointment, deepClone.Appointment); // false
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...