एकल विधि के साथ कक्षा - सर्वश्रेष्ठ दृष्टिकोण?


172

कहो कि मेरे पास एक वर्ग है जो एक ही कार्य करने के लिए है। कार्य करने के बाद, इसे नष्ट किया जा सकता है। क्या इन तरीकों में से किसी एक को पसंद करने का कोई कारण है?

// Initialize arguments in constructor
MyClass myObject = new MyClass(arg1, arg2, arg3);
myObject.myMethod();

// Pass arguments to method
MyClass myObject = new MyClass();
myObject.myMethod(arg1, arg2, arg3);

// Pass arguments to static method
MyClass.myMethod(arg1, arg2, arg3);

मुझे विवरणों के बारे में जानबूझकर अस्पष्ट बताया जा रहा था, विभिन्न स्थितियों के लिए दिशा-निर्देश प्राप्त करने का प्रयास करने के लिए। लेकिन मुझे वास्तव में Math.random () जैसे सरल पुस्तकालय कार्यों को ध्यान में नहीं था। मैं उन कक्षाओं के बारे में सोच रहा हूं जो कुछ विशिष्ट, जटिल कार्य करते हैं, लेकिन इसे करने के लिए केवल एक (सार्वजनिक) विधि की आवश्यकता होती है।

जवाबों:


264

मैं स्टैटिस्टिक मेथड से भरे यूटीलिटी क्लासेज से प्यार करता था। उन्होंने सहायक विधियों का एक बड़ा समेकन किया जो अन्यथा अतिरेक और रखरखाव नरक का कारण बन जाएगा। वे उपयोग करने के लिए बहुत आसान हैं, कोई तात्कालिकता नहीं, कोई निपटान नहीं, बस फायर'एन'फॉरगेट। मुझे लगता है कि यह एक सेवा उन्मुख वास्तुकला बनाने का मेरा पहला अनजाना प्रयास था - बहुत सारी स्टेटलेस सेवाएं जो सिर्फ अपना काम करती थीं और कुछ नहीं। हालाँकि सिस्टम बढ़ता है, ड्रेगन आ रहे हैं।

बहुरूपता
कहें कि हमारे पास यूटिलिटीक्लास.सोममेथोड है जो खुशी के साथ गूंजता है। अचानक हमें कार्यक्षमता को थोड़ा बदलना होगा। अधिकांश कार्यक्षमता समान है, लेकिन हमें कुछ हिस्सों को फिर भी बदलना होगा। अगर यह एक स्थैतिक विधि नहीं होती, तो हम एक व्युत्पन्न वर्ग बना सकते थे और आवश्यकतानुसार विधि सामग्री को बदल सकते थे। जैसा कि यह एक स्थिर विधि है, हम नहीं कर सकते। ज़रूर, अगर हमें पुरानी पद्धति से पहले या बाद में कार्यक्षमता को जोड़ने की आवश्यकता है, तो हम एक नया वर्ग बना सकते हैं और पुराने को इसके अंदर बुला सकते हैं - लेकिन यह सिर्फ स्थूल है।

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

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

फोस्टर खिलता है
जैसा कि स्थिर तरीकों का आमतौर पर उपयोगिता विधियों और उपयोगिता विधियों के रूप में उपयोग किया जाता है आमतौर पर अलग-अलग उद्देश्य होंगे, हम जल्दी से गैर-सुसंगत कार्यक्षमता से भरे एक बड़े वर्ग के साथ समाप्त हो जाएंगे - आदर्श रूप से, प्रत्येक वर्ग को सिस्टम के भीतर एक ही उद्देश्य होना चाहिए । जब तक उनके उद्देश्यों को अच्छी तरह से परिभाषित किया जाता है तब तक मैं कक्षाओं के पांच गुना अधिक होता हूं।

पैरामीटर रेंगना
शुरू करने के लिए, उस छोटे से प्यारे और निर्दोष स्थिर तरीके से एक एकल पैरामीटर लिया जा सकता है। जैसे-जैसे कार्यक्षमता बढ़ती है, कुछ नए पैरामीटर जोड़े जाते हैं। जल्द ही आगे के पैरामीटर जोड़े जाते हैं जो वैकल्पिक हैं, इसलिए हम विधि के अधिभार (या डिफ़ॉल्ट मान जोड़ें, उन भाषाओं में जो उनका समर्थन करते हैं)। लंबे समय से पहले, हमारे पास एक विधि है जो 10 पैरामीटर लेती है। केवल पहले तीन वास्तव में आवश्यक हैं, पैरामीटर 4-7 वैकल्पिक हैं। लेकिन अगर पैरामीटर 6 निर्दिष्ट किया जाता है, तो 7-9 को भी भरे जाने की आवश्यकता होती है ... क्या हमने एक वर्ग बनाया था जो इस स्थिर विधि को करने के उद्देश्य से किया गया था, हम इसे आवश्यक मापदंडों में ले जाकर हल कर सकते हैं निर्माता, और उपयोगकर्ता को एक ही समय में कई अन्योन्याश्रित मूल्यों को सेट करने के लिए गुणों, या विधियों के माध्यम से वैकल्पिक मान सेट करने की अनुमति देता है। इसके अलावा, यदि कोई विधि जटिलता की इस राशि तक बढ़ गई है,

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

केवल एक सिथ
निश्चित रूप से निरपेक्षता से संबंधित है , मेरे स्थिर तरीकों को नापसंद करने के अपवाद हैं। ट्रू यूटिलिटी क्लासेस जो ब्लोट के लिए कोई जोखिम नहीं उठाती हैं, वे स्टैटिक तरीकों के लिए उत्कृष्ट मामले हैं - System.Convert एक उदाहरण के रूप में। यदि आपकी परियोजना भविष्य के रखरखाव के लिए कोई आवश्यकता नहीं है, तो समग्र वास्तुकला वास्तव में बहुत महत्वपूर्ण नहीं है - स्थिर या गैर स्थिर, वास्तव में कोई फर्क नहीं पड़ता - विकास की गति, हालांकि।

मानकों, मानकों, मानकों!
उदाहरण विधियों का उपयोग करना आपको स्थैतिक तरीकों का उपयोग करने से भी रोकता नहीं है, और इसके विपरीत। जब तक भेदभाव के पीछे तर्क है और यह मानकीकृत है। अलग-अलग कार्यान्वयन विधियों के साथ एक व्यापारिक परत को देखने से अधिक बुरा कुछ नहीं है।


मार्क कहते हैं, बहुरूपता, कॉन्फ़िगर करने 'रणनीति' पैरामीटर के रूप में तरीकों पारित करने में सक्षम किया जा रहा है, और / सेट विकल्प सभी कारणों से एक का उपयोग कर रहे हैं उदाहरण । उदाहरण के लिए: एक ListDelimiter या DelimiterParser को उपयोग करने / स्वीकार करने के लिए सीमांकित के रूप में कॉन्फ़िगर किया जा सकता है, क्या पार्स टोकन से व्हॉट्सएप ट्रिम करना है, क्या सूची को ब्रैकेट करना है, कैसे एक रिक्त / अशक्त सूची का इलाज करना है .. आदि
थॉमस डब्ल्यू

4
"जैसा कि एक प्रणाली बढ़ती है ..." आप रिफ्लेक्टर?
रोडनी गिजेल

3
@ user3667089 सामान्य तर्क है कि वर्ग के लिए तार्किक रूप से मौजूद होने की जरूरत है, जिन्हें मैं कंस्ट्रक्टर में पास करूंगा। ये आमतौर पर ज्यादातर / सभी विधियों में उपयोग किया जाएगा। विधि विशिष्ट तर्क मैं उस विशिष्ट विधि में पास करूँगा।
मार्क एस रासमुसेन

1
वास्तव में आप एक स्थिर वर्ग के साथ जो कर रहे हैं वह कच्चे, गैर-वस्तु-उन्मुख सी (यद्यपि स्मृति-प्रबंधित) में वापस जा रहा है - सभी कार्य एक वैश्विक स्थान पर हैं, कोई नहीं है this, आपको वैश्विक स्थिति का प्रबंधन करना होगा आदि। यदि आप प्रक्रियात्मक प्रोग्रामिंग पसंद करते हैं, तो यह करें। लेकिन सराहना करें कि आप तब ओओ के संरचनात्मक लाभों में से कई को खो देते हैं।
इंजीनियर

1
इन कारणों में से अधिकांश खराब कोड प्रबंधन के साथ करना है, न कि स्थैतिक तरीकों का उपयोग करने से। F # और अन्य कार्यात्मक भाषाओं में हम उन सभी तरीकों का उपयोग करते हैं जो मूल रूप से स्थिर तरीके (बिना उदाहरण के राज्य के साथ कार्य) होते हैं और इनमें ये समस्याएँ नहीं होती हैं। उस ने कहा, F # कार्यों का उपयोग करने के लिए एक बेहतर प्रतिमान प्रदान करता है (फ़ंक्शन प्रथम श्रेणी हैं, फ़ंक्शन को करी और आंशिक रूप से लागू किया जा सकता है) जो उन्हें C # की तुलना में अधिक संभव बनाता है। कक्षाओं का उपयोग करने का एक बड़ा कारण यह है कि C # के लिए क्या बनाया गया है। .NET कोर में सभी डिपेंडेंसी इंजेक्शन का निर्माण डिप्स के साथ इंस्टेंस क्लासेस के चारों ओर घूमता है।
जस्टिन जे स्टार्क

89

मैं स्थिर तरीका पसंद करता हूं। चूँकि कक्षा किसी वस्तु का प्रतिनिधित्व नहीं कर रही है इसलिए इसका कोई उदाहरण नहीं है।

कक्षाएं जो केवल अपने तरीकों के लिए मौजूद हैं, उन्हें स्थिर छोड़ दिया जाना चाहिए।


19
-1 "कक्षाएं जो केवल अपने तरीकों के लिए मौजूद हैं, उन्हें स्थिर छोड़ दिया जाना चाहिए।"
16

8
@Rookian आप इससे असहमत क्यों हैं?
jjnguy

6
एक उदाहरण रिपॉजिटरी पैटर्न होगा। कक्षा में केवल विधियाँ होती हैं। अपने दृष्टिकोण के बाद इंटरफेस का उपयोग करने की अनुमति नहीं देता है। => युग्मन बढ़ाएँ
Rookian

1
@ देखें, आम लोगों को ऐसी कक्षाएं नहीं बनानी चाहिए जो सिर्फ तरीकों के लिए उपयोग की जाती हैं। उदाहरण में आपने स्थैतिक विधियाँ दीं, एक अच्छा विचार नहीं है। लेकिन सरल उपयोगिता विधियों के लिए स्थैतिक तरीका सबसे अच्छा है।
jjnguy

9
बिलकुल सही :) अपने कंफर्टन के साथ "सिंपल यूटिलिटी मेथड्स के लिए" मैं आपसे पूरी तरह सहमत हूँ, लेकिन आपने अपने जवाब में एक सामान्य नियम बनाया है जो कि गलत है।
रूकियन

18

यदि फ़ंक्शन को निष्पादित करने के लिए बनाई गई कक्षा का कोई उदाहरण नहीं है, तो स्थैतिक कार्यान्वयन का उपयोग करें। इस वर्ग के उपभोक्ता एक ऐसा उदाहरण क्यों बनाते हैं जब किसी की आवश्यकता नहीं होती है।


16

यदि आपको ऑब्जेक्ट की स्थिति को बचाने की आवश्यकता नहीं है , तो पहले स्थान पर इसे तत्काल करने की आवश्यकता नहीं है। मैं उस एकल स्थैतिक विधि के साथ जाऊँगा जिसे आप पैरामीटर पास करते हैं।

मैं एक विशाल यूटिल्स वर्ग के खिलाफ भी चेतावनी देता हूं जिसमें दर्जनों असंबंधित स्थिर तरीके हैं। यह जल्दी में अव्यवस्थित और अनिर्दिष्ट हो सकता है। कई वर्गों के लिए बेहतर है, प्रत्येक कुछ, संबंधित विधियों के साथ।


6

मैं कहूंगा कि स्टेटिक मेथड फॉर्मेट बेहतर विकल्प होगा। और मैं कक्षा को भी स्थिर बनाऊंगा, इस तरह से आपको गलती से कक्षा का एक उदाहरण बनाने की चिंता नहीं होगी।


5

मैं वास्तव में नहीं जानता कि स्थिति क्या है, लेकिन मैं इसे एक वर्ग में एक विधि के रूप में डालूंगा जो arg1, arg2 या arg3 से संबंधित है - यदि आप शब्दार्थ यह कह सकते हैं कि उन वर्गों में से एक स्वयं का होगा। तरीका।


4

मेरा सुझाव है कि प्रदान की गई जानकारी के आधार पर इसका उत्तर देना कठिन है।

मेरी आंत यह है कि यदि आप सिर्फ एक विधि के लिए जा रहे हैं, और यह कि आप तुरंत कक्षा को फेंकने जा रहे हैं, तो इसे एक स्थिर वर्ग बनाएं जो सभी मापदंडों को लेता है।

निश्चित रूप से, यह बताने में मुश्किल है कि आपको इस एक विधि के लिए एक एकल वर्ग बनाने की आवश्यकता क्यों है। क्या यह विशिष्ट "उपयोगिताएँ वर्ग" की स्थिति है जैसा कि अधिकांश लोग मान रहे हैं? या आप किसी प्रकार के नियम वर्ग को लागू कर रहे हैं, जिनमें से भविष्य में अधिक हो सकता है।

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


3

क्या आपकी कक्षा को स्थिर बनाया जा सकता है?

यदि ऐसा है, तो मैं इसे एक 'यूटिलिटीज' श्रेणी बनाऊंगा जिसे मैं अपने सभी एक-फंक्शन कक्षाओं में डालूँगा।


2
मैं कुछ हद तक असहमत हूं। जिन परियोजनाओं में मैं शामिल हूं, आपकी उपयोगिताएँ वर्ग में सैकड़ों असंबंधित तरीके हो सकते हैं।
पॉल टॉम्बलिन

3
@Paul: आम तौर पर सहमत। मैं पूरी तरह से असंबंधित तरीकों के दर्जनों (या हां, सैकड़ों) के साथ कक्षाएं देखने से नफरत करता हूं। यदि किसी को इस दृष्टिकोण को अपनाना चाहिए, तो कम से कम इन्हें उपयोगिताओं के छोटे, संबंधित सेटों में विभाजित करें (जैसे, फ़ू यूटिलिटीज़, बार यूटिलिटीज़, आदि)।
जॉन रूडी

9
एक वर्ग- एक प्रतिक्रिया।
एंड्रियास पीटरसन

3

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


3

सरल अनुप्रयोगों और internalसहायकों के लिए, मैं एक स्थिर पद्धति का उपयोग करूंगा। घटकों के साथ अनुप्रयोगों के लिए, मैं प्रबंधित एक्स्टेंसिबिलिटी फ्रेमवर्क से प्यार कर रहा हूं । यहाँ एक दस्तावेज़ से एक अंश है जो मैं अपने एपीआई में आपको मिलने वाले पैटर्न का वर्णन करने के लिए लिख रहा हूँ।

  • सेवाएं
    • एक I[ServiceName]Serviceइंटरफेस द्वारा परिभाषित ।
    • निर्यात और इंटरफ़ेस प्रकार द्वारा आयातित।
    • एकल कार्यान्वयन होस्ट एप्लिकेशन द्वारा प्रदान किया जाता है और आंतरिक रूप से और / या एक्सटेंशन द्वारा खपत किया जाता है।
    • सेवा इंटरफ़ेस पर विधियाँ थ्रेड-सुरक्षित हैं।

एक आकस्मिक उदाहरण के रूप में:

public interface ISettingsService
{
    string ReadSetting(string name);

    void WriteSetting(string name, string value);
}

[Export]
public class ObjectRequiringSettings
{
    [Import]
    private ISettingsService SettingsService
    {
        get;
        set;
    }

    private void Foo()
    {
        if (SettingsService.ReadSetting("PerformFooAction") == bool.TrueString)
        {
            // whatever
        }
    }
}

2

मैं बस कंस्ट्रक्टर में सब कुछ करूंगा। इस तरह:

new MyClass(arg1, arg2, arg3);// the constructor does everything.

या

MyClass my_object(arg1, arg2, arg3);

यदि आपको MyClass के ऑब्जेक्ट के अलावा कुछ भी वापस करने की आवश्यकता है तो क्या होगा?
छिपकली का बिल

13
मैं एक निर्माता में निष्पादन तर्क रखने के लिए इसे बुरा अभ्यास मानता हूं। अच्छा विकास शब्दार्थ के बारे में है। शब्दार्थ, एक कंस्ट्रक्टर किसी वस्तु का निर्माण करने के लिए मौजूद होता है, न कि अन्य कार्यों को करने के लिए।
mstrobl

बिल, यदि आपको रिटर्न वैल्यू की आवश्यकता है, तो रिट ववल्यू का संदर्भ दें: MyClass myObject (arg1, arg2, arg3, retvalue); mstrobl, यदि ऑब्जेक्ट की आवश्यकता नहीं है, तो इसे क्यों बनाएं? और यह ट्रिक वास्तविक रूप से कुछ उदाहरणों में आपकी मदद कर सकती है।

यदि किसी वस्तु की कोई स्थिति नहीं है, अर्थात कोई संस्करण नहीं है, तो यह उकसाने से कोई आवंटन नहीं होगा - न तो ढेर पर और न ही स्टैक। आप C ++ में ऑब्जेक्ट्स के बारे में सोच सकते हैं जैसे कि C फंक्शन किसी स्ट्रक्चर की ओर इशारा करते हुए छिपे हुए पहले पैरामीटर के साथ कॉल करता है।
mstrobl

mstrobl, यह स्टेरॉयड पर एसी फ़ंक्शन की तरह है क्योंकि आप निजी कार्यों को परिभाषित कर सकते हैं जो कि ctor उपयोग कर सकते हैं। निजी कार्यों में डेटा पास करने के लिए आप कक्षा सदस्य चर का भी उपयोग कर सकते हैं।

0

विचार करने के लिए एक और महत्वपूर्ण मुद्दा यह है कि क्या सिस्टम एक बहुआयामी वातावरण में चल रहा होगा, और क्या यह एक स्थिर विधि या चर के लिए थ्रेड-सुरक्षित होगा ...

आपको सिस्टम की स्थिति पर ध्यान देना चाहिए।


0

आप सभी एक साथ स्थिति से बचने में सक्षम हो सकते हैं। रिफ्लेक्टर की कोशिश करें ताकि आप प्राप्त करेंarg1.myMethod1(arg2, arg3) । यदि यह अधिक समझ में आता है तो arg2 या arg3 के साथ swg arg1।

यदि आपके पास arg1 की कक्षा पर नियंत्रण नहीं है, तो इसे सजाने के लिए:

class Arg1Decorator
    private final T1 arg1;
    public Arg1Decorator(T1 arg1) {
        this.arg1 = arg1;
    }
    public T myMethod(T2 arg2, T3 arg3) {
        ...
    }
 }

 arg1d = new Arg1Decorator(arg1)
 arg1d.myMethod(arg2, arg3)

तर्क यह है कि, OOP में, डेटा और विधियाँ संसाधन हैं जो डेटा एक साथ हैं। इसके अलावा, आपको मार्क द्वारा उल्लिखित सभी फायदे मिलते हैं।


0

मुझे लगता है, अगर आपकी कक्षा के गुणों या वर्ग के उदाहरण का उपयोग कंस्ट्रक्टरों या आपके तरीकों में नहीं किया जाएगा, तो विधियों को 'स्थिर' पैटर्न की तरह डिज़ाइन किए जाने का सुझाव नहीं दिया गया है। स्थैतिक विधि को हमेशा 'मदद' के तरीके से सोचना चाहिए।


0

इस पर निर्भर करता है कि आप केवल कुछ करना चाहते हैं या करते हैं और कुछ ऐसा कर सकते हैं जो आप कर सकते हैं:

public abstract class DoSomethingClass<T>
{
    protected abstract void doSomething(T arg1, T arg2, T arg3);
}

public abstract class ReturnSomethingClass<T, V>
{
    public T value;
    protected abstract void returnSomething(V arg1, V arg2, V arg3);
}

public class DoSomethingInt extends DoSomethingClass<Integer>
{
    public DoSomethingInt(int arg1, int arg2, int arg3)
    {
        doSomething(arg1, arg2, arg3);
    }

    @Override
    protected void doSomething(Integer arg1, Integer arg2, Integer arg3)
    {
        // ...
    }
}

public class ReturnSomethingString extends ReturnSomethingClass<String, Integer>
{
    public ReturnSomethingString(int arg1, int arg2, int arg3)
    {
        returnSomething(arg1, arg2, arg3);
    }

    @Override
    protected void returnSomething(Integer arg1, Integer arg2, Integer arg3)
    {
        String retValue;
        // ...
        value = retValue;
    }
}

public class MainClass
{
    static void main(String[] args)
    {
        int a = 3, b = 4, c = 5;

        Object dummy = new DoSomethingInt(a,b,c);  // doSomething was called, dummy is still around though
        String myReturn = (new ReturnSomethingString(a,b,c)).value; // returnSomething was called and immediately destroyed
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.