Дисциплины - Объектно-ориентированное программирование

Шаблоны проектирования практических задач - Порождающие шаблоны проектирования - Прототип

Prototype (Прототип) относиться к классу порождающих шаблонов. Прототип позволяет копировать объекты, не вдаваясь в подробности их реализации. Он используется для задания вида создаваемых объектов на основе объекта прототипа, от которого происходит передача внутреннего состояния. Он сродни фабричному методу, позволяет избавиться от жесткой привязки к классам, задаёт виды создаваемых объектов с помощью экземпляра-прототипа и создаёт новые объекты путём копированияэтого прототипа.

Применение

Шаблон используется чтобы:

  • избежать дополнительных усилий по созданию объекта стандартным путем (имеется в виду использованиеключевого слова 'new', когда вызывается конструктор не только самого объекта, но и конструкторы всей иерархии предков объекта), когда это непозволительно дорого для приложения.
  • избежать наследования создателя объекта (object creator) в клиентском приложении, как это делает шаблон абстрактная фабрика.

Используйте этот шаблон проектирования, когда система не должна зависеть от того, как в ней создаются,компонуются и представляются продукты:

  • инстанцируемые классы определяются во время выполнения, например с помощью динамической загрузки;
  • для того чтобы избежать построения иерархий классов или фабрик, параллельных иерархии классовпродуктов;
  • экземпляры класса могут находиться в одном из нескольких различных состояний. Может оказаться удобнееустановить соответствующее число прототипов и клонировать их, а не инстанцировать каждый раз классвручную в подходящем состоянии.

Встроенные прототипы в JavaScript

Свойство "prototype" широко используется внутри самого языка JavaScript. Все встроенные функции-конструкторы используют его.

Сначала мы рассмотрим детали, а затем используем "prototype" для добавления встроенным объектам новой функциональности.

Object.prototype

Давайте выведем пустой объект:

let obj = {};
alert( obj ); // "[object Object]" ?

Где код, который генерирует строку "[object Object]"? Это встроенный метод toString, но где он? obj ведь пуст!

…Но краткая нотация obj = {} – это то же самое, что и obj = new Object(), где Object – встроенная функция-конструктор для объектов с собственным свойством prototype, которое ссылается на огромный объект с методом toString и другими.

Когда вызывается new Object() (или создаётся объект с помощью литерала {...}), свойство [[Prototype]] этого объекта устанавливается на Object.prototype.

Таким образом, когда вызывается obj.toString(), метод берётся из Object.prototype.

Мы можем проверить это так:

let obj = {};

alert(obj.__proto__ === Object.prototype); // true
// obj.toString === obj.__proto__.toString == Object.prototype.toString

Другие встроенные прототипы

Другие встроенные объекты JavaScript, такие как Array, Date, Function и другие, также хранят свои методы в прототипах.

Например, при создании массива [1, 2, 3] внутренне используется конструктор массива Array. Поэтому прототипом массива становится Array.prototype, предоставляя ему свои методы. Это позволяет эффективно использовать память.

Согласно спецификации, наверху иерархии встроенных прототипов находится Object.prototype. Поэтому иногда говорят, что «всё наследует от объектов».

let arr = [1, 2, 3];
// наследует ли от Array.prototype?
alert( arr.__proto__ === Array.prototype ); // true
// затем наследует ли от Object.prototype?
alert( arr.__proto__.__proto__ === Object.prototype ); // true
// и null на вершине иерархии
alert( arr.__proto__.__proto__.__proto__ ); // null

В браузерных инструментах, таких как консоль разработчика, можно посмотреть цепочку наследования (возможно, потребуется использовать console.dir для встроенных объектов):

Другие встроенные объекты устроены аналогично. Даже функции – они объекты встроенного конструктора Function, и все их методы (call/apply и другие) берутся из Function.prototype. Также у функций есть свой метод toString.

Реализация на PHP

Существует множество реализаций данного шаблона в PHP. Рассмотрим 3 основных:

    // Method 1.
    class ClassA
    {
        public function __construct(ClassA $Prototype = null)
        {
            if (null !== $Prototype)
            {
                //do something
            }
        }
    }
    $Prototype = new ClassA();
    $NewObject = new ClassA($Prototype);
    // Method 2
    class ClassB
    {
        /**
         * This function return new object
         *
         * @return ClassB
         */
        public function getClone()
        {
           $object = new ClassB();
           //do something with object
           return $object;
        }
    }
    $Prototype = new ClassB();
    $NewObject = $Prototype -> getClone();
    // Method 3
    class ClassC
    {
        public function __clone()
        {
            //do something
        }
    }
    $Prototype = new ClassC();
    $NewObject = clone $Prototype;

В первом методе в конструктор поступает объект прототип, из которого мы получаем всю необходимую информацию. Недостаток такого метода в том, что мы все равно имеем жесткую привязку к классу. В таком случае шаблон прототип обычно используют совместно с фабричным методом.

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

Третий метод схож со вторым, за исключением того, что порождение нового объекта происходит встроенными средствами php. Этот метод часто бывает наиболее предпочтителен.

Реализация в Phyton

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

Проще, если бы ни один класс в меню не нуждался в аргументах в __init__():

class Sharp(object):
    "The symbol ♯."

class Flat(object):
    "The symbol ♭."

Вместо этого, в дело вступает паттерн «Прототип», когда требуется создание экземпляров классов с заранее заданными списками аргументов:

class Note(object):
    "Musical note 1 ÷ `fraction` measures long."
    def __init__(self, fraction):
        self.fraction = fraction

Питоническим подходом является проектирование классов исключительно с позиционными аргументами, без именованных. Затем легко хранить аргументы в виде кортежа, который предоставляется отдельно от самого класса. Это знакомый подход класса стандартной библиотеки Thread, который запрашивает вызываемый target= отдельно от передаваемых args=(...). Вот наши пункты меню:

menu = {
    'whole note': (Note, (1,)),
    'half note': (Note, (2,)),
    'quarter note': (Note, (4,)),
    'sharp': (Sharp, ()),
    'flat': (Flat, ()),
}

В качестве альтернативы, каждый класс и аргументы располагайте в одном кортеже:

menu = {
    'whole note': (Note, 1),
    'half note': (Note, 2),
    'quarter note': (Note, 4),
    'sharp': (Sharp,),
    'flat': (Flat,),
}

Затем структура будет вызывать каждый объект с использованием некоторой вариации tup[0](*tup[1:]).

Однако, возможно, классу потребуются не только позиционные аргументы, но и именованные. В ответ на это предоставьте простые вызываемые объекты, используя лямбда-выражения для классов, которые требуют аргументов:

menu = {
    'whole note': lambda: Note(fraction=1),
    'half note': lambda: Note(fraction=2),
    'quarter note': lambda: Note(fraction=4),
    'sharp': Sharp,
    'flat': Flat,
}

Хотя лямбда-выражения не поддерживают быструю интроспекцию для проверки, они хорошо работают, если структура только вызывает их.

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

# Чего избегает паттерн «Прототип»:
# создания фабрик для каждого класса.

class NoteFactory(object):
    def __init__(self, fraction):
        self.fraction = fraction

    def build(self):
        return Note(self.fraction)

class SharpFactory(object):
    def build(self):
        return Sharp()

class FlatFactory(object):
    def build(self):
        return Flat()

К счастью, ситуация не такая мрачная. Если перечитаете фабричные классы выше, то заметите, что каждый из них удивительно похож на целевые классы, которые хотим создать. Так же, как и Note, NoteFactory сам хранит атрибут fraction. Стек фабрик выглядит, как минимум, списками атрибутов, как стек создаваемых целевых классов.

Эта симметрия предлагает способ решения нашей проблемы без зеркалирования каждого класса с помощью фабрики. Что, если бы мы использовали сами исходные объекты для хранения аргументов и дали им возможность предоставлять новые экземпляры?

Результатом будет паттерн «Прототип», который напишем на Python с нуля. Все фабричные классы исчезают. Вместо этого у каждого объекта появляется метод clone(), на вызов которого он отвечает созданием нового экземпляра с полученными аргументами:

# Шаблон «Прототип»: научите каждый экземпляр 
# объекта создавать копии самого себя.

class Note(object):
    "Musical note 1 ÷ `fraction` measures long."
    def __init__(self, fraction):
        self.fraction = fraction

    def clone(self):
        return Note(self.fraction)

class Sharp(object):
    "The symbol ♯."
    def clone(self):
        return Sharp()

class Flat(object):
    "The symbol ♭."
    def clone(self):
        return Flat()

Количество комментариев: 0

Для того, чтобы оставить коментарий необходимо зарегистрироваться
814301 БГУИР
814302 БГУИР
814303 БГУИР
894351 БГУИР
90421 БГУИР


Изображения Видео

1. Абстрактная фабрика https://www.youtube.com/watch?v=1mVONOCxfLg
2. Фабричный метод https://www.youtube.com/watch?v=5UqUDR6_2cY
3. Шаблон декоратор https://www.youtube.com/watch?v=Lwb9bm8yKD0
4. Dessign patterns on PHP https://github.com/domnikl/DesignPatternsPHP
5. Приёмы объектно-ориентированного проектирования. Паттерны проектирования Э. Гамма, Р. Хелм, Р. Джонсон, Д. Влиссидес; [пер. с англ.: А. Слинкин науч. ред.: Н. Шалаев]. — Санкт-Петербург [и др.] : Питер, 2014. — 366 с. : ил. ; 24 см.
6. Приемы объектно-ориентированного проектирования. Паттерны проектирования Э. Гамма, Р. Хелм, Р. Джонсон, Д. Влиссидес; [пер. с англ.: А. Слинкин науч. ред.: Н. Шалаев]. — Санкт-Петербург [и др.] : Питер, 2014. — 366 с. : ил. ; 24 см.
7. Ajax http://erud.by/ajax
8. Ajax http://erud.by/ajax
9. Ajax http://erud.by/ajax
10. Документация Laravel http://laravel.com
Задание к курсовой работе
Задание к курсовой работе
Вопросы к экзамену