Design patterns
फ़ैक्टरी
खोज…
टिप्पणियों
संबंधित या आश्रित वस्तुओं के परिवारों को उनके ठोस वर्गों को निर्दिष्ट किए बिना बनाने के लिए एक इंटरफ़ेस प्रदान करें।
- जीओएफ 1994
सरल कारखाना (जावा)
एक कारखाने में कोड के बीच युग्मन घट जाता है जिसे ऑब्जेक्ट क्रिएशन कोड से ऑब्जेक्ट बनाने की आवश्यकता होती है। ऑब्जेक्ट निर्माण स्पष्ट रूप से एक क्लास कंस्ट्रक्टर को कॉल करके नहीं किया जाता है, लेकिन कॉल करने वाले की ओर से ऑब्जेक्ट बनाने वाले कुछ फ़ंक्शन को कॉल करके। एक सरल जावा उदाहरण निम्नलिखित है:
interface Car {
}
public class CarFactory{
static public Car create(String s) {
switch (s) {
default:
case "us":
case "american": return new Chrysler();
case "de":
case "german": return new Mercedes();
case "jp":
case "japanese": return new Mazda();
}
}
}
class Chrysler implements Car {
public String toString() { return "Chrysler"; }
}
class Mazda implements Car {
public String toString() { return "Mazda"; }
}
class Mercedes implements Car {
public String toString() { return "Mercedes"; }
}
public class CarEx {
public static void main(String args[]) {
Car car = CarFactory.create("us");
System.out.println(car);
}
}
इस उदाहरण में, उपयोगकर्ता बस कुछ संकेत देता है कि उसे क्या चाहिए और कारखाना कुछ उपयुक्त निर्माण करने के लिए स्वतंत्र है। यह एक निर्भरता उलटा है : Car
अवधारणा का कार्यान्वयन उपयोगकर्ता द्वारा अनुरोध की गई एक उपयुक्त कंक्रीट Car
को वापस करने के लिए स्वतंत्र है जो बदले में निर्मित ठोस वस्तु का विवरण नहीं जानता है।
यह एक सरल उदाहरण है कि कारखाना कैसे काम करता है, निश्चित रूप से इस उदाहरण में कंक्रीट कक्षाओं को तत्काल करना संभव है; लेकिन एक पैकेज में कंक्रीट कक्षाओं को छिपाकर इसे रोका जा सकता है, जैसे कि उपयोगकर्ता को कारखाने का उपयोग करने के लिए मजबूर किया जाता है।
उपरोक्त उदाहरण के लिए। नेट फिडेल ।
सार कारखाना (C ++)
एब्सट्रैक्ट फैक्ट्री पैटर्न कारखानों के कार्यों के संग्रह के माध्यम से वस्तुओं के सुसंगत संग्रह को प्राप्त करने का एक तरीका प्रदान करता है। जैसा कि हर पैटर्न के लिए, युग्मन को कम किया जाता है जिस तरह से वस्तुओं का एक सेट बनाया जाता है, जिससे उपयोगकर्ता कोड को उन वस्तुओं के कई विवरणों से अनजान होता है जिनकी उन्हें आवश्यकता होती है।
निम्न C ++ उदाहरण दिखाता है कि एक ही (काल्पनिक) GUI परिवार की विभिन्न प्रकार की वस्तुओं को कैसे प्राप्त किया जाए:
#include <iostream>
/* Abstract definitions */
class GUIComponent {
public:
virtual ~GUIComponent() = default;
virtual void draw() const = 0;
};
class Frame : public GUIComponent {};
class Button : public GUIComponent {};
class Label : public GUIComponent {};
class GUIFactory {
public:
virtual ~GUIFactory() = default;
virtual std::unique_ptr<Frame> createFrame() = 0;
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<Label> createLabel() = 0;
static std::unique_ptr<GUIFactory> create(const std::string& type);
};
/* Windows support */
class WindowsFactory : public GUIFactory {
private:
class WindowsFrame : public Frame {
public:
void draw() const override { std::cout << "I'm a Windows-like frame" << std::endl; }
};
class WindowsButton : public Button {
public:
void draw() const override { std::cout << "I'm a Windows-like button" << std::endl; }
};
class WindowsLabel : public Label {
public:
void draw() const override { std::cout << "I'm a Windows-like label" << std::endl; }
};
public:
std::unique_ptr<Frame> createFrame() override { return std::make_unique<WindowsFrame>(); }
std::unique_ptr<Button> createButton() override { return std::make_unique<WindowsButton>(); }
std::unique_ptr<Label> createLabel() override { return std::make_unique<WindowsLabel>(); }
};
/* Linux support */
class LinuxFactory : public GUIFactory {
private:
class LinuxFrame : public Frame {
public:
void draw() const override { std::cout << "I'm a Linux-like frame" << std::endl; }
};
class LinuxButton : public Button {
public:
void draw() const override { std::cout << "I'm a Linux-like button" << std::endl; }
};
class LinuxLabel : public Label {
public:
void draw() const override { std::cout << "I'm a Linux-like label" << std::endl; }
};
public:
std::unique_ptr<Frame> createFrame() override { return std::make_unique<LinuxFrame>(); }
std::unique_ptr<Button> createButton() override { return std::make_unique<LinuxButton>(); }
std::unique_ptr<Label> createLabel() override { return std::make_unique<LinuxLabel>(); }
};
std::unique_ptr<GUIFactory> GUIFactory::create(const string& type) {
if (type == "windows") return std::make_unique<WindowsFactory>();
return std::make_unique<LinuxFactory>();
}
/* User code */
void buildInterface(GUIFactory& factory) {
auto frame = factory.createFrame();
auto button = factory.createButton();
auto label = factory.createLabel();
frame->draw();
button->draw();
label->draw();
}
int main(int argc, char *argv[]) {
if (argc < 2) return 1;
auto guiFactory = GUIFactory::create(argv[1]);
buildInterface(*guiFactory);
}
यदि उत्पन्न निष्पादन योग्य नाम abstractfactory
तो आउटपुट दे सकता है:
$ ./abstractfactory windows
I'm a Windows-like frame
I'm a Windows-like button
I'm a Windows-like label
$ ./abstractfactory linux
I'm a Linux-like frame
I'm a Linux-like button
I'm a Linux-like label
एक आईओसी (C #) का उपयोग करने वाले कारखाने का सरल उदाहरण
फैक्ट्रीज का उपयोग इन्वर्शन ऑफ कंट्रोल (IoC) लाइब्रेरी के साथ भी किया जा सकता है।
- इस तरह के कारखाने के लिए विशिष्ट उपयोग का मामला तब है जब हम मापदंडों के आधार पर एक ऐसी वस्तु बनाना चाहते हैं जो रन-टाइम (जैसे कि वर्तमान उपयोगकर्ता) तक ज्ञात नहीं हो।
- इन मामलों में यह कभी-कभी मुश्किल हो सकता है (यदि असंभव नहीं है) इस तरह की रनटाइम प्रासंगिक जानकारी को संभालने के लिए अकेले आईओसी लाइब्रेरी को कॉन्फ़िगर करना है, इसलिए हम इसे एक कारखाने में लपेट सकते हैं।
उदाहरण
- मान लें कि हमारे पास एक
User
वर्ग है, जिसकी विशेषताएं (आईडी, सुरक्षा मंजूरी स्तर, आदि) रनटाइम तक अज्ञात हैं (चूंकि वर्तमान उपयोगकर्ता कोई भी हो सकता है जो एप्लिकेशन का उपयोग करता है)। - हमें वर्तमान उपयोगकर्ता को लेने और उनके लिए एक
ISecurityToken
प्राप्त करने की आवश्यकता है, जिसका उपयोग तब यह जांचने के लिए किया जा सकता है कि उपयोगकर्ता को कुछ कार्य करने की अनुमति है या नहीं। - ISecurityToken का कार्यान्वयन उपयोगकर्ता के स्तर के आधार पर अलग-अलग होगा - दूसरे शब्दों में, ISecurityToken बहुरूपता का उपयोग करता है ।
इस मामले में, हमारे पास दो कार्यान्वयन हैं, जो IoC लाइब्रेरी को पहचानने में आसान बनाने के लिए मार्कर इंटरफेसेस का उपयोग करते हैं; इस मामले में IoC पुस्तकालय अभी बना हुआ है और अमूर्त IContainer
द्वारा पहचाना जाता है।
यह भी ध्यान दें कि कई आधुनिक IoC कारखानों में मूल क्षमताएं या प्लगइन्स हैं जो नीचे दिखाए गए मार्कर इंटरफेस की आवश्यकता से बचने के साथ-साथ कारखानों के ऑटो-निर्माण की अनुमति देते हैं; हालाँकि, सभी ऐसा नहीं करते हैं, यह उदाहरण एक सरल, निम्नतम सामान्य कार्यक्षमता अवधारणा को पूरा करता है।
//describes the ability to allow or deny an action based on PerformAction.SecurityLevel
public interface ISecurityToken
{
public bool IsAllowedTo(PerformAction action);
}
//Marker interface for Basic permissions
public interface IBasicToken:ISecurityToken{};
//Marker interface for super permissions
public interface ISuperToken:ISecurityToken{};
//since IBasictoken inherits ISecurityToken, BasicToken can be treated as an ISecurityToken
public class BasicToken:IBasicToken
{
public bool IsAllowedTo(PerformAction action)
{
//Basic users can only perform basic actions
if(action.SecurityLevel!=SecurityLevel.Basic) return false;
return true;
}
}
public class SuperToken:ISuperToken
{
public bool IsAllowedTo(PerformAction action)
{
//Super users can perform all actions
return true;
}
}
आगे हम एक SecurityToken
फैक्ट्री बनाएंगे, जो हमारे IContainer
निर्भरता के रूप में होगी
public class SecurityTokenFactory
{
readonly IContainer _container;
public SecurityTokenFactory(IContainer container)
{
if(container==null) throw new ArgumentNullException("container");
}
public ISecurityToken GetToken(User user)
{
if (user==null) throw new ArgumentNullException("user);
//depending on the user security level, we return a different type; however all types implement ISecurityToken so the factory can produce them.
switch user.SecurityLevel
{
case Basic:
return _container.GetInstance<BasicSecurityToken>();
case SuperUser:
return _container.GetInstance<SuperUserToken>();
}
}
}
एक बार जब हम इन्हें IContainer
साथ पंजीकृत कर लेते हैं:
IContainer.For<SecurityTokenFactory>().Use<SecurityTokenFactory>().Singleton(); //we only need a single instance per app
IContainer.For<IBasicToken>().Use<BasicToken>().PerRequest(); //we need an instance per-request
IContainer.For<ISuperToken>().Use<SuperToken>().PerRequest();//we need an instance per-request
खपत कोड रनटाइम पर सही टोकन प्राप्त करने के लिए इसका उपयोग कर सकता है:
readonly SecurityTokenFactory _tokenFactory;
...
...
public void LogIn(User user)
{
var token = _tokenFactory.GetToken(user);
user.SetSecurityToken(token);
}
इस तरह हम कारखाने द्वारा प्रदान किए गए एनकैप्सुलेशन और आईओसी पुस्तकालय द्वारा प्रदान किए गए जीवनचक्र प्रबंधन से भी लाभान्वित होते हैं।
एक सार कारखाना
निम्नलिखित डिजाइन पैटर्न को एक रचनात्मक पैटर्न के रूप में वर्गीकृत किया गया है।
एक अमूर्त कारखाने का उपयोग संबंधित वस्तुओं के परिवारों को बनाने के लिए एक इंटरफ़ेस प्रदान करने के लिए किया जाता है, बिना ठोस वर्गों को निर्दिष्ट किए और इसका उपयोग प्लेटफ़ॉर्म विशिष्ट वर्गों को छिपाने के लिए किया जा सकता है।
interface Tool {
void use();
}
interface ToolFactory {
Tool create();
}
class GardenTool implements Tool {
@Override
public void use() {
// Do something...
}
}
class GardenToolFactory implements ToolFactory {
@Override
public Tool create() {
// Maybe additional logic to setup...
return new GardenTool();
}
}
class FarmTool implements Tool {
@Override
public void use() {
// Do something...
}
}
class FarmToolFactory implements ToolFactory {
@Override
public Tool create() {
// Maybe additional logic to setup...
return new FarmTool();
}
}
फिर किसी प्रकार के एक आपूर्तिकर्ता / निर्माता का उपयोग किया जाएगा जो कि ऐसी जानकारी को पारित करेगा जो इसे सही प्रकार के कारखाने के कार्यान्वयन को वापस देने की अनुमति देगा:
public final class FactorySupplier {
// The supported types it can give you...
public enum Type {
FARM, GARDEN
};
private FactorySupplier() throws IllegalAccessException {
throw new IllegalAccessException("Cannot be instantiated");
}
public static ToolFactory getFactory(Type type) {
ToolFactory factory = null;
switch (type) {
case FARM:
factory = new FarmToolFactory();
break;
case GARDEN:
factory = new GardenToolFactory();
break;
} // Could potentially add a default case to handle someone passing in null
return factory;
}
}
फैक्टरी विधि (जावा) को लागू करके फैक्टरी उदाहरण
आशय:
एक ऑब्जेक्ट बनाने के लिए एक इंटरफ़ेस को परिभाषित करें, लेकिन उप-वर्गों को यह तय करने दें कि किस क्लास को तुरंत करना है। फैक्ट्री मेथड एक क्लास को सब क्लास में तत्काल डिफरेंशियल की सुविधा देता है।
यूएमएल आरेख:
उत्पाद: यह उन फैक्टर्स के इंटरफ़ेस को परिभाषित करता है जो फ़ैक्टरी विधि बनाती है।
कंक्रीटप्रोडक्ट: इम्प्लीमेंट्स उत्पाद इंटरफ़ेस
निर्माता: फैक्टरी विधि की घोषणा करता है
ConcreateCreator: एक कंक्रीटप्रोडक्ट की आवृत्ति वापस करने के लिए फ़ैक्टरी विधि लागू करता है
समस्या कथन: फ़ैक्टरी विधियों का उपयोग करके खेलों का एक कारखाना बनाएँ, जो खेल इंटरफ़ेस को परिभाषित करता है।
सांकेतिक टुकड़ा:
import java.util.HashMap;
/* Product interface as per UML diagram */
interface Game{
/* createGame is a complex method, which executes a sequence of game steps */
public void createGame();
}
/* ConcreteProduct implementation as per UML diagram */
class Chess implements Game{
public Chess(){
createGame();
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Chess game");
System.out.println("Opponents:2");
System.out.println("Define 64 blocks");
System.out.println("Place 16 pieces for White opponent");
System.out.println("Place 16 pieces for Black opponent");
System.out.println("Start Chess game");
System.out.println("---------------------------------------");
}
}
class Checkers implements Game{
public Checkers(){
createGame();
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Checkers game");
System.out.println("Opponents:2 or 3 or 4 or 6");
System.out.println("For each opponent, place 10 coins");
System.out.println("Start Checkers game");
System.out.println("---------------------------------------");
}
}
class Ludo implements Game{
public Ludo(){
createGame();
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Ludo game");
System.out.println("Opponents:2 or 3 or 4");
System.out.println("For each opponent, place 4 coins");
System.out.println("Create two dices with numbers from 1-6");
System.out.println("Start Ludo game");
System.out.println("---------------------------------------");
}
}
/* Creator interface as per UML diagram */
interface IGameFactory {
public Game getGame(String gameName);
}
/* ConcreteCreator implementation as per UML diagram */
class GameFactory implements IGameFactory {
HashMap<String,Game> games = new HashMap<String,Game>();
/*
Since Game Creation is complex process, we don't want to create game using new operator every time.
Instead we create Game only once and store it in Factory. When client request a specific game,
Game object is returned from Factory instead of creating new Game on the fly, which is time consuming
*/
public GameFactory(){
games.put(Chess.class.getName(),new Chess());
games.put(Checkers.class.getName(),new Checkers());
games.put(Ludo.class.getName(),new Ludo());
}
public Game getGame(String gameName){
return games.get(gameName);
}
}
public class NonStaticFactoryDemo{
public static void main(String args[]){
if ( args.length < 1){
System.out.println("Usage: java FactoryDemo gameName");
return;
}
GameFactory factory = new GameFactory();
Game game = factory.getGame(args[0]);
System.out.println("Game="+game.getClass().getName());
}
}
उत्पादन:
java NonStaticFactoryDemo Chess
---------------------------------------
Create Chess game
Opponents:2
Define 64 blocks
Place 16 pieces for White opponent
Place 16 pieces for Black opponent
Start Chess game
---------------------------------------
---------------------------------------
Create Checkers game
Opponents:2 or 3 or 4 or 6
For each opponent, place 10 coins
Start Checkers game
---------------------------------------
---------------------------------------
Create Ludo game
Opponents:2 or 3 or 4
For each opponent, place 4 coins
Create two dices with numbers from 1-6
Start Ludo game
---------------------------------------
Game=Chess
यह उदाहरण एक FactoryMethod
को लागू करके Factory
वर्ग FactoryMethod
।
Game
सभी प्रकार के गेम्स के लिए इंटरफेस है। यह जटिल विधि को परिभाषित करता है:createGame()
Chess, Ludo, Checkers
गेम के विभिन्न प्रकार हैं, जोcreateGame()
को कार्यान्वयन प्रदान करते हैंcreateGame()
public Game getGame(String gameName)
हैFactoryMethod
मेंIGameFactory
वर्गGameFactory
विभिन्न प्रकार के गेम बनाता है। यहIGameFactory
कारखाने विधि को लागूIGameFactory
।गेम का नाम कमांड लाइन तर्क के रूप में
NotStaticFactoryDemo
getGame
मेंGameFactory
एक गेम नाम को स्वीकार करता है और संबंधितGame
ऑब्जेक्ट देता है।
कब इस्तेमाल करें:
- फैक्टरी : जब आप ग्राहक / कॉलर को ऑब्जेक्ट इंस्टेंटेशन लॉजिक को उजागर नहीं करना चाहते हैं
- सार फैक्टरी : जब आप संबंधित या आश्रित वस्तुओं के परिवारों को उनके ठोस वर्गों को निर्दिष्ट किए बिना इंटरफ़ेस प्रदान करना चाहते हैं
- फैक्ट्री विधि: ऑब्जेक्ट बनाने के लिए एक इंटरफ़ेस को परिभाषित करने के लिए, लेकिन उप-वर्गों को यह तय करने दें कि किस क्लास को तत्काल करना है
अन्य रचनात्मक पैटर्न के साथ तुलना:
डिज़ाइनर फैक्ट्री मेथड (कम जटिल, अधिक अनुकूलन योग्य, उपवर्ग प्रोलिफ़रेट) का उपयोग कर शुरू करते हैं और डिज़ाइनर के रूप में पता चलता है जहाँ अधिक लचीलेपन की आवश्यकता होती है , एब्सट्रैक्ट फ़ैक्टरी, प्रोटोटाइप या बिल्डर (अधिक लचीला, अधिक जटिल) की ओर विकसित होता है।
एब्सट्रैक्ट फैक्ट्री क्लासेस को अक्सर फैक्ट्री मेथड्स के साथ लागू किया जाता है, लेकिन इन्हें प्रोटोटाइप का उपयोग करके भी लागू किया जा सकता है
आगे पढ़ने के लिए संदर्भ: सोर्सिंग डिजाइन-पैटर्न
फ्लाईवेट फैक्टरी (C #)
सरल शब्दों में:
फ्लाईवेट फैक्ट्री जो किसी दिए गए, पहले से ज्ञात के लिए, कुंजी हमेशा प्रतिक्रिया के रूप में एक ही वस्तु देगी। नई कुंजियों के लिए उदाहरण बनाएंगे और उसे वापस करेंगे।
कारखाने का उपयोग:
ISomeFactory<string, object> factory = new FlyweightFactory<string, object>();
var result1 = factory.GetSomeItem("string 1");
var result2 = factory.GetSomeItem("string 2");
var result3 = factory.GetSomeItem("string 1");
//Objects from different keys
bool shouldBeFalse = result1.Equals(result2);
//Objects from same key
bool shouldBeTrue = result1.Equals(result3);
कार्यान्वयन:
public interface ISomeFactory<TKey,TResult> where TResult : new()
{
TResult GetSomeItem(TKey key);
}
public class FlyweightFactory<TKey, TResult> : ISomeFactory<TKey, TResult> where TResult : new()
{
public TResult GetSomeItem(TKey key)
{
TResult result;
if(!Mapping.TryGetValue(key, out result))
{
result = new TResult();
Mapping.Add(key, result);
}
return result;
}
public Dictionary<TKey, TResult> Mapping { get; set; } = new Dictionary<TKey, TResult>();
}
अतिरिक्त नोट्स
मैं इस समाधान को अपने स्वयं के उदाहरण बनाने के बजाय एक IoC Container
(जैसा कि यहां एक अलग उदाहरण में समझाया गया है) का उपयोग करने की सलाह IoC Container
। एक कंटेनर में TResult
लिए एक नया पंजीकरण जोड़कर कर सकता है और फिर इसे से हल कर सकता है (उदाहरण में dictionary
बजाय)।
फैक्टरी विधि
फ़ैक्टरी विधि पैटर्न एक रचनात्मक पैटर्न है जो क्लाइंट कोड को इससे अलग करने के लिए किसी ऑब्जेक्ट के तात्कालिकता को दूर करता है।
जब एक फैक्ट्री विधि एक वर्ग से संबंधित होती है जो किसी अन्य फैक्ट्री पैटर्न जैसे कि एब्सट्रैक्ट फैक्ट्री का कार्यान्वयन होता है तो आमतौर पर फ़ैक्टरी विधि पैटर्न के बजाय उस वर्ग द्वारा लागू पैटर्न को संदर्भित करना अधिक उपयुक्त होता है।
फ़ैक्टरी विधि का पैटर्न आमतौर पर संदर्भित किया जाता है जब एक फैक्ट्री विधि का वर्णन किया जाता है जो एक वर्ग से संबंधित होता है जो मुख्य रूप से एक कारखाना नहीं है।
उदाहरण के लिए, किसी फैक्ट्री विधि को किसी ऐसी वस्तु पर रखना फायदेमंद हो सकता है जो एक डोमेन कॉन्सेप्ट का प्रतिनिधित्व करती हो अगर वह वस्तु किसी राज्य को एनकैप्सुलेट करती है जो किसी अन्य ऑब्जेक्ट के निर्माण की प्रक्रिया को सरल करेगा। एक फैक्ट्री पद्धति से एक डिज़ाइन भी हो सकता है जो विशिष्ट संदर्भ के सर्वव्यापी भाषा के साथ अधिक संरेखित होता है।
यहाँ एक कोड उदाहरण है:
//Without a factory method
Comment comment = new Comment(authorId, postId, "This is a comment");
//With a factory method
Comment comment = post.comment(authorId, "This is a comment");