Прототип (пројектни узорак)

Прототип пројектни узорак је пројектни узорак креирања који се користи приликом развоја софтвера када се врста објекта који треба креирати одређује помоћу прототипске инстанце, која се клонира да би се добио нови објекат. Овај пројектни узорак се користи:

  • да се избегну поткласе које креирају објекте у клијентским апликацијама, као што је то случај код апстрактне фабрике.
  • да се избегне креирање нових објеката стандардним начинима (нпр. коришћењем кључне речи „new“) што некад може бити скупа операција.
  • када се класе специфицирају у време извршења, нпр. динамичким учитавањем.

Да би се овај пројектни узорак имплементирао, декларише се основна класа која обезбеђује чисту виртуалну clone() методу. Било коју класу којој треба могућност полиморфног конструктора треба извести од основне класе и имплементирати clone() операцију за њу.

Клијент, уместо да пише кôд који позива оператор „new“ са закуцаним именом класе може да позиве методу clone() над прототипом, или да позове фабрички метод са аргументима који ће одредити жељену конкретну поткласу за инстанцирање, или да позиве clone() методу на неки други начин (који је обезбедио неки други пројектни узорак).

Дијаграм класа

уреди
 

Примери

уреди

Прототип пројектни узорак специфицира врсте објеката који се креирају коришћењем прототипске инстанце. Прототипови нових објеката се често креирају пре стварне потребе за њима, али у овим примерима, прототип је пасиван и не учествује у копирању самог себе. Митоза, тј. дељење ћелије, чији су резултат две нове идентичне ћелије је пример прототипа који игра активну улогу у копирању самог себе и тако даје добар пример пројектног узорка прототипа. Када се ћелија подели, добијају се две ћелије са идентичним генотипима. Другим речима, ћелија клонира саму себе.

Јава

уреди
/**
 * Klasa prototipa
 */
abstract class PrototypeFactory implements Cloneable {
    public PrototypeFactory clone() throws CloneNotSupportedException {
        // zove Object.clone()
        PrototypeFactory copy = (PrototypeFactory) super.clone();
        // u stvarnoj implementaciji ovog uzorka, ovde mozete zameniti reference na
        // skupa kreiranja kopija koje se nalaze u ovom prototipu.
        return copy;
    }

    abstract void prototypeFactory(int x);

    abstract void printValue();
}

/**
 * Konkretan prototip za kloniranje
 */
class PrototypeImpl extends PrototypeFactory {
    int x;

    public PrototypeImpl(int x) {
        this.x = x;
    }

    @Override
    void prototypeFactory(int x) {
        this.x = x;
    }

    public void printValue() {
        System.out.println("Value : " + x);
    }
}

/**
 * Klijentska klasa
 */
public class PrototypeExample {

    private PrototypeFactory example; // ovde je mogao da bude i Cloneable tip

    public PrototypeExample(PrototypeFactory example) {
        this.example = example;
    }

    public PrototypeFactory makeCopy() throws CloneNotSupportedException {
        return this.example.clone();
    }

    public static void main(String args[]) {
        try {
            PrototypeFactory tempExample = null;
            int num = 1000;
            PrototypeFactory prot = new PrototypeImpl(1000);
            PrototypeExample cm = new PrototypeExample(prot);
            for (int i = 0; i < 10; i++) {
                tempExample = cm.makeCopy();
                tempExample.prototypeFactory(i * num);
                tempExample.printValue();
            }
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

Пајтон

уреди
import copy
#
# Klasa prototipa
#
class Cookie:
    def __init__(self, name):
        self.name = name
    
    def clone(self):
        return copy.deepcopy(self)
#
# Konkretan prototip za kloniranje
#
class CoconutCookie(Cookie):
    def __init__(self):
        Cookie.__init__(self, 'Coconut')
#
# Klijentska klasa
#
class CookieMachine:
    def __init__(self, cookie):
        self.cookie = cookie
 
    def make_cookie(self):
        return self.cookie.clone()
 
if __name__ == '__main__':
    prot = CoconutCookie()
    cm = CookieMachine(prot)

    for i in xrange(10):
        temp_cookie = cm.make_cookie()


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Prototype
{
    class PrototypeExample
    {
        abstract class PrototypeCloning<T> where T : PrototypeCloning<T>
        {
            public T Clone()
            {
                return (T)this.MemberwiseClone();
            }

            public abstract void Print();
            public abstract int X { get; set; }
        }

        class Implementation : PrototypeCloning<Implementation> 
        {
            public Implementation(int x) { X = x; }
            public override void Print()
            {
                Console.WriteLine(X);
            }

            public override int X { get; set; }
        }

        private PrototypeCloning<Implementation> example; 

        PrototypeExample(PrototypeCloning<Implementation> example)
        {
            this.example = example;
        }

        PrototypeCloning<Implementation> MakeCopy()
        {
            return this.example.Clone();
        }

        static void Main(string[] args)
        {
            int num = 1000;
            PrototypeCloning<Implementation> tempExample = null;
            PrototypeCloning<Implementation> prot = new Implementation(num);
            PrototypeExample cm = new PrototypeExample(prot);
            for (int i = 0; i < 10; i++)
            {
                tempExample = cm.MakeCopy();
                tempExample.X = i * num;
                tempExample.Print();
            }
        }
    }
}

Применљивост

уреди

Некада се пројектни узорци креирања преклапају - има случајева када је одговарајуће применити само прототип или апстрактну фабрику. У другим случајевима, они се надопуњују: апстрактна фабрика може да држи скуп прототипова које клонира и да враћа тако направљене објекте. Апстрактна фабрика, градитељ и прототип могу користити уникат у оквиру њихових имплементација. Класе апстрактне фабрике се често имплементирају помоћу фабричких метода (креирање кроз наслеђивање), али некад се могу имплементирати коришћењем прототипа (креирање кроз делегацију).

Често, почетни дизајн пројекта почиње са фабричком методом (простији, лако се прилагођава, али доводи до експлозије поткласа) и еволуира у апстрактну фабрику, прототип или градитеља (флексибилније, али и комплексније) како пројектант открива где је потребно више флексибилности.

Прототип не захтева наслеђивање, али захтева операцију „иницијализације“. Фабричка метода захтева наслешивање, али не захтева иницијализацију.

Пројектима који доста користе пројектне узорке композиције или декоратера често помаже прототип.

По овоме, применљивост је онда дефинисана тако да треба користити клонирање (clone() метода) објекта када се у време извршења жели креирати други објекат који је тачна копија објекта који се клонира. Тачна копија значи да сви чланови новокреираног објекта морају бити исти као у објекту који се клонира. Да се класа инстанцира коришћењем оператора new, добио би се објекат чији су чланови постављени на подразумеване, иницијалне вредности. На пример, ако се пројектује систем за обраду банкарских трансакција, требало би направити копију објекта који држи податке о налогу, извршити трансакцију са њим и заменити оригинални објекат са измењеним објектом. У таквим случајевима, требало би користити clone() уместо new.