неділя, 18 червня 2017 р.

Prototype

Прототип – один из самых простых паттернов, который позволяет нам получить точную копию необходимого объекта. То есть использовать как прототип для нового объекта.

Когда использовать:

  1. У нас есть семейство схожих объектов, разница между которыми только в состоянии их полей.
  2. Чтобы создать объект вам надо пройти через огонь, воду и медные трубы. Особенно если этот объект состоит из еще одной кучи объектов, многие из которых для заполнения требуют подгрузку даных из базы, веб сервисов и тому подобных источников. Часто, легче скопировать объект и поменять несколько полей.
  3. Да и в принципе, нам особо и не важно как создается объект. Ну есть и есть.
  4. Нам страшно лень писать иерархию фабрик (читайте дальше), которые будут инкапсулировать всю противную работу создания объекта.

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

Поверхностное и глубокое копирование

Тут особенно разницы с .NET нету. Есть указатель, есть значение в куче.

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

К примеру создадим объект:

@interface ASPerson : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *surname;
@property (nonatomic, copy) NSString *age;

@end

А теперь давайте просто создадим два объекта и посмотрим что же получится:

    ASPerson *firstPerson = [[ASPerson alloc] init];
    firstPerson.name = @"Dima";
    firstPerson.surname = @"Surname";
    ASPerson *secondPerson = firstPerson;
    NSLog(@"First Person name = %@ and surname = %@", firstPerson.name, firstPerson.surname);
    secondPerson.name = @"Roma";
    NSLog(@"Second Person name = %@ and surname = %@", secondPerson.name, secondPerson.surname);
    NSLog(@"First Person name = %@ and surname = %@", firstPerson.name, firstPerson.surname);

Как видим лог достаточно ожидаемый:

2017-06-18 22:33:52.234 PatternsObjC[1177:55666] First Person name = Dima and surname = Surname
2017-06-18 22:33:52.234 PatternsObjC[1177:55666] Second Person name = Roma and surname = Surname
2017-06-18 22:33:52.235 PatternsObjC[1177:55666] First Person name = Roma and surname = Surname

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

Для таких задач, стоит использовать глубокое копирование, которое в Objective-C сделанно в принципе очень похоже как и в .NET:

Для этого надо реализовать протокол NSCopying, и перегрузить:

- (id)copyWithZone:(nullable NSZone *)zone;

И да, не стоит переживать что мы не реализуем метод “copy”. Он уже есть у класса NSObject. Если вызвать этот метод, и не реализовать copyWithZone – то мы получем ошибку типа NSInvalidArgumentException.

Потому интерфейс нашего объекта теперь будет выглядеть следующим образом:

@interface ASPerson : NSObject <NSCopying>

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *surname;
@property (nonatomic, copy) NSString *age;

@end

а сама реализация:

@implementation ASPerson

#pragma mark - NSCopying

- (id)copyWithZone:(NSZone *)zone {
    ASPerson *copy = [[self class] allocWithZone:zone];
    
    copy.name = self.name;
    copy.surname = self.surname;
    copy.age = self.age;
    
    return copy;
}

@end

Теперь немного изменим код нашего тестового приложения:

    ASPerson *firstPerson = [[ASPerson alloc] init];
    firstPerson.name = @"Dima";
    firstPerson.surname = @"Surname";
    ASPerson *secondPerson = firstPerson.copy;
    NSLog(@"First Person name = %@ and surname = %@", firstPerson.name, firstPerson.surname);
    secondPerson.name = @"Roma";
    NSLog(@"Second Person name = %@ and surname = %@", secondPerson.name, secondPerson.surname);
    NSLog(@"First Person name = %@ and surname = %@", firstPerson.name, firstPerson.surname);

Ну, и естественно лог:

2017-06-18 22:47:01.577 PatternsObjC[1629:88078] First Person name = Dima and surname = Surname
2017-06-18 22:47:01.577 PatternsObjC[1629:88078] Second Person name = Roma and surname = Surname
2017-06-18 22:47:01.578 PatternsObjC[1629:88078] First Person name = Dima and surname = Surname

Как видим, мы в результате получили два независимых объекта, один из которых сделан по подобию первого.

Немає коментарів:

Дописати коментар

HyperComments for Blogger

comments powered by HyperComments