स्विफ्ट भाषा में सार कक्षाएं


141

क्या स्विफ्ट भाषा में एक अमूर्त वर्ग बनाने का एक तरीका है, या क्या यह उद्देश्य-सी की तरह एक सीमा है? मैं एक सार वर्ग के रूप में जावा को परिभाषित करता है, जो एक सार वर्ग बनाना चाहता हूं।


क्या आपको सारगर्भित होने के लिए पूरी कक्षा की आवश्यकता है या इसमें कुछ तरीके हैं? एकल विधियों और गुणों के लिए यहां उत्तर देखें। stackoverflow.com/a/39038828/2435872 । जावा में आप एवे एब्स्ट्रैक्ट क्लासेस कर सकते हैं, जिसमें एब्सट्रैक्ट का कोई भी तरीका नहीं है। वह विशेष सुविधा स्विफ्ट द्वारा प्रदान नहीं की गई है।
जौबी

जवाबों:


175

स्विफ्ट में कोई सार कक्षाएं नहीं हैं (बस उद्देश्य-सी की तरह)। आपका सबसे अच्छा शर्त एक प्रोटोकॉल का उपयोग करने वाला है , जो एक जावा इंटरफेस की तरह है।

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

इस तकनीक का एक उदाहरण होगा:

protocol Employee {
    var annualSalary: Int {get}
}

extension Employee {
    var biweeklySalary: Int {
        return self.annualSalary / 26
    }

    func logSalary() {
        print("$\(self.annualSalary) per year or $\(self.biweeklySalary) biweekly")
    }
}

struct SoftwareEngineer: Employee {
    var annualSalary: Int

    func logSalary() {
        print("overridden")
    }
}

let sarah = SoftwareEngineer(annualSalary: 100000)
sarah.logSalary() // prints: overridden
(sarah as Employee).logSalary() // prints: $100000 per year or $3846 biweekly

ध्यान दें कि यह "एब्सट्रैक्ट क्लास" प्रदान कर रहा है, यहां तक ​​कि स्ट्रक्चर्स के लिए भी, लेकिन कक्षाएं भी उसी प्रोटोकॉल को लागू कर सकती हैं।

यह भी ध्यान दें कि कर्मचारी प्रोटोकॉल को लागू करने वाले प्रत्येक वर्ग या संरचना को फिर से वार्षिक संपत्ति घोषित करनी होगी।

सबसे महत्वपूर्ण बात, ध्यान दें कि कोई गतिशील प्रेषण नहीं है । जब logSalaryइसे इंस्टेंस के रूप में संग्रहीत किया जाता है तो SoftwareEngineerइसे विधि का ओवरराइड संस्करण कहा जाता है । जब इसे कॉल logSalaryकरने के बाद इसे इंस्टेंस पर Employeeकॉल किया जाता है , तो यह ओरिजिनल इम्प्लीमेंटेशन कहता है (यह डायनामिकली ओवरराइड किए गए वर्जन पर नहीं जाता है, हालांकि इंस्टेंस वास्तव में ए है Software Engineer

अधिक जानकारी के लिए, उस सुविधा के बारे में महान WWDC वीडियो देखें: स्विफ्ट में मूल्य प्रकारों के साथ बिल्डिंग बेहतर ऐप्स


3
protocol Animal { var property : Int { get set } }। यदि आप नहीं चाहते कि आप सेट को छोड़ भी सकें, तो संपत्ति को एक सेटर करने की जरूरत नहीं है
20:15

3
मुझे लगता है कि यह wwdc वीडियो और भी अधिक प्रासंगिक है
Mario Zannone

2
@MarioZannone उस वीडियो ने मेरे दिमाग को उड़ा दिया और मुझे स्विफ्ट से प्यार हो गया।
स्कॉट एच।

3
यदि आप केवल func logSalary()कर्मचारी प्रोटोकॉल घोषणा में जोड़ते हैं , तो उदाहरण overriddenदोनों कॉल के लिए प्रिंट करता है logSalary()। यह स्विफ्ट 3.1 में है। इस प्रकार आपको बहुरूपता का लाभ मिलता है। दोनों ही मामलों में सही विधि कहा जाता है।
माइक टैवर्न

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

47

ध्यान दें कि यह उत्तर स्विफ्ट 2.0 और इसके बाद के संस्करण पर लक्षित है

आप प्रोटोकॉल और प्रोटोकॉल एक्सटेंशन के साथ समान व्यवहार प्राप्त कर सकते हैं।

सबसे पहले, आप एक प्रोटोकॉल लिखते हैं जो उन सभी तरीकों के लिए एक इंटरफ़ेस के रूप में कार्य करता है जिन्हें सभी प्रकारों में लागू किया जाना है जो इसके अनुरूप हैं।

protocol Drivable {
    var speed: Float { get set }
}

फिर आप उन सभी प्रकारों में डिफ़ॉल्ट व्यवहार जोड़ सकते हैं जो इसके अनुरूप हैं

extension Drivable {
    func accelerate(by: Float) {
        speed += by
    }
}

अब आप लागू करके नए प्रकार बना सकते हैं Drivable

struct Car: Drivable {
    var speed: Float = 0.0
    init() {}
}

let c = Car()
c.accelerate(10)

तो मूल रूप से आपको मिलता है:

  1. संकलित समय की जाँच करता है कि गारंटी है कि सभी को Drivableलागूspeed
  2. आप Drivable( accelerate) के अनुरूप सभी प्रकारों के लिए डिफ़ॉल्ट-व्यवहार लागू कर सकते हैं
  3. Drivable यह सिर्फ एक प्रोटोकॉल के बाद से तुरंत नहीं होने की गारंटी है

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


फिर भी, हमेशा कुछ प्रोटोकॉल का विस्तार करने की संभावना नहीं होती है, उदाहरण के लिए UICollectionViewDatasource,। मैं सभी बॉयलरप्लेट को हटाकर अलग-अलग प्रोटोकॉल / एक्सटेंशन में इनकैप्सुलेट करना चाहता हूं और फिर कई वर्गों द्वारा फिर से उपयोग करना चाहता हूं। वास्तव में, टेम्प्लेट पैटर्न यहां बिल्कुल सही होगा, लेकिन ...
रिचर्ड टोपची

1
आप ˚Car˚ में ˚accelerate˚ को ओवरराइट नहीं कर सकते। यदि आप करते हैं, ˚extentsion Driveeable˚ में कार्यान्वयन को अभी भी बिना किसी संकलक चेतावनी के कहा जाता है। एक जावा अमूर्त वर्ग के विपरीत
गर्ड कास्टान

@GerdCastan सच है, प्रोटोकॉल एक्सटेंशन डायनेमिक प्रेषण का समर्थन नहीं करते हैं।
इलूस्तोव

15

मुझे लगता है कि यह जावा abstractया C # के सबसे करीब है abstract:

class AbstractClass {

    private init() {

    }
}

ध्यान दें, privateकाम करने के लिए संशोधक के लिए, आपको एक अलग स्विफ्ट फ़ाइल में इस वर्ग को परिभाषित करना होगा।

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


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

C # में, यदि आप एक एब्सट्रैक्ट बेस क्लास में कोई फंक्शन लागू करते हैं, तो आप इसे इसके सबक्लासेस में लागू करने के लिए मजबूर नहीं होते हैं। फिर भी, यह कोड आपको ओवरराइड करने के लिए एक अमूर्त विधि घोषित करने की अनुमति नहीं देता है।
तीजय

आओ हम कहते हैं कि कंक्रीटक्लास सबक्लास कि एब्सट्रैक्टस। आप कैसे कर सकते हैं?
जेवियर

2
कंक्रीटक्लास में एक सार्वजनिक निर्माणकर्ता होना चाहिए। आपको शायद AbstractClass में एक संरक्षित कंस्ट्रक्टर की आवश्यकता है, जब तक कि वे एक ही फ़ाइल में न हों। जैसा कि मुझे याद है, स्विफ्ट में संरक्षित एक्सेस संशोधक मौजूद नहीं है। तो समाधान यह है कि एक ही फाइल में कंक्रीटक्लास घोषित किया जाए।
तीजय Te

13

सबसे आसान तरीका fatalError("Not Implemented")प्रोटोकॉल एक्सटेंशन पर अमूर्त विधि (चर नहीं) में कॉल का उपयोग करना है ।

protocol MyInterface {
    func myMethod() -> String
}


extension MyInterface {

    func myMethod() -> String {
        fatalError("Not Implemented")
    }

}

class MyConcreteClass: MyInterface {

    func myMethod() -> String {
        return "The output"
    }

}

MyConcreteClass().myMethod()

यह एक बेहतरीन जवाब है। मुझे नहीं लगता था कि यह काम करेगा अगर आप कहते हैं (MyConcreteClass() as MyInterface).myMethod()लेकिन यह करता है! कुंजी myMethodप्रोटोकॉल घोषणा में शामिल है; अन्यथा कॉल क्रैश हो जाता है।
माइक टैवर्न

11

कई हफ्तों तक संघर्ष करने के बाद, मुझे आखिरकार एहसास हुआ कि स्विफ्ट में जावा / पीएचपी अमूर्त वर्ग का अनुवाद कैसे किया जाए:

public class AbstractClass: NSObject {

    internal override init(){}

    public func getFoodToEat()->String
    {
        if(self._iAmHungry())
        {
            return self._myFavoriteFood();
        }else{
            return "";
        }
    }

    private func _myFavoriteFood()->String
    {
        return "Sandwich";
    }

    internal func _iAmHungry()->Bool
    {
        fatalError(__FUNCTION__ + "Must be overridden");
        return false;
    }
}

public class ConcreteClass: AbstractClass, IConcreteClass {

    private var _hungry: Bool = false;

    public override init() {
        super.init();
    }

    public func starve()->Void
    {
        self._hungry = true;
    }

    public override func _iAmHungry()->Bool
    {
        return self._hungry;
    }
}

public protocol IConcreteClass
{
    func _iAmHungry()->Bool;
}

class ConcreteClassTest: XCTestCase {

    func testExample() {

        var concreteClass: ConcreteClass = ConcreteClass();

        XCTAssertEqual("", concreteClass.getFoodToEat());

        concreteClass.starve();

        XCTAssertEqual("Sandwich", concreteClass.getFoodToEat());
    }
}

हालाँकि मुझे लगता है कि Apple ने अमूर्त वर्ग को लागू नहीं किया क्योंकि यह आम तौर पर डेलिगेट + प्रोटोकॉल पैटर्न का उपयोग करता है। उदाहरण के लिए उपरोक्त पैटर्न इसी तरह बेहतर होगा:

import UIKit

    public class GoldenSpoonChild
    {
        private var delegate: IStomach!;

        internal init(){}

        internal func setup(delegate: IStomach)
        {
            self.delegate = delegate;
        }

        public func getFoodToEat()->String
        {
            if(self.delegate.iAmHungry())
            {
                return self._myFavoriteFood();
            }else{
                return "";
            }
        }

        private func _myFavoriteFood()->String
        {
            return "Sandwich";
        }
    }

    public class Mother: GoldenSpoonChild, IStomach
    {

        private var _hungry: Bool = false;

        public override init()
        {
            super.init();
            super.setup(self);
        }

        public func makeFamilyHungry()->Void
        {
            self._hungry = true;
        }

        public func iAmHungry()->Bool
        {
            return self._hungry;
        }
    }

    protocol IStomach
    {
        func iAmHungry()->Bool;
    }

    class DelegateTest: XCTestCase {

        func testGetFood() {

            var concreteClass: Mother = Mother();

            XCTAssertEqual("", concreteClass.getFoodToEat());

            concreteClass.makeFamilyHungry();

            XCTAssertEqual("Sandwich", concreteClass.getFoodToEat());
        }
    }

मुझे इस तरह के पैटर्न की आवश्यकता थी क्योंकि मैं UITableViewController जैसे viewWillAppear आदि में कुछ तरीकों को सामान्य करना चाहता था।


1
+1 वही सटीक दृष्टिकोण करने की योजना बना रहा था जिसका आप पहले उल्लेख करते हैं; डेलिगेशन पैटर्न के लिए दिलचस्प सूचक।
अंगद

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

@ अंगद प्रतिनिधि पैटर्न एक ही उपयोग का मामला है, हालांकि इसका अनुवाद नहीं है; यह एक अलग पैटर्न है इसलिए इसे एक अलग परिप्रेक्ष्य लेना चाहिए।
जोश वुडकॉक

8

प्रोटोकॉल का उपयोग करके अमूर्त वर्गों का अनुकरण करने का एक तरीका है। यह एक उदाहरण है:

protocol MyProtocol {
   func doIt()
}

class BaseClass {
    weak var myDelegate: MyProtocol?

    init() {
        ...
    }

    func myFunc() {
        ...
        self.myDelegate?.doIt()
        ...
    }
}

class ChildClass: BaseClass, MyProtocol {
    override init(){
        super.init()
        self.myDelegate = self
    }

    func doIt() {
        // Custom implementation
    }
}

1

एक और तरीका है कि आप अमूर्त वर्ग को कैसे लागू कर सकते हैं, इनिशियलज़र को ब्लॉक करना है। मैंने इसे इस तरह किया है:

class Element:CALayer { // IT'S ABSTRACT CLASS

    override init(){ 
        super.init()
        if self.dynamicType === Element.self {
        fatalError("Element is abstract class, do not try to create instance of this class")
        }
    }
}

4
यह कोई गारंटी और / या चेक प्रदान नहीं करता है। रनटाइम के दौरान ब्लोइंग नियमों को लागू करने का एक बुरा तरीका है। निजी के रूप में इनिट होना बेहतर है।
Морт

अमूर्त कक्षाओं में भी अमूर्त तरीकों के लिए समर्थन होना चाहिए।
12

@ क्रिसिक मैंने मुख्य विचार दिखाया, यह पूर्ण समाधान नहीं है। इस तरह आप 80% उत्तरों को नापसंद कर सकते हैं क्योंकि वे आपकी स्थिति के लिए पर्याप्त विस्तृत नहीं हैं
अलेक्सई यारमोलोविच

1
@AlexeyYarmolovich कौन कहता है कि मैं उत्तर के 80% को नापसंद नहीं कर रहा हूँ? :) एक तरफ मजाक करते हुए, मैं सुझाव दे रहा था कि आपके उदाहरण में सुधार किया जा सकता है, इससे अन्य पाठकों को मदद मिलेगी, और उत्थान प्राप्त करने में आपकी मदद करेगा।
क्रिस्टिक

0

मैं एक Weatherसार वर्ग बनाने की कोशिश कर रहा था , लेकिन प्रोटोकॉल का उपयोग करना आदर्श नहीं था क्योंकि मुझे initबार-बार एक ही तरीके को लिखना पड़ता था। प्रोटोकॉल का विस्तार और एक लेखनinit विधि यह समस्या थी, खासकर जब से मैं इसके NSObjectअनुरूप उपयोग कर रहा थाNSCoding

इसलिए मैं इस NSCodingअनुरूपता के लिए आया :

required init?(coder aDecoder: NSCoder) {
    guard type(of: self) != Weather.self else {
        fatalError("<Weather> This is an abstract class. Use a subclass of `Weather`.")
    }
    // Initialize...
}        

के रूप में init:

fileprivate init(param: Any...) {
    // Initialize
}

0

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

protocol Commom:class{
  var tableView:UITableView {get};
  func update();
}

class Base{
   var total:Int = 0;
}

extension Common where Self:Base{
   func update(){
     total += 1;
     tableView.reloadData();
   }
} 

class Derived:Base,Common{
  var tableView:UITableView{
    return owner.tableView;
  }
}

0

कोई गतिशील प्रेषण की सीमा के साथ, आप ऐसा कुछ कर सकते हैं:

import Foundation

protocol foo {

    static var instance: foo? { get }
    func prt()

}

extension foo {

    func prt() {
        if Thread.callStackSymbols.count > 30 {
            print("super")
        } else {
            Self.instance?.prt()
        }
    }

}

class foo1 : foo {

    static var instance : foo? = nil

    init() {
        foo1.instance = self
    }

    func prt() {
        print("foo1")
    }

}

class foo2 : foo {

    static var instance : foo? = nil

    init() {
        foo2.instance = self
    }

    func prt() {
        print("foo2")
    }

}

class foo3 : foo {

    static var instance : foo? = nil

    init() {
        foo3.instance = self
    }

}

var f1 : foo = foo1()
f1.prt()
var f2 : foo = foo2()
f2.prt()
var f3 : foo = foo3()
f3.prt()
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.