Шаблоны программирования на Swift: Prototype
Хотел бы представить на суд читателей новый раздел сайта — Паттерны проектирования в iOS программировании.
Wikipedia нам говорит:
Шаблон проектирования или паттерн (англ. design pattern) в разработке программного обеспечения — повторимая архитектурная конструкция, представляющая собой решение проблемы проектирования в рамках некоторого часто возникающего контекста.
Для того чтобы разобраться в шаблонах программирования или шаблонах проектирования (Design pattern) я взял книгу Дмитрия Малеева
«Хрестоматия iOS паттернов. На всякий…» и читая ее, переписывал код на Swift, пытался вникнуть во все тонкости и решить все проблемы, которые мне встречались.
Материал данного раздела не претендует на уникальность, да и что может быть уникального в распространенных шаблонах объектно ориентированного программирования. Этот материал я выкладываю сюда для собственного понимания, закрепления и для обсуждения читателями. Я — не истина в последней инстанции и всегда готов выслушать критику и исправить ошибки.
Прототип – один из самых простых паттернов, который позволяет нам получить точную копию необходимого объекта. Тоесть использовать как прототип для нового объекта.
Когда использовать:
1. У нас есть семейство схожих объектов, разница между которыми только в состоянии их полей.
2. Чтобы создать объект вам надо пройти через огонь, воду и медные трубы. Особенно если этот объект состоит из еще одной кучи объектов, многие из которых для заполнения требуют подгрузку даных из базы, веб сервисов и тому подобных источников. Часто, легче скопировать объект и поменять несколько полей
3. Нам не важно как создается объект.
4. Нам страшно лень писать иерархию фабрик (читай дальше), которые будут инкапсулировать всю противную работу создания объекта
Иными словами если для создания объекта нужно затратить много ресурсов или выполнить кучу условий и требований, а объектов нам требуется много, то проще создать один прототип, затем копировать его и заменять значения свойств на необходимые.
И вот тут нужно разобрать понятия «поверхностное копирование» и «глубокое копирование». В переменной, которая содержит экземпляр класса на самом деле содержится указатель на блок памяти в куче, где расположен этот экземпляр. Поверхностное копирование – это просто создание нового указателя на те же самые байты в куче. То есть, в результате мы можем получить два объекта, которые указывают на одно и тоже значение.
Перейдем к примеру
// // proSwift.ru // import Foundation class Person: NSObject { var name = "" var surname = "" }
А теперь создадим два объекта нашего класса, поменяем значения свойств, выведем лог и посмотрим что же получится:
// // proSwift.ru // let firstPerson = Person() firstPerson.name = "Pavel" firstPerson.surname = "Davidoff" let secondPerson = firstPerson print("First person name: \(firstPerson.name) and surname: \(firstPerson.surname)") secondPerson.name = "Alex" secondPerson.surname = "Black" print("Second person name: \(secondPerson.name) and surname: \(secondPerson.surname)") print("First person name: \(firstPerson.name) and surname: \(firstPerson.surname)")
Вывод лога в консоль:
First person name: Pavel and surname: Davidoff Second person name: Alex and surname: Black First person name: Alex and surname: Black
Думаю, понятно, что при создании второго указателя secondPerson, он сослался на тот же экземпляр что и firstPerson, и поэтому изменения свойств через один указатель повлияют и на другой.
Именно для создания копии объектов в памяти следует использовать глубокое копирование, которое в Swift реализовано протоколом NSCopying, и методом этого протокола
// public func copyWithZone(zone: NSZone) -> AnyObject
Изменим реализация нашего класса, чтобы он соответствовал протоколу, и добавим инициализатор от объекта собственно типа.
// // proSwift.ru // import Foundation class Person: NSObject, NSCopying { var name = "" var surname = "" // это чтобы реализовать NSCopyng на Swift required override init() { } required init(_ person: Person) { self.name = person.name self.surname = person.surname } func copyWithZone(zone: NSZone) -> AnyObject { return self.dynamicType.init(self) } }
Создаем третий и четвертый объекты, только для создания четвертого используем написанный ранее инициализатор.
// // proSwift.ru // let thirdPerson = Person() thirdPerson.name = "Pavel" thirdPerson.surname = "Davidoff" print("Third person name: \(thirdPerson.name) and surname: \(thirdPerson.surname)") let fourthPerson = Person(thirdPerson) fourthPerson.name = "Alex" fourthPerson.surname = "Black" print("Fourth person name: \(fourthPerson.name) and surname: \(fourthPerson.surname)") print("Third person name: \(thirdPerson.name) and surname: \(thirdPerson.surname)")
Вполне ожидаемый лог в консоле:
Third person name: Pavel and surname: Davidoff Fourth person name: Alex and surname: Black Third person name: Pavel and surname: Davidoff
И последнее, что нужно сказать про NSCopyng. Ведь мы нигде не использовали метод этого протокола. Это потому, что NSZone больше не используется в Swift да и в Objective-C в течение длительного времени. И передающийся в этот метод аргумент игнорируется. Этот метод существует по историческим причинам.
Ну и напоследок приведу пример использования шаблона программирования Prototype, реализованного без использования протокола NSCopyng, а лишь преимущества языка программирования Swift. Как было сказано выше — шаблон прототип используется для создания экземпляра нового объекта путем копирования всех свойств существующего объекта, создание независимого клона. Эта практика особенно полезна , когда создание нового объекта является неэффективным.
// // proSwift.ru // // https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/source/creational/prototype.swift class ChungasRevengeDisplay { var name: String? let font: String init(font: String) { self.font = font } func clone() -> ChungasRevengeDisplay { return ChungasRevengeDisplay(font:self.font) } } let Prototype = ChungasRevengeDisplay(font:"GotanProject") let Philippe = Prototype.clone() Philippe.name = "Philippe" let Christoph = Prototype.clone() Christoph.name = "Christoph" let Eduardo = Prototype.clone() Eduardo.name = "Eduardo"
В следующей стате мы рассмотрим шаблоны программирования Factory:Шаблоны программирования на Swift: Factory
Добавить комментарий