2015-06-01 17 views
7

Swift không có lớp và phương thức trừu tượng. Thay vào đó, nó cung cấp các giao thức.Swift - trộn các phương pháp trừu tượng và cụ thể

Điều đó tốt khi lớp học của bạn hoàn toàn trừu tượng hoặc hoàn toàn cụ thể.

Nhưng cách 'Swift' tốt nhất để triển khai lớp trừu tượng cũng có phương pháp cụ thể là gì?

Pseudo-code ví dụ:

class Animal { 
    abstract makeSound() 
    abstract eyeCount() 
} 

class Mammal : Animal { 
    override eyeCount { return 2 } // Let's assume all mammals have hard-coded 2 eyes... 

class Cat : Mammal { 
    override makeSound { print "Meow!" } 
} 

class Dog : Mammal { 
    override makeSound { print "Woof!" } 
} 

Trong động vật có vú, tôi muốn thực hiện phương pháp bê tông eyeCount() bởi vì tất cả động vật có vú có 2 mắt mã hóa cứng (được cho là) ​​và tôi không muốn tái thực hiện nó trong chó và mèo. Tuy nhiên, chỉ nên triển khai makeSound() cho Chó và Mèo vì động vật có vú có các giọng nói khác nhau.

Bạn sẽ triển khai điều này trong Swift như thế nào? Cảm ơn!

Trả lời

6

tôi sẽ thực hiện nó như thế này:

class AbstractAnimal 
{ 
    // Fully abstract method 
    func methodThatReturnsSomething() -> String { 
     fatalError("methodThatReturnsSomething() is abstract and must be overriden!"); 
    } 

    func eyeCount() -> Int { 
     return 2; 
    } 
} 

fatalError ngăn Xcode từ phàn nàn rằng phương thức trừu tượng methodThatReturnsSomething() không thực sự trả về bất kỳ thứ gì.

+0

Tôi thực sự gặp rắc rối với những thứ tuyên bố đi sai chỉ trong thời gian chạy. Nó làm cho mã không đáng tin cậy. Kiểm tra @pommes câu trả lời hoặc này, để thực hiện, kiểm tra trong thời gian biên dịch http://stackoverflow.com/a/39038828/2435872 – jboi

0

Một phương pháp phổ biến để làm điều này là có một lớp trừu tượng có phương pháp cụ thể trong đó. Tất cả các lớp có thể phân lớp lớp trừu tượng này để kế thừa các phương thức cụ thể cũng như các phương thức trừu tượng.

UIGuestureRecognizerUIGuestureRecognizerSubclass là một ví dụ rất hay về điều này. Bởi vì điều này là dành cho lớp con, lớp trừu tượng này chứa nhiều phương thức cụ thể bên trong cũng như các phương thức trừu tượng có thể phân lớp để bạn thực hiện.

Đây là mẫu chung cho nhiều dự án Swift và Objective-C.

+0

Tôi đã cố gắng thực hiện điều gì đó tương tự trong Swift. Tuy nhiên, tôi gặp lỗi trình biên dịch trên "My UIGestureRecgonizerSubclass" vì nó không thực hiện các phương thức (bắt buộc) được khai báo trong giao thức "My UIGestureRecognizer". –

+1

Bạn phải thực hiện các phương thức được yêu cầu nếu bạn quyết định thực hiện theo một giao thức trừ khi các phương thức đó là '@ optional'. – Schemetrical

+0

Bạn chính xác. Tuy nhiên, tôi muốn tránh các lớp 'lá' cụ thể, trong đó các phương thức tùy chọn từ Giao thức không được thực hiện. Điều này sẽ dẫn đến các cuộc gọi 'respondToSelector' - một mã kinh dị theo quan điểm khiêm nhường của tôi. –

0

Một cách để bạn có thể làm được điều này, theo đề nghị của @BobDickinson trong Abstract functions in Swift Language, như sau:

protocol Animal { 
    var eyeCount: Int { get } 
    func makeSound() 
} 

// Note - `Mammal` doesn't conform to `Animal`. 
class Mammal { 
    let eyeCount = 2 
} 

// Only `Cat` and `Dog` conform to `Animal`, so only they need to implement `makeSound`. 
class Dog: Mammal, Animal { 
    func makeSound() { 
     println("Woof") 
    } 
} 

class Cat: Mammal, Animal { 
    func makeSound() { 
     println("Meow") 
    } 
} 
+0

Đó là một giải pháp tốt đẹp. Tuy nhiên, tôi thà tránh kế thừa động vật có vú và động vật một cách riêng biệt.Động vật có vú là một Động vật, và do đó mọi lớp con của Động vật có vú cũng nên triển khai Động vật. –

+0

Ngoài ra, vì lợi ích trừu tượng, giả sử ** eyeCount ** là một hàm chứ không phải là thuộc tính có trình thu thập dữ liệu. Đó là một sự lựa chọn nghèo nàn của một ví dụ. –

3

Bạn có thể sử dụng Nghị định thư Extensions để có được những hành vi chính xác giống như với lớp Abstract: Kiểm tra nếu phương pháp trừu tượng được thực hiện trong các lớp con tại thời gian biên dịch.

protocol Entity { 
    // MARK: - Abstract methods 
    func filename() -> String 

    // MARK: - Traits 
    func saveData(data: NSArray) 
} 

extension Entity { 
    func saveData(data: NSArray) { 
     // Do something and call: 
     let filename = filename() 
    } 
} 

Bây giờ bạn có thể thực hiện các giao thức Entity trên Subclass và trình biên dịch sẽ buộc bạn phải thực hiện filename() trong khi phương pháp saveData() đã được thực hiện.

+1

Tốt, nhưng trong một lớp trừu tượng tôi có thể xác định các biến và sử dụng chúng trong funcs. Ở đây tôi sẽ phải khai báo các biến trong mỗi lớp thực hiện giao thức và truy cập chúng từ phần mở rộng thông qua các phương thức accessor, hoặc có cách nào tốt hơn không? – AmigoNico

+0

Tiện ích mở rộng không thể lưu trữ thuộc tính, vì vậy phương pháp này khá hạn chế. – Raphael

Các vấn đề liên quan