मुझे एक वर्ग को अमूर्त वर्ग क्यों घोषित करना चाहिए?


40

मुझे सिंटैक्स पता है, अमूर्त वर्ग के लिए लागू नियम और मैं एक अमूर्त वर्ग का उपयोग जानना चाहता हूं

एब्सट्रैक्ट क्लास को सीधे इंस्टैंट नहीं किया जा सकता है लेकिन अन्य क्लास द्वारा बढ़ाया जा सकता है

ऐसा करने से फायदा क्या है?

यह एक इंटरफेस से कैसे अलग है?

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

मैं एक इंटरफ़ेस के उपयोग के बारे में अवगत हूं। मुझे पता चला है कि जावा में AWT के इवेंट डेलिगेशन मॉडल से।

किन स्थितियों में मुझे कक्षा को एक सार वर्ग घोषित करना चाहिए? उससे क्या लाभ है?


14
यह पूछा गया है, आप जानते हैं। एक खोज इस तरह के अन्य प्रश्नों को बदल देगी। आपको Google पर शुरू करना चाहिए, जो आपको स्टैक ओवरफ्लो की ओर ले जाएगा। ये सभी डुप्लिकेट हैं: stackoverflow.com/search?q=abstract+interface
S.Lott

1
"सार वर्ग का उपयोग वे सिर्फ चर्चा करते हैं ... नियम"। क्या? "नियम" और "उपयोग" के बीच क्या अंतर है? आपका सटीक सवाल पूछा गया, वैसे। मुझे पता है। मैंने इसका जवाब दिया। देखते रहो। खोज का उपयोग करना सीखना महत्वपूर्ण है।
S.Lott

मेरा मतलब है "किन स्थितियों में मुझे कक्षा को अमूर्त वर्ग के रूप में घोषित करना चाहिए? क्या लाभ है?"
वैभव जानी

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

मुझे लगता है कि टेम्पलेट विधि पैटर्न एक बहुत शक्तिशाली एक है और अमूर्त वर्गों के लिए एक अच्छा उपयोग-मामला है।
m3th0dman

जवाबों:


49

यह उत्तर एक अमूर्त वर्ग और एक अंतरफलक के बीच के अंतर को समझाने का एक अच्छा काम करता है , लेकिन यह जवाब नहीं देता है कि आपको एक क्यों घोषित करना चाहिए।

विशुद्ध रूप से तकनीकी दृष्टिकोण से, एक वर्ग को सार घोषित करने की आवश्यकता कभी नहीं होती है ।

निम्नलिखित तीन वर्गों पर विचार करें:

class Database { 
    public String[] getTableNames() { return null; } //or throw an exception? who knows...
}

class SqlDatabase extends Database { } //TODO: override getTableNames

class OracleDatabase extends Database { }  //TODO: override getTableNames

आपको डेटाबेस वर्ग को अमूर्त बनाने की आवश्यकता नहीं है , भले ही इसके कार्यान्वयन में एक स्पष्ट समस्या है: जब आप इस कार्यक्रम को लिख रहे हैं, तो आप टाइप कर सकते हैं new Database()और यह मान्य होगा, लेकिन यह कभी काम नहीं करेगा।

भले ही, आप अभी भी बहुरूपता प्राप्त करेंगे, इसलिए जब तक आपका कार्यक्रम केवल बनाता है SqlDatabaseऔर OracleDatabaseउदाहरण देता है, आप निम्न तरीके लिख सकते हैं:

public void printTableNames(Database database) {
    String[] names = database.getTableNames();
}

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

इंटरफेस एक पूरी तरह से अलग विषय है। एक इंटरफ़ेस आपको यह वर्णन करने देता है कि किसी ऑब्जेक्ट पर कौन से ऑपरेशन किए जा सकते हैं। जब आप अन्य घटकों, वस्तुओं की सेवाओं का उपयोग करते हैं, तो आप आमतौर पर इंटरफेस का उपयोग करते हैं, लेकिन आप इस बात की परवाह नहीं करते हैं कि आपको किस प्रकार की वस्तु मिल रही है।

निम्नलिखित विधि पर विचार करें:

public void saveToDatabase(IProductDatabase database) {
     database.addProduct(this.getName(), this.getPrice());
}

आप इस बात की परवाह नहीं करते हैं कि databaseवस्तु किसी विशेष वस्तु से विरासत में मिली है या नहीं, आप केवल इस बात की परवाह करते हैं कि यह एक addProductविधि है। तो इस मामले में, एक इंटरफ़ेस आपके सभी वर्गों को एक ही आधार वर्ग से विरासत में लेने से बेहतर है।

कभी-कभी दोनों का संयोजन बहुत अच्छी तरह से काम करता है। उदाहरण के लिए:

abstract class RemoteDatabase implements IProductDatabase { 
    public abstract String[] connect();
    public abstract void writeRow(string col1, string col2);

    public void addProduct(String name, Double price) {
        connect();
        writeRow(name, price.toString());
    }
}

class SqlDatabase extends RemoteDatabase {
    //TODO override connect and writeRow
}

class OracleDatabase extends RemoteDatabase { 
    //TODO override connect and writeRow
}

class FileDatabase implements IProductDatabase {
    public void addProduct(String name, Double price) {
         //TODO: just write to file
    }
}

ध्यान दें कि कुछ कार्यक्षमता साझा करने के लिए रिमोटडेटाबेस से कुछ डेटाबेस कैसे प्राप्त होते हैं (जैसे कि पंक्ति लिखने से पहले कनेक्ट करना), लेकिन फाइलडाबेस अलग वर्ग है जो केवल लागू करता है IProductDatabase


16

समानताएँ

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

अंतर

  1. इंटरफेस

    • सार्वजनिक अनुबंध, प्रकार की क्षमताओं को अच्छी तरह से परिभाषित करें
    • क्षैतिज विरासत को दर्शाने के लिए लागू किया जाता है, अर्थात वंशानुक्रम के पहले स्तर पर शाखाकरण (जैसे डेटाबेस, पाठ फ़ाइल, XML, SOAP आदि में लॉगिंग सुविधाओं को परिभाषित करने के लिए ILog)
    • सभी सदस्य सार्वजनिक हैं
    • कोई कार्यान्वयन की अनुमति नहीं है
    • वंशानुक्रम बच्चे को लागू करने के लिए कई इंटरफेस हो सकते हैं
    • तीसरे पक्ष के एकीकरण के लिए उपयोगी
    • नामकरण आमतौर पर I से शुरू होता है
  2. सार वर्ग

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

सरल Google क्वेरी द्वारा उत्तर खोजना वास्तव में आसान है ।


क्या आप कार्यान्वयन की अनुमति नहीं देते हैं? जावा वर्ग इंटरफेस को लागू कर सकता है।
सजुक

@Sajuuk वह लाइन एक इंटरफ़ेस को संदर्भित करती है। आप एक अनुबंध के कार्यान्वयन को एक इंटरफ़ेस में नहीं डाल सकते। आपके पास एक अमूर्त वर्ग में एक अनुबंध का डिफ़ॉल्ट कार्यान्वयन हो सकता है।
oleksii

11

यह एक इंटरफेस से कैसे अलग है?

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


यह वास्तव में जावा 8 के लिए एक अपडेट की जरूरत है, जहां डिफ़ॉल्ट तरीके पेश किए गए थे।
हाकॉन लोटवेट

8

अमूर्त वर्ग "के लिए है" एक रिश्ते और इंटरफेस "कर सकते हैं" के लिए हैं।

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


3

गहरे तकनीकी विवरणों के अलावा - जैसे अमूर्त वर्गों के लिए कुछ तरीके लागू करना, आदि, अर्थ इस प्रकार है:

इंटरफेस सामान्य क्षमता को परिभाषित करते हैं - IEnumerable परिभाषित करता है, कि जो वर्ग इस इंटरफ़ेस को लागू करता है उसे गणना की जा सकती है। यह वर्ग के बारे में कुछ नहीं कहता है।

सार (या आधार) कक्षाएं व्यवहार को परिभाषित करती हैं - WebRequest सभी बाल वर्गों जैसे HttpWebRequest, आदि के सामान्य व्यवहार को परिभाषित करता है। यह कक्षा के मूल अर्थ को परिभाषित करता है और वेब संसाधनों तक पहुंचने के लिए यह वास्तविक उद्देश्य है।


2

विकिपीडिया प्रविष्टि

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

interface MyInterface1 {
  string getValue1();
}

interface MyInterface2 {
  string getValue2();
}

abstract class MyAbstractClass implements MyInterface1, MyInterface2{
  void printValues() {
    System.out.println("Value 1: " + getValue1() + ", Value 2: " + getValue2() + 
                       ", Value 3: " + getValue3());
  }

  protected abstract string getValue3();
}

class ImpClass extends MyAbstractClass {
  public string getValue1() {
    return "1";
  }

  public string getValue2() {
    return "2";
  }

  protected string getValue3() {
    return "3";
  }
}

इस उदाहरण में, MyAbstractClass एक सार्वजनिक विधि प्रदान करता है जो तीनों मानों को प्रिंट करता है। में ImpClass आप से क्रमशः getValue1, और getValue2 लागू करने की आवश्यकता MyInterface1 और MyInterface2 सार वर्ग से और getValue3।

Voilà।

इसके और पहलू हैं (इंटरफ़ेस: केवल सार्वजनिक विधियाँ, अमूर्त वर्ग: संरक्षित अमूर्त और सार्वजनिक अमूर्त विधियाँ) लेकिन आप इसे अपने लिए पढ़ सकते हैं।

अंतिम नोट पर, एक अमूर्त वर्ग जो केवल अमूर्त तरीके प्रदान करता है, वह है "शुद्ध" सार आधार वर्ग, उर्फ़, एक इंटरफ़ेस।


2
  • इंटरफ़ेस - जब कुछ कक्षाएं एक एपीआई (विधि नाम और पैरामीटर) साझा करती हैं
  • सार वर्ग - जब कुछ वर्ग समान कोड (कार्यान्वयन) साझा करते हैं

दूसरे शब्दों में, आपको एक प्रश्न के साथ शुरू करना चाहिए: "क्या ये कक्षाएं आवश्यक रूप से कार्यान्वयन को साझा करती हैं , या क्या उनके पास एक सामान्य इंटरफ़ेस है ?"

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

कार्यान्वयन को साझा करने के अन्य तरीके भी हैं, उदाहरण के लिए उस कार्यान्वयन के साथ एक वस्तु को एनकैप्सुलेट करना (उदाहरण के लिए रणनीति पैटर्न)।


1

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

उदाहरण के लिए, एक गेम पर विचार करें जहां विभिन्न प्रकार की गेम इकाइयां हैं। वे सभी आधार GameEntityवर्ग से विरासत में मिले हैं ।

abstract class GameEntity{

    int lifePoint, speed, damage;

    public attack(GameEntity target){ target.damage(damage); }

    public damage(int damageInflicted){ lifePoint -= damageInflicted - speed; }

    // etc...

}

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

एक अमूर्त वर्ग और एक अंतरफलक के बीच उपयोग में अंतर के बारे में:

जैसा कि मैं इसे देखता हूं, एक इंटरफ़ेस कुछ भाषाओं के एकल-विरासत तंत्र द्वारा सीमित किए बिना बहुरूपी व्यवहार प्राप्त करने का एक तरीका है।

एक उदाहरण के रूप में खेल में लौटते हैं। एक वर्ग पर विचार करें, Enemyजो से प्राप्त होता है GameEntity। इस वर्ग की एक विधि है attackMeFromDistance(RangedAttacker attacker)। यह विधि संस्थाओं को दूर से दुश्मन पर हमला करने की अनुमति देने के लिए है।

जैसा कि आप देख सकते हैं, यह विधि RangedAttackerएक पैरामीटर के रूप में एक प्रकार लेती है । हालाँकि, सभी खेल निकाय पहले से ही विरासत में हैं GameEntity। वे दूसरे वर्ग का विस्तार नहीं कर सकते।

कक्षाएं ले लो Mageऔर Archerउदाहरण के लिए। हम दोनों को attackMeFromDistance(RangedAttacker attacker)विधि में पैरामीटर के रूप में स्वीकार करने की अनुमति देना चाहते हैं , लेकिन वे पहले से ही व्युत्पन्न हैं GameEntity

इसे हल करने के लिए, हम एक नया इंटरफ़ेस बनाते हैं:

interface RangedAttacker{
    public void attackFromDistance();
}

एक वर्ग जो इस इंटरफ़ेस को लागू करता attackFromDistance()है, उसे विधि को लागू करना चाहिए , और इस प्रकार यह सुनिश्चित किया जाता है कि इस पर हमला करने की क्षमता है। इसका मतलब यह है कि attackMeFromDistanceविधि अब उन इंटरफ़ेस को लागू करने वाली कक्षाओं को सुरक्षित रूप से स्वीकार कर सकती है। इसलिए उस इंटरफ़ेस को बनाना Mageऔर Archerकार्यान्वित करना हमारी समस्या को हल करता है।

मेरे लिए, यह इंटरफेस की शक्ति है।

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


0
  1. मौका है कुछ वर्ग (व्यावसायिक तर्क) के लिए बहुत कम विधियां आम हैं। और शेष विधियां अलग हैं। उस तरह के परिदृश्यों में आप सभी सामान्य तरीकों को एक कक्षा में लागू कर सकते हैं, और शेष को सार के रूप में घोषित कर सकते हैं। फिर आपको कक्षा को सार घोषित करना चाहिए।
  2. कभी-कभी आप कक्षा को सीधे ऑब्जेक्ट बनाने की अनुमति नहीं देते हैं। उस तरह के वर्ग को आपको कक्षा को सार घोषित करने की आवश्यकता है।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.