2010-01-11 25 views
7

मैं संदेश ऑब्जेक्ट्स के एक सेट से निपट रहा हूं, जिनमें से प्रत्येक के अनुरूप एक अद्वितीय पहचानकर्ता है। प्रत्येक संदेश या तो मानचित्र से या बाइटबफर से बनाया जा सकता है (संदेश द्विआधारी हैं, लेकिन हम जानते हैं कि बाइनरी प्रतिनिधित्व से कैसे और कैसे स्थानांतरित करना है)।जावा - स्थैतिक फैक्ट्री विधि और स्विच स्टेटमेंट

public static Message fromMap(int uuid, Map<String, Object> fields) { 
    switch (uuid) { 
     case FIRST_MESSAGE_ID: 
     return new FirstMessage(fields); 
     . 
     . 
     . 
     default: 
      // Error 
      return null; 
    } 
} 

public static Message fromByteBuffer(int uuid, ByteBuffer buffer) { 
    switch (uuid) { 
     case FIRST_MESSAGE_ID: 
     return new FirstMessage(buffer); 
     . 
     . 
     . 
     default: 
      // Error 
      return null; 
    } 
} 

अब, आइटम 1 के बारे में बात करती है Josh Bloch's Effective Java: इस प्रकार

इन संदेशों के निर्माण के लिए वर्तमान कार्यान्वयन मोटे तौर पर है पर विचार करें कंस्ट्रक्टर्स के बजाय आंकड़े कारखाने विधियों, और इस जहां इस पद्धति है एक जगह हो रहा है उपयोगी (ग्राहक सीधे संदेश उपप्रकारों के रचनाकारों तक नहीं पहुंचते हैं, इसके बजाय वे इस विधि से गुज़रते हैं)। लेकिन मुझे इस तथ्य को पसंद नहीं है कि हमें दो स्विच स्टेटमेंट्स को अपडेट रखना याद रखना है (DRY सिद्धांत का उल्लंघन करता है)।

मैं इसे पूरा करने के सर्वोत्तम तरीके में किसी भी अंतर्दृष्टि की सराहना करता हूं; हम ऑब्जेक्ट्स कैशिंग नहीं कर रहे हैं (प्रत्येक कॉल से मैप या सेबटेबफर एक नई ऑब्जेक्ट लौटाएगा), जो इस तरह की एक स्थिर फैक्ट्री विधि का उपयोग करने के कुछ लाभों को अस्वीकार करता है। इस कोड के बारे में कुछ मुझे गलत मानता है, इसलिए मुझे समुदाय के विचारों को सुनना अच्छा लगेगा कि क्या यह नई वस्तुओं का निर्माण करने का एक वैध तरीका है, या यदि कोई बेहतर समाधान नहीं होगा।

उत्तर

12

शायद तुम एक अंतरफलक MessageFactory और इसका कार्यान्वयन बना सकते हैं:

public interface MessageFactory { 
    Message createMessage(Map<String, Object> fields); 
    Message createMessage(ByteBuffer buffer); 
} 

public class FirstMessageFactory implements MessageFactory { 
    public Message createMessage(Map<String, Object> fields){ 
    return new FirstMessage(fields); 
    } 

    public Message createMessage(ByteBuffer buffer){ 
    return new FirstMessage(buffer); 
    } 

} 

अगले, ऊपर तरीकों के रूप में एक ही कक्षा में एक विधि getFactoryFromId:

public static MessageFactory getMessageFactoryFromId(int uuid){ 
switch (uuid) { 
    case FIRST_MESSAGE_ID: 
    return new FirstMessageFactory(); 
    ... 
    default: 
     // Error 
     return null; 
    } 
} 

हालांकि, बजाय इस बात का, आईड्स और कारखानों वाले हैशमैप को बनाना बेहतर है, इसलिए जब भी आप कोई संदेश बना रहे हों तो आपको एक नई फैक्टरी ऑब्जेक्ट बनाने की ज़रूरत नहीं है। comment below भी देखें।

और अपने तरीके:

public static Message fromMap(int uuid, Map<String, Object> fields) { 
    getMessageFactoryFromId(uuid).createMessage(fields); 
} 

public static Message fromByteBuffer(int uuid, ByteBuffer buffer) { 
    getMessageFactoryFromId(uuid).createMessage(buffer); 
} 

इस तरह, आप कारखाने पद्धति का उपयोग कर रहे हैं, और दो बार एक ही स्विच बयान के लिए कोई जरूरत नहीं है।

(यह परीक्षण नहीं किया, इसलिए संभवतः कुछ संकलन त्रुटियों/लिखने की त्रुटियों)

+0

यदि कारखाने की वस्तुओं को मानचित्र में संग्रहीत किया जाता है और यूयूआईडी द्वारा पुनर्प्राप्त किया जाता है, तो कोई स्विच की आवश्यकता नहीं होती है। – rsp

+0

yup, मैंने यही कहा है :-) मैंने आपके उत्तर में एक लिंक भी जोड़ा है :) – Fortega

+0

यह एक अच्छा समाधान है (इसलिए इसके लिए +1) लेकिन यदि लक्ष्य डबल स्विच से छुटकारा पाना था तो आप बस अनुमान लगा सकते थे स्विच तर्क को एक अलग विधि से बाहर करें। (नक्शा मूल रूप से भेस में एक स्विच है) – wds

0

आप AbstractFactory पैटर्न, जहां प्रत्येक संदेश प्रकार के लिए एक कारखाने वर्ग के लिए होता है, कि आप संदेश देता है या तो द्वारा इस्तेमाल कर सकते हैं बफर या नक्शा। फिर आप एक विधि बनाते हैं जो आपको संबंधित फैक्ट्री ऑब्जेक्ट देता है। लौटे कारखाने से आप संदेश बनाते हैं।

1

क्या बाइटबफर को मानचित्र या किसी अन्य चीज़ में बदलने का कोई तरीका है? यह अच्छा होगा अगर आप इनपुट को सामान्यीकृत रूप में रूपांतरित करते हैं और एक अनूठा स्विच लागू करते हैं।

यदि आप क्या करना चाहते हैं तो यह एक संदेश प्राप्त कर रहा है और इसे विशिष्ट मानों के साथ स्वरूपित कर रहा है (जैसे "तालिका: तालिका नाम में कॉलम नाम नहीं है: colName") आप बाइटबफर को मानचित्र पर परिवर्तित कर सकते हैं और पहले कॉल कर सकते हैं तरीका। यदि आपको एक नया संदेश चाहिए तो क्या आप केवल सेमैप विधि का विस्तार करते हैं।

यह सामान्य भाग को फैक्टर करने जैसा कुछ है।

3

आप अपने वस्तुओं एक इंटरफ़ेस को लागू की तरह कारखाने तरीकों घोषित करता है, तो:

Map map = new HashMap(); 

map.put(Integer.valueOf(FirstMessage.UUID), new FirstMessage.Factory()); 
: एक स्थिर नेस्टेड वर्ग में

public Message newInstance(Map<String, Object> fields); 
public Message newInstance(ByteBuffer buffer); 

, अपने कारखाने एक Map युक्त कारखाने वस्तुओं UUID द्वारा अनुक्रमित बना सकते हैं

और मानचित्र लुकअप द्वारा अपने स्विच को प्रतिस्थापित करें:

public static Message fromMap(int uuid, Map<String, Object> fields) { 

    Factory fact = map.get(Integer.valueOf(uuid)); 

    return (null == fact) ? null : fact.newInstance(fields); 
} 

public static Message fromByteBuffer(int uuid, ByteBuffer buffer) { 

    Factory fact = map.get(Integer.valueOf(uuid)); 

    return (null == fact) ? null : fact.newInstance(buffer); 
} 

इसे अन्य निर्माण विधियों का समर्थन करने के लिए आसानी से बढ़ाया जा सकता है।

+0

बस एक छोटी टिप्पणी: आपको नए इंटीजर() के बजाय Integer.valueOf() का उपयोग करना चाहिए। – I82Much

+0

अच्छा बिंदु, बदल गया। – rsp

1

मैं ऐसे निम्न उदाहरण के रूप में सार विधियों, के साथ एक enum प्रकार का उपयोग की सलाह देते हैं:

enum MessageType { 

    FIRST_TYPE(FIRST_MESSAGE_ID) { 

     @Override 
     Message fromByteBuffer(ByteBuffer buffer) { 
      return new FirstMessage(buffer); 
     } 

     @Override 
     Message fromMap(Map<String, Object> fields) { 
      return new FirstMessage(fields); 
     } 

     @Override 
     boolean appliesTo(int uuid) { 
      return this.uuid == uuid; 
     } 

    }, 

    SECOND_TYPE(SECOND_MESSAGE_ID) { 

     @Override 
     Message fromByteBuffer(ByteBuffer buffer) { 
      return new SecondMessage(buffer); 
     } 

     @Override 
     Message fromMap(Map<String, Object> fields) { 
      return new SecondMessage(fields); 
     } 

     @Override 
     boolean appliesTo(int uuid) { 
      return this.uuid == uuid; 
     } 

    }; 

    protected final int uuid; 

    MessageType(int uuid) { 
     this.uuid = uuid; 
    } 

    abstract boolean appliesTo(int uuid); 

    abstract Message fromMap(Map<String, Object> map); 

    abstract Message fromByteBuffer(ByteBuffer buffer); 

} 

इस तरह, आपके मौजूदा स्थिर तरीकों में आप बस यह कर सकते हैं ...

public static Message fromByteBuffer(int uuid, ByteBuffer buffer) { 
    Message rslt = null; 
    for (MessageType y : MessageType.values()) { 
     if (y.appliesTo(uuid)) { 
      rslt = y.fromByteBuffer(buffer); 
      break; 
     } 
    } 
    return rslt; 
} 

यह दृष्टिकोण आपकी स्थाई विधि को आपके द्वारा समर्थित संदेश प्रकारों और उन्हें बनाने के बारे में जानने से बचाता है - आप स्थैतिक तरीकों को दोबारा किए बिना संदेशों को जोड़, संशोधित या हटा सकते हैं।

0

आप चाहिए सार अपने FirstMessage वस्तु:

public abstract Message { 
    // ... 
} 

फिर उन्हें अपने कारखाने में कैश (के रूप में एक स्विच के खिलाफ):

private static final Map<Integer, Class<Message>> MESSAGES = new HashMap<Integer, Class<Message>>(); 
static { 
    MESSAGES.put(1, FirstMessage.class); 
} 

अपने कारखाने विधि में:

public static Message fromMap(UUID uuid, Map<String, Object> fields) { 
    return MESSAGES.get(uuid).newInstance(); 
} 

वैसे भी यह एक विचार है, आपको पास करने के लिए कुछ प्रतिबिंब (कन्स्ट्रक्टर प्राप्त करें) काम करना होगा खेत।

0

आप संदेश को संशोधित कर सकते हैं ताकि इसमें दो प्रारंभिक विधियां हों, एक मानचित्र के लिए और एक बाइटबफर के लिए (दो रचनाकार संस्करणों के बजाय)। फिर आपकी फैक्ट्री विधि निर्मित (लेकिन अनियमित) संदेश लौटाती है, फिर आप लौटे ऑब्जेक्ट पर मानचित्र या बाइटबफर के साथ प्रारंभ करते हैं।

private static Message createMessage(int uuid) { 
    switch (uuid) { 
     case FIRST_MESSAGE_ID: 
     return new FirstMessage(); 
     . 
     . 
     . 
     default: 
      // Error 
      return null; 
    } 

} 

और उसके बाद सार्वजनिक कारखाने तरीकों बन: - -:

public static Message fromMap(int uuid, Map<String, Object> fields) { 
    Message message = createMessage(uuid); 
    // TODO: null checking etc.... 
    return message.initialize(fields); 
} 

और

public static Message fromByteBuffer(int uuid, ByteBuffer buffer) { 
    Message message = createMessage(uuid); 
    // TODO: null checking etc.... 
    return message.initialize(buffer); 
} 
3

tem

तो अब इस तरह एक कारखाने विधि है 1: इसके बजाए स्थिर कारखाने के तरीकों पर विचार करें रचनाकार

आप उस फैक्ट्री विधि के पीछे निर्माता को छुपाकर पहले से ही ऐसा कर रहे हैं, इसलिए यहां कोई अन्य कारखाना विधि जोड़ने की आवश्यकता नहीं है।

तो आप इसे फैक्टरी इंटरफ़ेस और मानचित्र के साथ कर सकते हैं।(मूल रूप से क्या हर किसी को पहले से ही कह रहा है, लेकिन अंतर के साथ, आप आंतरिक वर्गों का उपयोग कारखानों इनलाइन सकते हैं कि)

interface MessageFactory { 
    public Message createWithMap(Map<String,Object> fields); 
    public Message createWithBuffer(ByteBuffer buffer); 
} 

Map<MessageFactory> factoriesMap = new HashMap<MessageFactory>() {{ 
    put(FIRST_UUID, new MessageFactory() { 
     public Message createWithMap(Map<String, Object> fields) { 
      return new FirstMessage(fields); 
     } 
     public Message createWithBuffer(ByteBuffer buffer){ 
      return new FirstMessage(buffer); 
     } 
    }); 
    put(SECOND_UUID, new MessageFactory(){ 
     public Message createWithMap(Map<String, Object> fields) { 
      return new SecondMessage(fields); 
     } 
     public Message createWithBuffer(ByteBuffer buffer){ 
      return new SecondMessage(buffer); 
     } 
    }); 
    put(THIRD_UUID, new MessageFactory(){ 
     public Message createWithMap(Map<String, Object> fields) { 
      return new ThirdMessage(fields); 
     } 
     public Message createWithBuffer(ByteBuffer buffer){ 
      return new ThirdMessage(buffer); 
     } 
    }); 
    ... 
}}; 

और अपने आमंत्रण बदल जाएगी में:

public static Message fromMap(int uuid, Map<String, Object> fields) { 
    return YourClassName.factoriesMap.get(uuid).createWithMap(fields); 
} 

public static Message fromByteBuffer(int uuid, ByteBuffer buffer) { 
    return YourClassName.factoriesMap.get(uuid).createWithBuffer(buffer); 
} 

क्योंकि के लिए इस्तेमाल किया UUID स्विच कारखानों के लिए कुंजी के रूप में प्रयोग किया जाता है।

0

एक रणनीति के रूप में enum का उपयोग करने के समाधान के बारे में समाधान (एक enum के लिए रणनीति विधियां जोड़ें) स्वच्छ कोड चीट शीट ऐप कहता है कि यह एक रखरखाव हत्यारा है।
हालांकि मुझे नहीं पता कि मैं इसे आपके साथ क्यों साझा करना चाहता हूं।

+0

दिलचस्प। लेकिन कृपया उत्तर की बजाय टिप्पणियों के रूप में टिप्पणियां जोड़ें। – I82Much

संबंधित मुद्दे