ฟังก์ชันนามธรรมในภาษา Swift


127

ฉันต้องการสร้างฟังก์ชันนามธรรมในภาษาที่รวดเร็ว เป็นไปได้ไหม?

class BaseClass {
    func abstractFunction() {
        // How do I force this function to be overridden?
    }
}

class SubClass : BaseClass {
    override func abstractFunction() {
        // Override
    }
}

คำถามนี้ค่อนข้างใกล้เคียงกับคำถามอื่น ๆ ของคุณแต่คำตอบที่นี่ดูเหมือนจะดีกว่าเล็กน้อย
David Berry

คำถามคล้ายกัน แต่วิธีแก้ปัญหาแตกต่างกันมากเนื่องจากคลาสนามธรรมมีกรณีการใช้งานที่แตกต่างจากฟังก์ชันนามธรรม
kev

อ๋อทำไมฉันไม่โหวตให้ปิด แต่คำตอบไม่มีประโยชน์นอกจาก "คุณทำไม่ได้" ที่นี่คุณจะได้คำตอบที่ดีที่สุด :)
David Berry

คำตอบ:


198

ไม่มีแนวคิดเรื่องนามธรรมใน Swift (เช่น Objective-C) แต่คุณสามารถทำได้:

class BaseClass {
    func abstractFunction() {
        preconditionFailure("This method must be overridden") 
    } 
}

class SubClass : BaseClass {
     override func abstractFunction() {
         // Override
     } 
}

13
assert(false, "This method must be overriden by the subclass")หรือคุณสามารถใช้
Erik

20
หรือfatalError("This method must be overridden")
nathan

5
การยืนยันจะต้องใช้คำสั่ง return เมื่อเมธอดมีชนิด return ฉันคิดว่า fatalError () ดีกว่าด้วยเหตุผลนี้
André Fratelli

6
นอกจากนี้ยังมีpreconditionFailure("Bla bla bla")ใน Swift ซึ่งจะดำเนินการในรุ่นที่วางจำหน่ายและไม่จำเป็นต้องมีคำสั่งส่งคืน แก้ไข: เพิ่งค้นพบว่าวิธีการนี้โดยทั่วไปจะมีค่าเท่ากับfatalError()แต่มันเป็นวิธีการที่เหมาะสมมากขึ้น (เอกสารที่ดีขึ้นแนะนำในสวิฟท์พร้อมกับprecondition(), assert()และassertionFailure()อ่านที่นี่ )
Kametrixom

2
จะเกิดอะไรขึ้นถ้าฟังก์ชันมีประเภทการส่งคืน
LoveMeow

36

สิ่งที่คุณต้องการไม่ใช่คลาสพื้นฐาน แต่เป็นโปรโตคอล

protocol MyProtocol {
    func abstractFunction()
}

class MyClass : MyProtocol {
    func abstractFunction() {
    }
}

หากคุณไม่ได้ใส่ abstractFunction ในคลาสของคุณมันเป็นข้อผิดพลาด

หากคุณยังต้องการคลาสพื้นฐานสำหรับพฤติกรรมอื่น ๆ คุณสามารถทำได้:

class MyClass : BaseClass, MyProtocol {
    func abstractFunction() {
    }
}

23
วิธีนี้ใช้ไม่ได้ผล หาก BaseClass ต้องการเรียกใช้ฟังก์ชันว่างเหล่านั้นก็จะต้องใช้โปรโตคอลจากนั้นจึงใช้ฟังก์ชัน นอกจากนี้คลาสย่อยยังไม่ถูกบังคับให้ใช้ฟังก์ชันในเวลาคอมไพล์และคุณไม่ได้บังคับให้ใช้โปรโตคอลด้วย
bandejapaisa

5
นั่นคือประเด็นของฉัน .... BaseClass ไม่มีส่วนเกี่ยวข้องกับโปรโตคอลและการทำหน้าที่เหมือนคลาสนามธรรมมันควรมีอะไรเกี่ยวข้องกับมัน เพื่อชี้แจงสิ่งที่ฉันเขียน "หาก BaseClass ต้องการเรียกใช้ฟังก์ชันว่างเหล่านั้นก็จะต้องใช้โปรโตคอลซึ่งจะบังคับให้คุณใช้ฟังก์ชันซึ่งจะนำไปสู่คลาสย่อยที่ไม่ถูกบังคับให้ใช้ฟังก์ชันในเวลาคอมไพล์ เพราะ BaseClass ทำไปแล้ว ".
bandejapaisa

4
นั่นขึ้นอยู่กับว่าคุณกำหนด "นามธรรม" อย่างไร จุดรวมของฟังก์ชันนามธรรมคือคุณสามารถวางไว้ในคลาสที่สามารถทำสิ่งต่างๆในคลาสปกติได้ ตัวอย่างเช่นเหตุผลที่ฉันต้องการเป็นเพราะฉันต้องการกำหนดสมาชิกแบบคงที่ซึ่งดูเหมือนว่าคุณจะทำกับโปรโตคอลไม่ได้ ดังนั้นมันจึงยากสำหรับฉันที่จะเห็นว่าสิ่งนี้ตรงกับจุดที่เป็นประโยชน์ของฟังก์ชันนามธรรม
Puppy

3
ฉันคิดว่าข้อกังวลเกี่ยวกับความคิดเห็นที่ได้รับการโหวตด้านบนคือสิ่งนี้ไม่เป็นไปตามหลักการ Liskov Substitution ซึ่งระบุว่า "วัตถุในโปรแกรมควรสามารถแทนที่ได้ด้วยอินสแตนซ์ของชนิดย่อยโดยไม่ต้องเปลี่ยนแปลงความถูกต้องของโปรแกรมนั้น" สันนิษฐานว่าเป็นประเภทนามธรรม สิ่งนี้มีความสำคัญอย่างยิ่งในกรณีของรูปแบบ Factory Method ซึ่งคลาสฐานมีหน้าที่สร้างและจัดเก็บคุณสมบัติที่มาจากคลาสย่อย
David James

2
คลาสฐานนามธรรมและโปรโตคอลไม่ใช่สิ่งเดียวกัน
Shoerob

29

ฉันโอนรหัสจำนวนพอสมควรจากแพลตฟอร์มที่รองรับคลาสพื้นฐานแบบนามธรรมไปยัง Swift และทำงานในสิ่งนี้เป็นจำนวนมาก หากสิ่งที่คุณต้องการอย่างแท้จริงคือการทำงานของคลาสพื้นฐานที่เป็นนามธรรมนั่นหมายความว่าคลาสนี้ทำหน้าที่เป็นทั้งการใช้งานฟังก์ชันคลาสที่ใช้ร่วมกัน (มิฉะนั้นจะเป็นเพียงอินเทอร์เฟซ / โปรโตคอล) และจะกำหนดวิธีการที่ต้องดำเนินการโดย คลาสที่ได้รับ

ในการทำเช่นนั้นใน Swift คุณจะต้องมีโปรโตคอลและคลาสพื้นฐาน

protocol Thing
{
    func sharedFunction()
    func abstractFunction()
}

class BaseThing
{
    func sharedFunction()
    {
        println("All classes share this implementation")
    }
}

โปรดสังเกตว่าคลาสพื้นฐานใช้เมธอดที่ใช้ร่วมกัน แต่ไม่ได้ใช้โปรโตคอล (เนื่องจากไม่ได้ใช้วิธีการทั้งหมด)

จากนั้นในคลาสที่ได้รับ:

class DerivedThing : BaseThing, Thing 
{
    func abstractFunction() 
    {
        println("Derived classes implement this");
    }
}

คลาสที่ได้รับสืบทอด sharedFunction จากคลาสฐานช่วยให้ตอบสนองส่วนนั้นของโปรโตคอลและโปรโตคอลยังคงต้องการคลาสที่ได้รับมาเพื่อใช้ abstractFunction

ข้อเสียที่แท้จริงเพียงอย่างเดียวของวิธีนี้คือเนื่องจากคลาสพื้นฐานไม่ได้ใช้โปรโตคอลหากคุณมีเมธอดคลาสพื้นฐานที่ต้องการเข้าถึงคุณสมบัติ / วิธีการโปรโตคอลคุณจะต้องแทนที่สิ่งนั้นในคลาสที่ได้รับและจากนั้นการเรียกใช้ คลาสพื้นฐาน (ผ่านซุปเปอร์) selfเพื่อให้คลาสฐานมีอินสแตนซ์ของโปรโตคอลที่จะทำงาน

ตัวอย่างเช่นสมมติว่า sharedFunction จำเป็นในการเรียก abstractFunction โปรโตคอลจะยังคงเหมือนเดิมและชั้นเรียนจะมีลักษณะดังนี้:

class BaseThing
{
    func sharedFunction(thing: Thing)
    {
        println("All classes share this implementation")
        thing.abstractFunction()
    }
}

class DerivedThing : BaseThing, Thing 
{
    func sharedFunction()
    {
        super.sharedFunction(self)
    }

    func abstractFunction() 
    {
        println("Derived classes implement this");
    }
}

ตอนนี้ sharedFunction จากคลาสที่ได้รับนั้นเป็นที่พอใจในส่วนนั้นของโปรโตคอล แต่คลาสที่ได้รับยังคงสามารถแบ่งปันตรรกะของคลาสพื้นฐานได้อย่างตรงไปตรงมา


4
ความเข้าใจที่ดีและความลึกซึ้งที่ดีเกี่ยวกับ "คลาสฐานไม่ใช้โปรโตคอล" ... ซึ่งเป็นประเด็นของคลาสนามธรรม ฉันงงงันว่าฟีเจอร์ OO พื้นฐานนี้หายไป แต่แล้วอีกครั้งฉันได้รับการเลี้ยงดูบน Java
Dan Rosenstark

2
อย่างไรก็ตามการนำไปใช้งานไม่อนุญาตให้ใช้เมธอด Template Function อย่างราบรื่นโดยที่เมธอด template เรียกใช้วิธีนามธรรมจำนวนมากที่ใช้ในคลาสย่อย ในกรณีนี้คุณควรเขียนทั้งสองเป็นวิธีการปกติในคลาสระดับสูงเป็นวิธีการในโปรโตคอลและอีกครั้งเป็นการนำไปใช้ในคลาสย่อย ในทางปฏิบัติคุณต้องเขียนสิ่งเดียวกันสามครั้งและพึ่งพาการตรวจสอบการลบล้างเท่านั้นเพื่อให้แน่ใจว่าจะไม่สะกดผิดที่เป็นหายนะ! ฉันหวังเป็นอย่างยิ่งว่าตอนนี้ Swift จะเปิดให้นักพัฒนาซอฟต์แวร์ฟังก์ชั่นนามธรรมที่สมบูรณ์จะได้รับการแนะนำ
Fabrizio Bartolomucci

1
สามารถประหยัดเวลาและรหัสได้มากโดยการแนะนำคำหลัก "ป้องกัน"
Shoerob

23

วิธีนี้ดูเหมือนจะเป็นวิธีที่ "เป็นทางการ" วิธีที่ Apple จัดการกับวิธีนามธรรมใน UIKit ลองดูUITableViewControllerและวิธีการใช้งานUITableViewDelegateและวิธีที่จะทำงานร่วมกับdelegate = selfหนึ่งในสิ่งแรกที่คุณทำคือการเพิ่มบรรทัด: นั่นคือเคล็ดลับ

1. ใส่วิธีนามธรรมในโปรโตคอล
protocol AbstractMethodsForClassX {
    func abstractMethod() -> String
}
2. เขียนคลาสพื้นฐานของคุณ
/// It takes an implementation of the protocol as property (same like the delegate in UITableViewController does)
/// And does not implement the protocol as it does not implement the abstract methods. It has the abstract methods available in the `delegate`
class BaseClassX {
    var delegate: AbstractMethodsForClassX!

    func doSomethingWithAbstractMethod() -> String {
        return delegate.abstractMethod() + " - And I believe it"
    }
}
3. เขียน Subclass (es)
/// First and only additional thing you have to do, you must set the "delegate" property
class ClassX: BaseClassX, AbstractMethodsForClassX {
    override init() {
        super.init()
        delegate = self
    }

    func abstractMethod() -> String {return "Yes, this works!"}
}
นี่คือวิธีที่คุณใช้ทั้งหมดนั้น
let x = ClassX()
x.doSomethingWithAbstractMethod()

ตรวจสอบกับ Playground เพื่อดูผลลัพธ์

ข้อสังเกตบางประการ

  • ประการแรกมีคำตอบมากมายแล้ว ฉันหวังว่าจะมีใครสักคนที่สามารถค้นพบสิ่งนี้ได้
  • คำถามคือเพื่อค้นหารูปแบบที่ใช้:
    • คลาสเรียกเมธอดที่ต้องนำไปใช้ในคลาสย่อยที่ได้รับมา (แทนที่)
    • ในกรณีที่ดีที่สุดถ้าเมธอดไม่ถูกแทนที่ในคลาสย่อยให้รับข้อผิดพลาดระหว่างเวลาคอมไพล์
  • สิ่งที่เกี่ยวกับวิธีนามธรรมก็คือพวกมันเป็นส่วนผสมของนิยามอินเทอร์เฟซและเป็นส่วนหนึ่งของการนำไปใช้จริงในคลาสพื้นฐาน ทั้งสองอย่างในเวลาเดียวกัน. เนื่องจากความรวดเร็วเป็นสิ่งใหม่มากและมีการกำหนดไว้อย่างสะอาดตาจึงไม่มีความสะดวกสบายเช่นนี้ แต่เป็นแนวคิดที่ "ไม่สะอาด" (ยัง)
  • สำหรับฉัน (ชายชราชาวชวาที่น่าสงสาร) ปัญหานี้มีวิวัฒนาการเป็นระยะ ๆ ฉันได้อ่านคำตอบทั้งหมดในโพสต์นี้แล้วและคราวนี้ฉันคิดว่าฉันพบรูปแบบที่ดูเป็นไปได้ - อย่างน้อยสำหรับฉัน
  • อัปเดต : ดูเหมือนว่าอุปกรณ์ UIKit ที่ Apple จะใช้รูปแบบเดียวกัน UITableViewControllerดำเนินการUITableViewDelegateแต่ยังคงต้องมีการลงทะเบียนในฐานะผู้รับมอบสิทธิ์โดยการตั้งค่าdelegateคุณสมบัติอย่างชัดเจน
  • ทั้งหมดนี้ผ่านการทดสอบบน Playground ของ Xcode 7.3.1

อาจจะไม่สมบูรณ์แบบเพราะฉันมีปัญหาในการซ่อนอินเทอร์เฟซของคลาสจากคลาสอื่น ๆ แต่นี่ก็เพียงพอแล้วสำหรับสิ่งที่ฉันต้องการในการใช้ Factory Method แบบคลาสสิกใน Swift
Tomasz Nazarenko

อืมม ฉันชอบรูปลักษณ์ของคำตอบนี้มากกว่า แต่ฉันก็ไม่แน่ใจว่ามันเหมาะ กล่าวคือฉันมี ViewController ที่สามารถแสดงข้อมูลสำหรับวัตถุประเภทต่างๆจำนวนหนึ่ง แต่จุดประสงค์ของการแสดงข้อมูลนั้นเหมือนกันทั้งหมดโดยใช้วิธีการเดียวกันทั้งหมด แต่การดึงและรวบรวมข้อมูลต่างกันตามวัตถุ . ดังนั้นฉันจึงมีคลาสตัวแทนหลักสำหรับ VC นี้และคลาสย่อยของผู้รับมอบสิทธิ์นั้นสำหรับอ็อบเจ็กต์แต่ละประเภท ฉันสร้างอินสแตนซ์ผู้รับมอบสิทธิ์ตามวัตถุที่ส่งเข้ามาในตัวอย่างหนึ่งแต่ละออบเจ็กต์มีความคิดเห็นที่เกี่ยวข้องเก็บ
Jake T.

ดังนั้นฉันจึงมีgetCommentsวิธีการในตัวแทนหลัก มีประเภทความคิดเห็นไม่กี่ประเภทและฉันต้องการความคิดเห็นที่เกี่ยวข้องกับแต่ละออบเจ็กต์ ดังนั้นฉันต้องการให้คลาสย่อยไม่คอมไพล์เมื่อฉันทำdelegate.getComments()ถ้าฉันไม่แทนที่เมธอดนั้น VC เพิ่งรู้ว่ามีการParentDelegateเรียกวัตถุdelegateหรือBaseClassXในตัวอย่างของคุณ แต่BaseClassXไม่มีวิธีการที่เป็นนามธรรม ฉันต้องการให้ VC ทราบเป็นพิเศษว่ากำลังใช้ไฟล์SubclassedDelegate.
Jake T.

ฉันหวังว่าฉันจะเข้าใจคุณถูกต้อง หากไม่ต้องการถามคำถามใหม่ที่คุณสามารถเพิ่มรหัสได้หรือไม่? ฉันคิดว่าคุณต้องมี if-statement ในการตรวจสอบ‚ doSomethingWithAbstractMethod ว่ามีการตั้งค่ามอบหมายหรือไม่
jboi

ควรค่าแก่การกล่าวถึงว่าการมอบหมายตนเองให้กับผู้รับมอบสิทธิ์จะสร้างวงจรการรักษา อาจจะดีกว่าถ้าประกาศว่าอ่อนแอ (ซึ่งโดยทั่วไปเป็นความคิดที่ดีสำหรับผู้ได้รับมอบหมาย)
Enricoza

7

วิธีหนึ่งในการทำเช่นนี้คือใช้การปิดทางเลือกที่กำหนดไว้ในคลาสพื้นฐานและเด็ก ๆ สามารถเลือกที่จะใช้หรือไม่ก็ได้

class BaseClass {
    var abstractClosure?:(()->())?
    func someFunc()
    {
        if let abstractClosure=abstractClosure
        {
            abstractClosure()
        }
    } 
}

class SubClass : BaseClass {
    init()
    {
        super.init()
        abstractClosure={ ..... }
    }
}

สิ่งที่ฉันชอบเกี่ยวกับแนวทางนี้คือฉันไม่จำเป็นต้องจำไว้ว่าต้องใช้โปรโตคอลในคลาสที่สืบทอด ฉันมีคลาสพื้นฐานสำหรับ ViewControllers ของฉันและนี่คือวิธีที่ฉันบังคับใช้ฟังก์ชันเฉพาะของ ViewController (เช่นแอปเริ่มทำงานเป็นต้น) ซึ่งอาจถูกเรียกใช้โดยฟังก์ชันคลาสพื้นฐาน
ProgrammierTier

6

ฉันรู้ว่าฉันมาสายและอาจจะใช้ประโยชน์จากการเปลี่ยนแปลงที่เกิดขึ้น ขอโทษด้วยกับเรื่องนั้น.

ไม่ว่าในกรณีใดฉันต้องการให้คำตอบของฉันเพราะฉันชอบทำการทดสอบและวิธีแก้ปัญหาfatalError()คือ AFAIK ไม่สามารถทดสอบได้และข้อยกเว้นนั้นทดสอบได้ยากกว่ามาก

ฉันขอแนะนำให้ใช้swiftyมากขึ้นวิธี เป้าหมายของคุณคือการกำหนดสิ่งที่เป็นนามธรรมที่มีรายละเอียดร่วมกัน แต่ยังไม่ได้กำหนดไว้ทั้งหมดนั่นคือวิธีนามธรรม ใช้โปรโตคอลที่กำหนดวิธีการที่คาดหวังทั้งหมดในสิ่งที่เป็นนามธรรมทั้งวิธีที่กำหนดไว้และวิธีที่ไม่ได้กำหนด จากนั้นสร้างส่วนขยายโปรโตคอลที่ใช้วิธีการที่กำหนดไว้ในกรณีของคุณ สุดท้ายคลาสที่ได้รับจะต้องใช้โปรโตคอลซึ่งหมายถึงวิธีการทั้งหมด แต่คลาสที่เป็นส่วนขยายโปรโตคอลมีการใช้งานอยู่แล้ว

ขยายตัวอย่างของคุณด้วยฟังก์ชันที่เป็นรูปธรรม:

protocol BaseAbstraction {
    func abstractFunction() {
        // How do I force this function to be overridden?
    }
}

extension BaseAbstraction {
    func definedFunction() {
        print("Hello")
}

class SubClass : BaseAbstraction {
    func abstractFunction() {
        // No need to "Override". Just implement.
    }
}

สังเกตว่าการทำเช่นนี้คอมไพเลอร์เป็นเพื่อนของคุณอีกครั้ง หากเมธอดไม่ "ลบล้าง" คุณจะได้รับข้อผิดพลาดในขณะคอมไพล์แทนที่จะเป็นวิธีที่คุณจะได้รับfatalError()หรือข้อยกเว้นที่จะเกิดขึ้นในขณะรันไทม์


1
Imo คำตอบที่ดีที่สุด
Apfelsaft

1
คำตอบที่ดี แต่สิ่งที่เป็นนามธรรมพื้นฐานของคุณไม่สามารถจัดเก็บคุณสมบัติได้
joehinkle11

5

ฉันเข้าใจว่าคุณกำลังทำอะไรอยู่ตอนนี้ฉันคิดว่าคุณควรใช้โปรโตคอลดีกว่า

protocol BaseProtocol {
    func abstractFunction()
}

จากนั้นคุณก็ปฏิบัติตามโปรโตคอล:

class SubClass : BaseProtocol {

    func abstractFunction() {
        // Override
        println("Override")
    }
}

หากคลาสของคุณเป็นคลาสย่อยโปรโตคอลจะเป็นไปตาม Superclass:

class SubClass: SuperClass, ProtocolOne, ProtocolTwo {}

3

การใช้assertคำสำคัญเพื่อบังคับใช้วิธีนามธรรม:

class Abstract
{
    func doWork()
    {
        assert(false, "This method must be overriden by the subclass")
    }
}

class Concrete : Abstract
{
    override func doWork()
    {
        println("Did some work!")
    }
}

let abstract = Abstract()
let concrete = Concrete()

abstract.doWork()    // fails
concrete.doWork()    // OK

อย่างไรก็ตามอย่างที่Steve Waddicorพูดถึงคุณอาจต้องการprotocolแทน


ประโยชน์ของวิธีนามธรรมคือการตรวจสอบจะทำในเวลารวบรวม
Fabrizio Bartolomucci

อย่าใช้การยืนยันเนื่องจากในการเก็บถาวรคุณจะได้รับข้อผิดพลาด 'Missing return in a function expected to return' ใช้ fatalError ("วิธีนี้ต้องถูกลบล้างโดยคลาสย่อย")
Zaporozhchenko Oleksandr

2

ฉันเข้าใจคำถามและกำลังมองหาวิธีแก้ปัญหาเดียวกัน โปรโตคอลไม่เหมือนกับวิธีนามธรรม

ในโปรโตคอลคุณต้องระบุว่าคลาสของคุณสอดคล้องกับโปรโตคอลดังกล่าววิธีนามธรรมหมายความว่าคุณต้องแทนที่วิธีการดังกล่าว

กล่าวอีกนัยหนึ่งโปรโตคอลเป็นตัวเลือกประเภทหนึ่งคุณต้องระบุคลาสพื้นฐานและโปรโตคอลหากคุณไม่ระบุโปรโตคอลคุณก็ไม่จำเป็นต้องแทนที่วิธีการดังกล่าว

วิธีนามธรรมหมายความว่าคุณต้องการคลาสพื้นฐาน แต่ต้องใช้วิธีการของคุณเองหรือสองวิธีซึ่งไม่เหมือนกัน

ฉันต้องการพฤติกรรมแบบเดียวกันนั่นคือเหตุผลที่ฉันกำลังมองหาวิธีแก้ปัญหา ฉันเดาว่า Swift ไม่มีคุณสมบัติดังกล่าว


1

มีทางเลือกอื่นสำหรับปัญหานี้แม้ว่าจะยังมีข้อเสียเมื่อเทียบกับข้อเสนอของ @jaumard ต้องมีคำสั่งส่งคืน แม้ว่าฉันจะพลาดจุดที่ต้องการเพราะมันประกอบด้วยข้อยกเว้นโดยตรง:

class AbstractMethodException : NSException {

    init() {
        super.init(
            name: "Called an abstract method",
            reason: "All abstract methods must be overriden by subclasses",
            userInfo: nil
        );
    }
}

แล้ว:

class BaseClass {
    func abstractFunction() {
        AbstractMethodException.raise();
    }
}

สิ่งที่เกิดขึ้นหลังจากนั้นก็ไม่สามารถเข้าถึงได้ดังนั้นฉันจึงไม่เห็นว่าทำไมต้องบังคับให้ส่งคืน


คุณหมายถึงAbstractMethodException().raise()?
Joshcodes

เอ่อ ... ฉันไม่สามารถทดสอบได้ในตอนนี้ แต่ถ้ามันได้ผลมากกว่าใช่
André Fratelli

1

ฉันไม่รู้ว่ามันจะมีประโยชน์หรือเปล่า แต่ฉันมีปัญหาคล้าย ๆ กันกับวิธีการที่เป็นนามธรรมขณะพยายามสร้างเกม SpritKit สิ่งที่ฉันต้องการคือคลาส Animal นามธรรมที่มีเมธอดเช่น move (), run () และอื่น ๆ แต่เด็กในชั้นเรียนควรระบุชื่อสไปรท์ (และฟังก์ชันอื่น ๆ ) ดังนั้นฉันจึงทำสิ่งนี้ (ทดสอบสำหรับ Swift 2):

import SpriteKit

// --- Functions that must be implemented by child of Animal
public protocol InheritedAnimal
{
    func walkSpriteNames() -> [String]
    func runSpriteNames() -> [String]
}


// --- Abstract animal
public class Animal: SKNode
{
    private let inheritedAnimal: InheritedAnimal

    public init(inheritedAnimal: InheritedAnimal)
    {
        self.inheritedAnimal = inheritedAnimal
        super.init()
    }

    public required init?(coder aDecoder: NSCoder)
    {
        fatalError("NSCoding not supported")
    }

    public func walk()
    {
        let sprites = inheritedAnimal.walkSpriteNames()
        // create animation with walking sprites...
    }

    public func run()
    {
        let sprites = inheritedAnimal.runSpriteNames()
        // create animation with running sprites
    }
}


// --- Sheep
public class SheepAnimal: Animal
{
    public required init?(coder aDecoder: NSCoder)
    {
        fatalError("NSCoding not supported")
    }

    public required init()
    {
        super.init(inheritedAnimal: InheritedAnimalImpl())
    }

    private class InheritedAnimalImpl: InheritedAnimal
    {
        init() {}

        func walkSpriteNames() -> [String]
        {
            return ["sheep_step_01", "sheep_step_02", "sheep_step_03", "sheep_step_04"]
        }

        func runSpriteNames() -> [String]
        {
            return ["sheep_run_01", "sheep_run_02"]
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.