มีวิธีในการสร้างคลาสนามธรรมในภาษา Swift หรือนี่เป็นข้อ จำกัด เช่นเดียวกับ Objective-C หรือไม่? ฉันต้องการสร้างคลาสนามธรรมเปรียบได้กับสิ่งที่ Java กำหนดเป็นคลาสนามธรรม
มีวิธีในการสร้างคลาสนามธรรมในภาษา Swift หรือนี่เป็นข้อ จำกัด เช่นเดียวกับ Objective-C หรือไม่? ฉันต้องการสร้างคลาสนามธรรมเปรียบได้กับสิ่งที่ Java กำหนดเป็นคลาสนามธรรม
คำตอบ:
ไม่มีคลาสนามธรรมใน Swift (เหมือนกับ Objective-C) ทางออกที่ดีที่สุดของคุณคือการใช้โปรโตคอลซึ่งเป็นเหมือนส่วนต่อประสาน Java
ด้วย Swift 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
โปรดสังเกตว่านี่เป็นการให้ "abstract class" เช่นเดียวกับฟีเจอร์สำหรับ structs แต่คลาสสามารถใช้โปรโตคอลเดียวกันได้
โปรดสังเกตด้วยว่าทุกคลาสหรือโครงสร้างที่ใช้โพรโทคอลพนักงานจะต้องประกาศคุณสมบัติประจำปีประจำปีอีกครั้ง
สิ่งสำคัญที่สุดคือการแจ้งให้ทราบว่าไม่มีการจัดส่งแบบไดนามิก เมื่อlogSalary
มีการเรียกใช้งานอินสแตนซ์ที่จัดเก็บตามที่SoftwareEngineer
เรียกใช้รุ่นที่ถูกแทนที่ของวิธีการ เมื่อlogSalary
ถูกเรียกบนอินสแตนซ์หลังจากที่มันได้รับการโยนไปที่Employee
จะเรียกการดำเนินงานเดิม Software Engineer
(มันไม่ได้แบบไดนามิกส่งไปยังรุ่นแทนที่แม้ว่าอินสแตนซ์เป็นจริง
สำหรับข้อมูลเพิ่มเติมตรวจสอบวิดีโอ WWDC ที่ยอดเยี่ยมเกี่ยวกับคุณสมบัตินั้น: การสร้างแอปที่ดีขึ้นด้วยประเภทค่าใน Swift
protocol Animal { var property : Int { get set } }
. นอกจากนี้คุณยังสามารถออกจากชุดหากคุณไม่ต้องการให้ทรัพย์สินมี setter
func logSalary()
การประกาศโปรโตคอลพนักงานพิมพ์ตัวอย่างสำหรับการโทรทั้งoverridden
logSalary()
นี่คือใน Swift 3.1 ดังนั้นคุณจะได้รับประโยชน์ของ polymorphism วิธีการที่ถูกต้องเรียกว่าในทั้งสองกรณี
โปรดทราบว่าคำตอบนี้มีเป้าหมายที่ Swift 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)
ดังนั้นโดยทั่วไปคุณจะได้รับ:
Drivable
การใช้งานspeed
Drivable
( accelerate
)Drivable
ไม่รับประกันอินสแตนซ์เนื่องจากเป็นเพียงโปรโตคอลแบบจำลองนี้มีลักษณะเหมือนลักษณะความเป็นจริงมากขึ้นซึ่งหมายความว่าคุณสามารถปฏิบัติตามโปรโตคอลจำนวนมากและใช้งานการเริ่มต้นของพวกเขาใด ๆ ในขณะที่มี superclass นามธรรมคุณถูก จำกัด ชั้นลำดับชั้นที่เรียบง่าย
UICollectionViewDatasource
ยังไม่มีความเป็นไปได้ที่จะขยายโปรโตคอลบางตัวอย่างเช่น ฉันต้องการลบต้นแบบสำเร็จรูปทั้งหมดและห่อหุ้มด้วยโพรโทคอล / ส่วนขยายแยกต่างหากจากนั้นใช้ซ้ำหลายคลาส ในความเป็นจริงรูปแบบแม่แบบจะเหมาะที่นี่ แต่ ...
ฉันคิดว่านี่ใกล้เคียงที่สุดของ Java abstract
หรือ C # abstract
:
class AbstractClass {
private init() {
}
}
โปรดทราบว่าเพื่อให้private
ตัวดัดแปลงทำงานคุณต้องกำหนดคลาสนี้ในไฟล์ Swift แยกต่างหาก
แก้ไข:ยังรหัสนี้ไม่อนุญาตให้ประกาศวิธีนามธรรมและบังคับให้ดำเนินการ
วิธีที่ง่ายที่สุดคือใช้การเรียกไปยังfatalError("Not Implemented")
เมธอด abstract (ไม่ใช่ตัวแปร) บนส่วนขยายโปรโตคอล
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
อยู่ในการประกาศโปรโตคอล; มิฉะนั้นสายขัดข้อง
หลังจากที่ฉันพยายามมาหลายสัปดาห์ในที่สุดฉันก็รู้ว่าจะแปลคลาสนามธรรม Java / PHP เป็น Swift:
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 เป็นต้นสิ่งนี้มีประโยชน์หรือไม่
มีวิธีการจำลองคลาสนามธรรมโดยใช้โปรโตคอล นี่คือตัวอย่าง:
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
}
}
อีกวิธีหนึ่งที่คุณสามารถใช้คลาสนามธรรมคือการบล็อก initializer ฉันทำแบบนี้แล้ว:
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")
}
}
}
ฉันพยายามทำให้เป็น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
}
ย้ายการอ้างอิงทั้งหมดไปยังคุณสมบัตินามธรรมและเมธอดของคลาสฐานไปยังการใช้งานส่วนขยายโปรโตคอลซึ่งข้อ จำกัด ของตัวเองไปยังคลาสพื้นฐาน คุณจะสามารถเข้าถึงวิธีการและคุณสมบัติทั้งหมดของคลาสฐาน นอกจากนี้การตรวจสอบคอมไพเลอร์ใช้วิธีนามธรรมและคุณสมบัติในโปรโตคอลสำหรับคลาสที่ได้รับ
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;
}
}
ด้วยข้อ จำกัด ที่ไม่มีการแจกจ่ายแบบไดนามิกคุณสามารถทำสิ่งนี้:
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()