'स्थिरांक' साझा करने के लिए जावा में स्थिर क्षेत्रों के साथ इंटरफेस


116

मैं जावा में जाने के लिए कुछ खुले स्रोत जावा परियोजनाओं को देख रहा हूं और उनमें से बहुत से नोटिस करते हैं कि उनमें कुछ प्रकार के 'स्थिरांक' इंटरफ़ेस हैं।

उदाहरण के लिए, प्रोसेसिंग.ऑर्ग में एक इंटरफ़ेस है जिसका नाम PConstants.java है , और अधिकांश अन्य मुख्य वर्ग इस इंटरफ़ेस को लागू करते हैं। इंटरफ़ेस स्थैतिक सदस्यों से भरा हुआ है। क्या इस दृष्टिकोण का कोई कारण है, या इसे बुरा व्यवहार माना जाता है? जहां यह समझ में आता है , या एक स्थिर वर्ग का उपयोग क्यों नहीं करते ?

मुझे कुछ प्रकार के छद्म 'वैश्विक चर' के लिए अनुमति देने के लिए एक इंटरफ़ेस का उपयोग करना अजीब लगता है।

public interface PConstants {

  // LOTS OF static fields...

  static public final int SHINE = 31;

  // emissive (by default kept black)
  static public final int ER = 32;
  static public final int EG = 33;
  static public final int EB = 34;

  // has this vertex been lit yet
  static public final int BEEN_LIT = 35;

  static public final int VERTEX_FIELD_COUNT = 36;


  // renderers known to processing.core

  static final String P2D    = "processing.core.PGraphics2D";
  static final String P3D    = "processing.core.PGraphics3D";
  static final String JAVA2D = "processing.core.PGraphicsJava2D";
  static final String OPENGL = "processing.opengl.PGraphicsOpenGL";
  static final String PDF    = "processing.pdf.PGraphicsPDF";
  static final String DXF    = "processing.dxf.RawDXF";


  // platform IDs for PApplet.platform

  static final int OTHER   = 0;
  static final int WINDOWS = 1;
  static final int MACOSX  = 2;
  static final int LINUX   = 3;

  static final String[] platformNames = {
    "other", "windows", "macosx", "linux"
  };

  // and on and on

}

15
नोट: static finalयह आवश्यक नहीं है, यह एक इंटरफ़ेस के लिए बेमानी है।
9

यह भी ध्यान दें कि platformNamesहो सकता है public, staticऔर final, लेकिन यह निश्चित रूप से एक स्थिर नहीं है। एकमात्र निरंतर सरणी शून्य लंबाई के साथ एक है।
वलसेक

@ThomasW मुझे पता है कि यह कुछ साल पुराना है, लेकिन मुझे आपकी टिप्पणी में एक त्रुटि इंगित करने की आवश्यकता है। static finalजरूरी नहीं कि बेमानी हो। केवल finalकीवर्ड के साथ एक वर्ग या इंटरफ़ेस फ़ील्ड उस फ़ील्ड के अलग-अलग उदाहरणों को बनाएगा जैसा कि आप कक्षा या इंटरफ़ेस की ऑब्जेक्ट बनाते हैं। उपयोग static finalकरने से प्रत्येक वस्तु उस क्षेत्र के लिए एक स्मृति स्थान साझा कर देगी। दूसरे शब्दों में, यदि MyClass final String str = "Hello";के N उदाहरणों के लिए एक वर्ग MyClass के पास कोई फ़ील्ड है , तो स्मृति में str क्षेत्र के N उदाहरण होंगे। staticकीवर्ड जोड़ने पर केवल 1 उदाहरण दिखाई देगा।
सिन्ट्रियास

जवाबों:


160

यह आमतौर पर बुरा अभ्यास माना जाता है। समस्या यह है कि स्थिरांक कार्यान्वयन वर्ग के सार्वजनिक "इंटरफ़ेस" (बेहतर शब्द के लिए) का हिस्सा हैं। इसका मतलब यह है कि कार्यान्वयन वर्ग इन सभी मूल्यों को बाहरी कक्षाओं में प्रकाशित कर रहा है, यहां तक ​​कि जब वे केवल आंतरिक रूप से आवश्यक होते हैं। पूरे कोड में कॉन्स्टेंट का प्रसार होता है। एक उदाहरण स्विंगकॉन्स्टेंट्स है स्विंग में इंटरफ़ेस, जो दर्जनों वर्गों द्वारा कार्यान्वित किया जाता है जो सभी अपने सभी स्थिरांक (यहां तक ​​कि वे जो वे उपयोग नहीं करते हैं) को अपने स्वयं के रूप में "पुन: निर्यात" करते हैं।

लेकिन इसके लिए सिर्फ मेरे शब्द मत लो, जोश बलोच भी कहते हैं कि यह बुरा है:

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

एक एनम एक बेहतर दृष्टिकोण हो सकता है। या आप बस स्थिरांक के रूप में स्थिरांक क्षेत्रों में स्थिरांक लगा सकते हैं जिसे तत्काल नहीं किया जा सकता है। यह एक अन्य वर्ग को अपने स्वयं के एपीआई को प्रदूषित किए बिना उन्हें एक्सेस करने की अनुमति देता है।


8
Enums यहां एक लाल हेरिंग हैं - या कम से कम एक अलग प्रश्न। एनमों का इस्तेमाल निश्चित रूप से किया जाना चाहिए, लेकिन उन्हें भी छिपाया जाना चाहिए, अगर उन्हें कार्यान्वयनकर्ताओं की आवश्यकता नहीं है।
डीजेकेवर्थ

12
BTW: आप एक वर्ग के रूप में कोई उदाहरण के साथ एक एनम का उपयोग कर सकते हैं जिसे तत्काल नहीं किया जा सकता है। ;)
पीटर लॉरी

5
लेकिन पहली बार में उन इंटरफेस को क्यों लागू करना? सिर्फ स्थिरांक भंडार के रूप में उनका उपयोग क्यों नहीं किया जाता है? अगर मुझे विश्व स्तर पर साझा किए जाने वाले कुछ प्रकार के स्थिरांक चाहिए तो मुझे इसे करने के "क्लीनर" तरीके नहीं दिखते हैं।
शादॉक्स

2
@DDDyer हाँ, लेकिन एक इंटरफ़ेस कुछ घोषणाओं को निहित करता है। जैसे पब्लिक स्टेटिक फाइनल केवल एक डिफ़ॉल्ट है। क्लास से क्यों परेशान? एनम - ठीक है, यह निर्भर करता है। एक एनुम को एक इकाई के लिए संभावित मानों के संग्रह को परिभाषित करना चाहिए, न कि विभिन्न संस्थाओं के लिए मूल्यों का संग्रह।
शादॉक्स

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

99

जावा 1.5+ में "स्थिरांक इंटरफ़ेस" को लागू करने के बजाय, आप स्थिरांक आयात का उपयोग अन्य कक्षा / इंटरफ़ेस से स्थिरांक / स्थैतिक विधियों को आयात करने के लिए कर सकते हैं:

import static com.kittens.kittenpolisher.KittenConstants.*;

यह आपकी कक्षाओं को ऐसे इंटरफेस को लागू करने की कुरूपता से बचाता है जिनकी कोई कार्यक्षमता नहीं है।

केवल स्थिरांक को संग्रहीत करने के लिए एक वर्ग होने के अभ्यास के लिए, मुझे लगता है कि यह कभी-कभी आवश्यक होता है। ऐसे कुछ स्थिरांक हैं जो सिर्फ एक कक्षा में एक प्राकृतिक स्थान नहीं रखते हैं, इसलिए उन्हें "तटस्थ" स्थान पर रखना बेहतर है।

लेकिन एक इंटरफ़ेस का उपयोग करने के बजाय, एक निजी कंस्ट्रक्टर के साथ एक अंतिम वर्ग का उपयोग करें। (कक्षा को तुरंत या उप-वर्ग में करना असंभव बना देता है, एक मजबूत संदेश भेजना कि इसमें गैर-स्थिर कार्यक्षमता / डेटा नहीं है।)

उदाहरण के लिए:

/** Set of constants needed for Kitten Polisher. */
public final class KittenConstants
{
    private KittenConstants() {}

    public static final String KITTEN_SOUND = "meow";
    public static final double KITTEN_CUTENESS_FACTOR = 1;
}

तो, आप समझा रहे हैं कि, स्थैतिक आयात के कारण, हमें अलग-अलग वर्गों के बजाय पूर्णांक के रूप में उसी त्रुटि को फिर से करने के लिए उपयोग करना चाहिए ??? ये बेवकूफी है!
gizmo

11
नहीं, ऐसा मैं नहीं कह रहा हूं। मैं दो स्वतंत्र बातें कह रहा हूं। 1: वंशानुक्रम के दुरुपयोग के बजाय स्थैतिक आयात का उपयोग करें। 2: यदि आपके पास एक स्थिरांक भंडार होना चाहिए, तो इसे इंटरफ़ेस के बजाय एक अंतिम वर्ग बनाएं।
जारकोनेन

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

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

2
उपयोगी - मैं स्थैतिक आयातों का प्रशंसक नहीं हूं, लेकिन वह जो कर रहा है वह वर्ग के नाम का उपयोग करने से परहेज कर रहा है, यानी कॉन्स्टैक्लेस.सोम_कोनस्ट। स्थिर आयात करने से उन सदस्यों को उस वर्ग में नहीं जोड़ा जाता है जिन्हें आप Z से जोड़ते हैं। इंटरफ़ेस से विरासत में नहीं कहते हैं, वह वास्तव में विपरीत कहता है।
mtruesdell

8

मैं सही होने का दिखावा नहीं करता, लेकिन इस छोटे से उदाहरण को देखने देता हूं:

public interface CarConstants {

      static final String ENGINE = "mechanical";
      static final String WHEEL  = "round";
      // ...

}

public interface ToyotaCar extends CarConstants //, ICar, ... {
      void produce();
}

public interface FordCar extends CarConstants //, ICar, ... {
      void produce();
}

// and this is implementation #1
public class CamryCar implements ToyotaCar {

      public void produce() {
           System.out.println("the engine is " + ENGINE );
           System.out.println("the wheel is " + WHEEL);
      }
}

// and this is implementation #2
public class MustangCar implements FordCar {

      public void produce() {
           System.out.println("the engine is " + ENGINE );
           System.out.println("the wheel is " + WHEEL);
      }
}

ToyotaCar को FordCar के बारे में कुछ भी नहीं पता है, और FordCar को ToyotaCar के बारे में नहीं पता है। सिद्धांत CarConstants बदला जाना चाहिए, लेकिन ...

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

public interface InnovativeCarConstants {

          static final String ENGINE = "electronic";
          static final String WHEEL  = "flat";
          // ...
}

और अब हम अपना अमूर्त परिवर्तन कर सकते हैं:

public interface ToyotaCar extends CarConstants

सेवा

public interface ToyotaCar extends InnovativeCarConstants 

और अब अगर हमें कभी भी इंजन या WHEEL को एस्ट्रैक्शन लेवल पर ToyotaCar इंटरफ़ेस में बदल सकते हैं, तो कोर वैल्यू को बदलने की जरूरत नहीं है, न ही टचिंग इम्प्लीमेंटेशन

यह सुरक्षित नहीं है, मुझे पता है, लेकिन मैं अभी भी जानना चाहता हूं कि क्या आप इस बारे में सोचते हैं


मैं 2019 में अब आपको जानना चाहता हूं। मेरे लिए इंटरफ़ेस फ़ील्ड का मतलब कुछ वस्तुओं के बीच साझा करना है।
बारिश

: मैं अपने विचारों से सम्बंधित एक जवाब लिखा stackoverflow.com/a/55877115/5290519
बारिश हो रही

यह एक अच्छा उदाहरण है कि कैसे पीएमडी के नियम बहुत सीमित हैं। नौकरशाही के माध्यम से बेहतर कोड प्राप्त करने का प्रयास एक निरर्थक प्रयास है।
बेबो

6

जावा में इस पैटर्न के लिए बहुत नफरत है। हालांकि, स्थिर स्थिरांक के एक इंटरफ़ेस का कभी-कभी मूल्य होता है। आपको मूल रूप से निम्नलिखित शर्तों को पूरा करने की आवश्यकता है:

  1. अवधारणाएं कई वर्गों के सार्वजनिक इंटरफ़ेस का हिस्सा हैं।

  2. भविष्य के रिलीज में उनके मूल्य बदल सकते हैं।

  3. यह महत्वपूर्ण है कि सभी कार्यान्वयन समान मूल्यों का उपयोग करते हैं।

उदाहरण के लिए, मान लीजिए कि आप काल्पनिक क्वेरी भाषा के लिए एक्सटेंशन लिख रहे हैं। इस एक्सटेंशन में आप कुछ नए ऑपरेशन के साथ भाषा सिंटैक्स का विस्तार करने जा रहे हैं, जो एक इंडेक्स द्वारा समर्थित हैं। उदाहरण के लिए, आपके पास एक आर-ट्री जियोस्पेशल प्रश्नों का समर्थन करने वाला है।

तो आप स्थैतिक स्थिर के साथ एक सार्वजनिक इंटरफ़ेस लिखते हैं:

public interface SyntaxExtensions {
     // query type
     String NEAR_TO_QUERY = "nearTo";

     // params for query
     String POINT = "coordinate";
     String DISTANCE_KM = "distanceInKm";
}

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

पुनश्च: इस उदाहरण के लिए प्रेरणा Neo4j स्थानिक कोड से तैयार की गई है।


5

दृष्टि के लाभ को देखते हुए, हम देख सकते हैं कि जावा कई तरीकों से टूट गया है। जावा की एक बड़ी विफलता सार विधियों और स्थिर अंतिम क्षेत्रों के लिए इंटरफेस का प्रतिबंध है। नए, अधिक परिष्कृत ओओ भाषाओं जैसे कि स्काला के लक्षणों को उपसमुच्चय द्वारा नियंत्रित किया जाता है जो (और आमतौर पर ऐसा कर सकते हैं) में ठोस विधियां शामिल हैं, जिनमें शून्य (स्थिरांक) हो सकती हैं। संयोजक व्यवहार की इकाइयों के रूप में लक्षणों पर एक प्रदर्शनी के लिए, http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf देखें । जावा में इंटरफेस के साथ स्काला की तुलना कैसे की जाती है, इसका संक्षिप्त विवरण देखें http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-5। ओओ डिजाइन सिखाने के संदर्भ में, सरलीकृत नियमों जैसे कि स्थैतिक क्षेत्रों को मूर्खतापूर्ण क्षेत्रों में शामिल नहीं करना चाहिए। कई लक्षणों में स्वाभाविक रूप से स्थिरांक शामिल हैं और ये स्थिरांक विशेषता द्वारा समर्थित सार्वजनिक "इंटरफ़ेस" का उचित हिस्सा हैं। जावा कोड लिखने में, लक्षणों का प्रतिनिधित्व करने का कोई साफ, सुरुचिपूर्ण तरीका नहीं है, लेकिन इंटरफेस के भीतर स्थिर अंतिम क्षेत्रों का उपयोग करना अक्सर एक अच्छा बदलाव का हिस्सा होता है।


12
बेहद दिखावा और आजकल पुराना है।
Esko

1
अद्भुत अंतर्दृष्टि (+1), हालांकि जावा के खिलाफ शायद थोड़ा बहुत महत्वपूर्ण है।
पेटेर -

0

जेवीएम विनिर्देश के अनुसार, एक इंटरफ़ेस में फ़ील्ड और विधियाँ केवल सार्वजनिक, स्थैतिक, अंतिम और सार हो सकती हैं। जावा वीएम के अंदर से रेफरी

डिफ़ॉल्ट रूप से, इंटरफ़ेस की सभी विधियाँ अमूर्त हैं, यहां तक ​​कि आपने इसका स्पष्ट रूप से उल्लेख नहीं किया है।

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


0

प्लेलेक को एक टिप्पणी देने के लिए मेरे पास पर्याप्त प्रतिष्ठा नहीं है, इसके लिए मुझे एक जवाब बनाना होगा। मुझे इसके लिए खेद है, लेकिन उन्होंने इसमें कुछ अच्छा प्रयास किया है और मैं इसका जवाब देना चाहूंगा।

Pleerock, आपने यह दिखाने के लिए सही उदाहरण बनाया कि क्यों उन स्थिरांक को इंटरफेस से स्वतंत्र होना चाहिए और विरासत से स्वतंत्र होना चाहिए। आवेदन के ग्राहक के लिए यह महत्वपूर्ण नहीं है कि कारों के कार्यान्वयन के बीच तकनीकी अंतर हो। वे ग्राहक के लिए समान हैं, बस कारें। इसलिए, ग्राहक उन्हें उस दृष्टिकोण से देखना चाहते हैं, जो कि I_Somecar जैसा एक इंटरफ़ेस है। आवेदन के दौरान ग्राहक केवल एक ही दृष्टिकोण का उपयोग करेगा और प्रत्येक अलग कार ब्रांड के लिए अलग-अलग नहीं होगा।

अगर कोई ग्राहक कार खरीदने से पहले उसकी तुलना करना चाहता है तो उसके पास इस तरह का तरीका हो सकता है:

public List<Decision> compareCars(List<I_Somecar> pCars);

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


-1

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

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


2
जावा 5 से पहले, आप टाइप-सेफ एनुम पैटर्न ( java.sun.com/developer/Books/shiftintojava/page1.html देखें ) का उपयोग कर सकते थे ।
डैन डायर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.