Բազմակի օգտագործման (նախագծման ձևանմուշ)

Բազմակի օգտագործման նախագծման մոդելը նման է եզակի օգտագործման նախագծման մոդելին, որը թույլ է տալիս ստեղծել դասի միայն մեկ նմուշ։ Բազմակի օգտագործման մոդելն ընդլայնում է Եզակիի հնարավորությունները և թույլ է տալիս ունենալ անուններով հիշվող նմուշների մասիվ, ընդ որում, անունները հանդիսանում են նմուշների բանալի-արժեքները։

Բազմակի օգտագործման
ՏեսակՍտեղծող
ՆշանակությունԳլոբալ հասանելիություն է ապահովում միայն անուններով նմուշներ ունեցող դասի բոլոր նմուշներին
Կառուցվածք
Առավելություններհեշտացնում է ծրագրային ապահովումից ընդհանուր օբյեկտների դուրս բերումը
ԹերություններՀետևյալն է.
  • Դժվարեցնում է թեսթավորումը
  • Որոշ լեզուներում առաջացնում է հիշողության կուրուստի վտանգ
Նկարագրությունը ԳօՖի
"Design Patterns" գրքում
Չկա

Մարդկանցից շատերը այն համարում են եզակի օգտագործման նախագծման մոդելի ձև։ Օրինակ բազմակի օգտագործման ձևանմուշը ԳօՖի գրքում միայն հանդես է գալիս որպես եզակի օգտագործման մոդելի մասնակի ձև, որը թույլ է տալիս ռեգիստրացնել եզակի օգտագործման մոդելները՝ այն ավելի ճկուն դարձնելու նպատակով։

Ընդհանուր հասկացողություններ խմբագրել

  • Client - հայցող
  • Instance - նմուշ
  • Implementation - իրականացում
  • Product - արգասիք

Նկարագրություն խմբագրել

Բազմակի օգտագործման նախագծման ձևանմուշը գլոբալ հասանելիություն է ապահովում միայն անուններով նմուշներ ունեցող դասի բոլոր նմուշներին։

Կառուցվածք խմբագրել

 
Բազմակի օգտագործման նախագծման ձևանմուշի կառուցվածքը

Օգտագործում խմբագրել

Առաջին հայացքից թվում է, թե բազմակի օգտագործումը ոչ ավելի է քան հեշ աղյուսակների օգտագործումով հասանելիությունը սինխրոնիզացնող մոդել, բայց այն երկու կարևոր տարբերություն ունի։ Առաջին հերթին բազմակի օգտագործումը հայցողներին թույլ չի տալիս mapping-ներ ավելացնել։ Երկրորդ հերթին բազմակի օգտագործումը երբեք null կամ դատարկ հղում չի վերադրաձնում։ Դրա փոխարեն այն ստեղծում և պահում է բազմակիի նմուշը նրա հետ կապված «բանալու» (անգլ.՝ key) հետ։ Նույն բանալիով հաջորդող կանչերի դեպքում կվերադարձվի իրական նմուշը։ Հեշ աղյուսակը միայն հանդիսանում է իրականացման դետալներից մեկը, բայց ոչ միակը։ Ձևանմուշը հեշտացնում է ծրագրային ապահովումից (անգլ.՝ application) ընդհանուր (անգլ.՝ shared) օբյեկտների դուրս բերումը։

Քանի որ ավազան օբյեկտը ստեղծվում է միայն մեկ անգամ՝ ասոցացված լինելով դասի (նմուշի փոխարեն), ապա բազմակի օգտագործումը պահում է իր հարթ վարքագիծը (անգլ.՝ behavior) և չի վերածվում ծառատիպ կառուցվածքի։

Բազմակի օգտագործման ձևանմուշը ունիկալ է նրանով, որ այն ապահովում է կենտրոնացված հասանելիություն մի ուղղությամբ (այսինքն բոլոր բանալիները գտնվում են միևնույն անվանատարածքում), որտեղ ամեն մի նմուշ ունի իր սեփական վիճակը։ Այսպիսով ձևանմուշը հանդիսանում է օյեկտները համակարգում ինդեքսավորումով պահող մոդել։ Բայց և այնպես, բազմակի օգտագործումը ունի սահմանափակ կիրառություն եզակի համակարգում՝ ի տարբերություն բազմաթիվ բաշխվածություն ունեցող համակարգերում (անգլ.՝ distributed systems

Թերություններ խմբագրել

Այս ձևանմուշը եզակի օգտագործման ձևանմուշի նման թեսթավորումը դարձնում է բավականին դժվար[1], քանի որ այն գլոբալ վիճակ է մտցնում ծրագրային ապահովվածության մեջ։

Բազմակի օգտագործման ձևանմուշը օբյեկտների վրա օգտագործում է գլոբալ հզոր հղումներ, որը որոշ ծրագրավորման լեզուներում կարող է բերել հիշողության կորստի (անգլ.՝ memory leaks

Օրինակներ խմբագրել

Ահա պարզեցված օգտագործման օրինակներ մի շարք ծրագրավորման լեզուներով.

Java խմբագրել

public class FooMultiton {
    private static final Map<Object, FooMultiton> instances = new HashMap<Object, FooMultiton>();

    private FooMultiton() {
        // no explicit implementation
    }

    public static synchronized FooMultiton getInstance(Object key) {

        // Our "per key" singleton
        FooMultiton instance = instances.get(key);
            
        if (instance == null) {
            // Lazily create instance
            instance = new FooMultiton();

            // Add it to map   
            instances.put(key, instance);
        }

        return instance;
    }

    // other fields and methods ...
}

ActionScript 3.0 խմբագրել

    import flash.utils.Dictionary;
    
    public class InternalModelLocator {
		private static var instances:Dictionary = new Dictionary();
                private static var allowCreation:boolean = false;		

		public function InternalModelLocator() {
                   if(!allowCreation){
                      throw new Error("Only one instance allowed to be created, and only with GetInstanceMethod"); 
                   }
		}
		
		/* module_uuid can be a String --------
                   In case of PureMVC "multitonKey" (this.multitonKey) can be used as unique key for multiple modules
                */
		public static function getInstance(module_uuid:String):InternalModelLocator {
			var instance:InternalModelLocator = instances[module_uuid];
			
			if (instance == null) {
                                allowCreation = true; 
				instance = new InternalModelLocator();
                                allowCreation = false;
				instances[module_uuid] = instance;
			}
			return instance;
		}
	}

C# խմբագրել

using System.Collections.Generic;
using System.Collections.Concurrent;

namespace MyApplication
{
    public class Multiton<T> where T : new() // generic multition.
    {
        private static readonly ConcurrentDictionary<object, T> _instances = new ConcurrentDictionary<object, T>();

        private Multiton() { }

        public static T GetInstance(object key) 
        {
            return _instances.GetOrAdd(key, (k) => new T());
        }
    }
}

Բազմակի օգտագործման ձևանմուշի բավականին պարզ օրինակ private կոնստրուկտորով և ստեղծված օբյեկներ ռեգիստրացնող մոդելով.

//IVSR: Multiton design pattern
namespace IVSR.DesignPatterns.Multiton
{
    public class Multiton
    {
        //read-only dictionary to track multitons
        private static IDictionary<int, Multiton> _Tracker = new Dictionary<int, Multiton> { };

        private Multiton()
        {
        }

        public static Multiton GetInstance(int key)
        {
            //value to return
            Multiton item = null;
            
            //lock collection to prevent changes during operation
            lock (_Tracker)
            { 
                //if value not found, create and add
                if(!_Tracker.TryGetValue(key, out item))
                {
                    item = new Multiton();

                    //calculate next key
                    int newIdent = _Tracker.Keys.Max() + 1;

                    //add item
                    _Tracker.Add(newIdent, item);
                }
            }
            return item;
        }
    }
}

C++ խմբագրել

Ճշգրտված իրականացումը StackOverflow կայքից.

#ifndef MULTITON_H
#define MULTITON_H

#include <map>
#include <string>

template <typename T, typename Key = std::string>
class Multiton
{
public:
    static void destroy()
    {
        for (typename std::map<Key, T*>::const_iterator it = instances.begin(); it != instances.end(); ++it)
            delete (*it).second;
        instances.clear();
    }

    static T* getPtr(const Key& key) {
        typename std::map<Key, T*>::const_iterator it = instances.find(key);

        if (it != instances.end()) {
            return (T*)(it->second);
        }

        T* instance = new T();
        instances[key] = instance;
        return instance;
    }

    static T& getRef(const Key& key) {
        return *getPtr(key);
    }

protected:
    Multiton() {}
    ~Multiton() {}

private:
    Multiton(const Multiton&) {}
    Multiton& operator= (const Multiton&) { return *this; }

    static std::map<Key, T*> instances;
};

template <typename T, typename Key>
std::map<Key, T*> Multiton<T, Key>::instances;

#endif

// example usage
class Foo : public Multiton<Foo> {};
Foo& foo1 = Foo::getRef("foobar");
Foo* foo2 = Foo::getPtr("foobar");
Foo::destroy();

PHP խմբագրել

<?php
// This example requires PHP 5.3+
abstract class Multiton {
    private static $instances = array();
    public static function getInstance() {
	// For non-complex construction arguments, you can just use the $arg as the key
		$key = get_called_class() . serialize(func_get_args());
		if (!isset(self::$instances[$key])) {
		// You can do this without the reflection class if you want to hard code the class constructor arguments
			$rc = new ReflectionClass(get_called_class());
			self::$instances[$key] = $rc->newInstanceArgs(func_get_args());
		}
		return self::$instances[$key];
    }
}

class Hello extends Multiton {
	public function __construct($string = 'World') {
		echo "Hello $string\n";
	}
}

class GoodBye extends Multiton {
	public function __construct($string = 'my', $string2 = 'darling') {
		echo "Goodbye $string $string2\n";
	}
}

$a = Hello::getInstance('World');
$b = Hello::getInstance('bob');
// $a !== $b

$c = Hello::getInstance('World');
// $a === $c

$d = GoodBye::getInstance();
$e = GoodBye::getInstance();
// $d === $e

$f = GoodBye::getInstance('your');
// $d !== $f

Python խմբագրել

class A(object):
    def __init__(self, *args, **kw):
        pass

multiton = {}
a0 = multiton.setdefault('a0', A()) # get object by key, or create new and return it
a1 = multiton.setdefault('a1', A())
print multiton.get('a0')
print multiton.get('a1')

Դեկորատորների օգտագործում խմբագրել

def multiton(cls):
    instances = {}
    def getinstance(name):
        if name not in instances:
            instances[name] = cls()
        return instances[name]
    return getinstance

@multiton
class MyClass:
    ...

a=MyClass("MyClass0")
b=MyClass("MyClass0")
c=MyClass("MyClass1")
print a is b #True
print a is c #False

Perl խմբագրել

use strict;
use warnings;

package MyClass {
    use Moose;

    has name => ( is => 'ro', isa => 'Str', required => 1 );
}

package Multiton { # version 5.14 package syntax
    my %_objs;
    our $_class = 'MyClass';

    sub set {
        my $self = shift;
        my ( $key, @args ) = @_;
        die "key unspecifiied" unless defined $key;
        die "key is not a plain scalar" if ref($key);
        if ( exists $_objs{$key} ) {
            $_objs{$key};
        } else {
            $_objs{$key} = $_class->new(@args);
        }
    }

    sub get {
        my $self = shift;
        my $class = ref($self) || $self;
        $class->set(@_);
    }

    sub delete {
        my $self = shift;
        for my $key (@_) {
            delete $_objs{$key};
        }
    }
}

my $a = Multiton->get( 'MyClassA' => ( name => 'Rover' ) );
my $b = Multiton->get( 'MyClassB' => ( name => 'Roger' ) );
my $c = Multiton->get('MyClassA');

print $a == $b ? 'true' : 'false', "\n"; # false
print $a == $c ? 'true' : 'false', "\n"; # true

Ծանոթագրություններ խմբագրել