2011-09-28 14 views
7
में खुद का एक उदाहरण बनाता

आप एक वर्ग समारोह है कि एक वर्ग का एक उदाहरण बनाता मिल सकता है , वह फ़ंक्शन कोड कैसा दिखता है? क्या आप टॉब्जेक्ट की न्यू इंस्टेंस विधि का उपयोग करते हैं और क्या आप हर व्युत्पन्न कक्षा के लिए हर बार फिर से लागू करते हैं? क्या यह संरक्षक का उपयोग करने के लिए सुरक्षित/बेहतर है?कक्षा समारोह है कि डेल्फी

लक्ष्य एक आदेश पैटर्न में इस दृष्टिकोण का उपयोग और वर्ग प्रकार के साथ आदेश सूची और एक रिसीवर जैसे लोड करने के लिए है:

//FYI: document is an instance of TDocument 
commandList.Execute(TOpenDocument(document)); 
commandList.Execute(TPasteFromClipboard(document)); 
//... lots of actions - some can undo 
commandList.Execute(TPrintDocument(document)); 
commandList.Execute(TSaveDocument(document)); 

और इस का कारण यह है कि है कुछ आदेश पाठ के माध्यम से निर्दिष्ट किया जाएगा/स्क्रिप्ट और रनटाइम पर हल करने की आवश्यकता होगी।

उत्तर

2

क्या आपके पास कक्षा वर्ग हो सकता है जो कक्षा का उदाहरण बनाता है।
क्या यह कन्स्ट्रक्टर का उपयोग करने के लिए सुरक्षित/बेहतर है?

कन्स्ट्रक्टर एक वर्ग कार्य है जो कक्षा का एक उदाहरण बनाता है। बस डालें:

constructor New(); virtual; 

और आप जाने के लिए अच्छे हैं।

virtual; भाग आपको सभी वंश वर्गों के लिए एक ही नया() कन्स्ट्रक्टर कॉल करने देगा।

+0

निर्माण के बजाय निर्माता को नया कॉल करने का कोई कारण नहीं है, और इसके लिए वर्चुअल होने का कोई कारण नहीं है। –

+0

@ रोब: कक्षा चर के माध्यम से निर्माण करते समय कन्स्ट्रक्टर ओवरराइड का एक कारण है। मैं मानता हूं कि बनाएं, अब तक एक बेहतर नाम है; यह नया() यहां सिर्फ उस प्रश्न से संबंधित है जहां नया() कक्षा कार्य के नाम के रूप में दिया जाता है। – dmajkic

+0

यह सच है कि वर्चुअल कन्स्ट्रक्टर केवल तब उपयोगी होते हैं जब कक्षा-संदर्भ चर का उपयोग किया जाता है, लेकिन यह * सत्य नहीं है कि वर्चुअल कन्स्ट्रक्टर * आवश्यक * हैं। आपको केवल आभासी रचनाकारों की आवश्यकता है यदि आपको * उन्हें वंश वर्गों में * ओवरराइड करने की आवश्यकता है; यह आभासी तरीकों के किसी भी अन्य उपयोग से अलग नहीं है। इस सवाल से कोई संकेत नहीं दिया गया कि वंशजों को अपनी बेस क्लास से अलग निर्माण व्यवहार की आवश्यकता होगी (एक अलग वर्ग बनाने के अलावा, लेकिन यह स्वचालित है)। –

12

जो आप खोज रहे हैं उसे फैक्टरी पैटर्न कहा जाता है। यह डेल्फी में किया जा सकता है; इस तरह वीसीएल अन्य चीजों के साथ रूपों को deserializes। आप जो खो रहे हैं वह सिस्टम का पंजीकरण/लुकअप हिस्सा है। यहां मूल विचार है:

  • कहीं, आप पंजीकरण तालिका सेट अप करते हैं। यदि आप डेल्फी एक्सई पर हैं, तो आप इसे TDictionary<string, TMyClassType> के रूप में कार्यान्वित कर सकते हैं, जहां TMyClassType को class of TMyClass के रूप में परिभाषित किया गया है। यह महत्वपूर्ण है। आपको कक्षा के नाम और कक्षा प्रकार संदर्भों के बीच एक मानचित्र की आवश्यकता है।
  • TMyClass पर वर्चुअल कंस्ट्रक्टर रखें। फैक्ट्री पैटर्न इसे बनाता है, जब से जो कुछ भी उतरता है, वह इस कन्स्ट्रक्टर या इसके ओवरराइड का उपयोग करेगा।
  • जब आप कोई नया वंशज बनाते हैं, तो उसे एक विधि कॉल करें जो पंजीकरण तालिका के साथ स्वयं पंजीकृत होगी। यह प्रोग्राम स्टार्टअप पर होना चाहिए, या तो प्रारंभिक या कक्षा कन्स्ट्रक्टर में होना चाहिए।

जब आप एक स्क्रिप्ट से कुछ का दृष्टांत की जरूरत है, इस तरह यह कार्य करें:

class function TMyClass.New(clsname: string; [other params]): TMyClass; 
begin 
    result := RegistrationTable[clsName].Create(other params); 
end; 

आप वर्ग के नाम से कक्षा संदर्भ मिलता है, और पर आभासी निर्माता कॉल करने के लिए पंजीकरण तालिका का उपयोग सही प्रकार के ऑब्जेक्ट को प्राप्त करने के लिए क्लास संदर्भ।

+1

उत्कृष्ट स्पष्टीकरण – RBA

+0

वाह। एक साधारण समस्या overengineering के बारे में बात करो! –

+1

@Rob: हुह? यह सबसे आसान है कि आप "मूल प्रकार से प्राप्त ऑब्जेक्ट्स बना सकते हैं जिसका वास्तविक प्रकार स्क्रिप्ट से आता है" और यह काम करता है। –

0

आपको एक कन्स्ट्रक्टर (क्लास फ़ंक्शन का एक विशेष "प्रकार") का उपयोग करना चाहिए। TObject.NewInstance एक उपयुक्त विकल्प नहीं है, जब तक कि आपको विशेष स्मृति आवंटन की आवश्यकता न हो।

और कमांड सूची के निष्पादन दिनचर्या के संबंध में: अब शामिल क्रिया वस्तु के प्रकार पर निर्भर करती है। कल्पना करें कि एक दस्तावेज़ एक ही समय में खोलने, मुद्रित करने, पेस्ट करने और सहेजने में सक्षम है (अजीब धारणा नहीं), जो इस संरचना में लागू करना मुश्किल होगा। इसके बजाय, इंटरफेस (IOpenDocument, IPasteFromClipboard, IPrintable, ISaveDocument) जोड़ने पर विचार करें जो वास्तव में सभी एक दस्तावेज़ उदाहरण के क्रियाएं हो सकता है।

5

हाँ, यह तकनीकी रूप से संभव है एक वर्ग विधि से एक उदाहरण बनाने के लिए, बस वास्तविक निर्माता फोन और फिर वापस जाने के उदाहरण यह बनाता है, जैसे:

type 
    TMyClass = class(TSomeParent) 
    public 
    constructor Create(AValue : Integer); virtual; 
    class function New(AValue : integer) : TMyClass; 
    end; 

    TDerivedClass = class(TMyClass) 
    public 
    constructor Create(AValue : Integer); override; 
    function Beep; 
    end; 

constructor TMyClass.Create(AValue : Integer); 
begin 
    inherited Create; 
    ... 
end; 

function TMyClass.New(AValue : integer) : TMyClass; 
begin 
    Result := Create(AValue); 
end; 

constructor TDerivedClass.Create(AValue : Integer); 
begin 
    inherited Create(AValue); 
    ... 
end; 

var 
    myList : TList<TMyClass>; 
    item : TMyClass; 
begin 
    myList.Add(TDerivedClass.New(1)) 
    myList.Add(TDerivedClass.New(3)) 
    myList.Add(TDerivedClass.New(5)) 
    for item in myList do 
    TDerivedClass(item).Beep; 

जो मामले में, तुम सिर्फ बेहतर कर रहे हैं सीधे निर्माता का उपयोग कर:

type 
    TMyClass = class(TSomeParent) 
    end; 

    TDerivedClass = class(TMyClass) 
    public 
    constructor Create(AValue : Integer); 
    function Beep; 
    end; 

var 
    myList : TList<TDerivedClass>; 
    item : TDerivedClass; 
begin 
    myList.Add(TDerivedClass.Create(1)) 
    myList.Add(TDerivedClass.Create(3)) 
    myList.Add(TDerivedClass.Create(5)) 
    for item in myList do 
    item.Beep; 
1

आरटीटीआई का उपयोग करने का एक और विकल्प है। नीचे दिए गए कोड वस्तु के एक सबसेट के साथ ऑब्जेक्ट का एक नया उदाहरण प्राप्त करने के तरीके के रूप में मेरी कक्षा में एक सामान्य विधि के रूप में चलता है, लेकिन आइटम (सूची वस्तु के साथ ही) के रूप में शायद मूल वस्तुएं हैं, उदाहरण का एक उदाहरण बनाना जिस वस्तु में विधि परिभाषित की गई है वह पर्याप्त नहीं है क्योंकि इसे उसी प्रकार के उदाहरण की आवश्यकता है।

यानी

TParentItem = Class 
End; 

TParentList = Class 
    Items : TList<TParentItem>; 
    Function GetSubRange(nStart,nEnd : Integer) : TParentList; 
End; 

TChildItem = Class(TParentItem) 
end 

TChildList = Class(TParentList) 
end 

List := TChildList.Create; 
List.LoadData; 

SubList := List.GetSubRange(1,3); 

कार्यान्वयन अगर GetSubRange कुछ जैसा होगा ...

Function TParentList.GetSubRange(nStart,nEnd : Integer) : TParentList; 
var 
    aContext: TRttiContext; 
    aType: TRttiType; 
    aInsType : TRttiInstanceType; 
    sDebug : String; 
begin 
    aContext := TRttiContext.Create; 
    aType := aContext.GetType(self.ClassType); 
    aInsType := aType.AsInstance; 
    Result := aInsType.GetMethod('Create').Invoke(aInsType.MetaclassType,[]).AsType<TParentList>; 
    sDebug := Result.ClassName; // Should be TChildList 

    // Add the items from the list that make up the subrange. 
End; 

मैं कुछ चीजें इसे थोड़ा OTT हो सकता है के लिए सराहना करते हैं, लेकिन इसके बाद के संस्करण के डिजाइन में, यह काम करता है और एक और विकल्प है, हालांकि मैं सराहना करता हूं, इसकी कक्षा विधि नहीं है।

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