ฉันควรใช้ Classes หรือ Structs สำหรับองค์ประกอบที่จัดเก็บไว้ใน Swift Array

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

ฉันได้รับการสนับสนุนให้ใช้ Struct's เมื่อเป็นไปได้ อย่างไรก็ตามกรณีการใช้งานที่ค่อนข้างง่าย (ด้านล่าง) กำลังผลักดันให้ฉันใช้คลาส (ประเภทอ้างอิง)

ฉันควรใช้ Classes สำหรับ Game และ/หรือ Player

โปรดดูตัวอย่างโค้ดด้านล่าง .. พร้อมด้วย UnitTest

สรุปการทดสอบ
• สร้างเกม
• สร้างผู้เล่นสองคน
• เพิ่มผู้เล่นทั้งสองคนในเกม
• ส่งข้อความถึงเกมเพื่อลดผู้เล่น
• เกมวนซ้ำผ่านคอลเลกชัน (players)
• ค้นหาผู้เล่นและส่งข้อความคะแนนลดลง
การทดสอบล้มเหลว - คะแนนของผู้เล่นไม่ตามที่คาดไว้ (60 และ 70 ตามลำดับ)

struct Game {
    fileprivate(set) var players = [Player]()
}

extension Game {
    mutating func addPlayer(_ player: Player) {
        players.append(player)
    }

    mutating func decrementPlayer(_ decrementPlayer: Player, byScore: Int) {
        for var player in players {
            if player == decrementPlayer {
                player.decrementScore(by: byScore)
            }
        }
    }
}


struct Player {
    var name: String
    var score: Int

    init(name: String, score: Int) {
        self.name = name
        self.score = score
    }

    mutating func decrementScore(by byScore: Int) {
        self.score -= byScore
    }
}

extension Player: Equatable {
    public static func ==(lhs: Player, rhs: Player) -> Bool {
        return lhs.name == rhs.name
    }
}

class GameTests: XCTestCase {

var sut: Game!

func testDecrementingPlayerScores_isReflectedCorrectlyInGamePlayers() {
        sut = Game()
        let player1 = Player(name: "Ross", score: 100)
        let player2 = Player(name: "Mary", score: 100)

        sut.addPlayer(player1)
        sut.addPlayer(player2)
        XCTAssertEqual(2, sut.players.count)    // Passes

        sut.decrementPlayer(player1, byScore: 40)
        sut.decrementPlayer(player2, byScore: 30)
        XCTAssertEqual(60, sut.players[0].score) // Fails - score is 100 .. expecting 60
        XCTAssertEqual(70, sut.players[1].score) // Fails - score is 100 .. expecting 70
    }
}

person MDMonty    schedule 12.02.2018    source แหล่งที่มา
comment
โปรดเพิ่มรหัสทดสอบของคุณด้วย   -  person picciano    schedule 12.02.2018
comment
จะทำ .. ขอบคุณ @picciano แยก XCTestCase ออกจากตัวอย่างโค้ดที่มีอยู่   -  person MDMonty    schedule 12.02.2018


คำตอบ (3)


ฉันสนับสนุนให้ใช้ Struct's เมื่อเป็นไปได้

ใช่นั่นเป็นปัญหา คุณควรได้รับการสนับสนุนให้ใช้ structs ในกรณีที่เหมาะสม โดยทั่วไปแล้ว ฉันพบว่า structs ไม่ได้เหมาะสมเท่าที่แฟชั่นกำหนดเสมอไป

ปัญหาของคุณที่นี่คือคำสั่ง for var player ... สร้างสำเนาที่ไม่แน่นอนของผู้เล่นแต่ละคนในขณะที่ทำซ้ำและแก้ไขสำเนา หากคุณต้องการใช้ structs ต่อไป คุณอาจจำเป็นต้องนำแนวทางที่ใช้งานได้ดีกว่ามาใช้

mutating func decrementPlayer(_ decrementPlayer: Player, byScore: Int) {
    players = players.map {
        return $0 == decrementPlayer ? $0.scoreDecrementedBy(by: byScore) : $0
    }
}

หรือวิธีดั้งเดิมกว่า (และเกือบจะมีประสิทธิภาพมากกว่า) ก็คือการค้นหาดัชนีของผู้เล่นที่คุณต้องการ

mutating func decrementPlayer(_ decrementPlayer: Player, byScore: Int) {
    if let index = players.index(of: decrementPlayer)
    {
        players[index].decrementScore(by: byScore)
    }
}
person JeremyP    schedule 12.02.2018
comment
ฉันต้องการที่จะยอมรับคำตอบของคุณ .. ขอบคุณ แม้ว่าหลังจากให้ข้อคิดและคำแนะนำแก่เพื่อนร่วมงานแล้ว ฉันรู้สึกว่าวิธี decreatScore ควรส่งคืนโครงสร้าง Player ใหม่ จากนั้นทำการมอบหมายกลับเข้าไปในอาร์เรย์ของผู้เล่น (โดยใช้ดัชนีที่คุณอธิบายข้างต้น) ... ขอขอบคุณที่สละเวลาและช่วยเหลือ - person MDMonty; 14.02.2018
comment
@MDMonty ใช่ฉันคิดว่าวิธีการทำงานที่ดูฉลาดกว่าจะมีประสิทธิภาพน้อยลงเนื่องจากมันสร้างอาร์เรย์ใหม่ทั้งหมด ฉันคิดว่าวิธีการจัดทำดัชนีของฉันข้างต้นจะเร็วที่สุด แต่วิธีการของคุณอาจจะใกล้เคียงกันหลังจากการเพิ่มประสิทธิภาพ - person JeremyP; 14.02.2018

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

นั่นคือสิ่งที่คุณต้องทำ เนื่องจากโครงสร้างไม่สามารถเปลี่ยนแปลงได้ หากคุณต้องการที่จะกลายพันธุ์วัตถุที่มีอยู่ (ภายในอาร์เรย์) คุณต้องมีคลาส

person matt    schedule 12.02.2018

คำถามที่ควรถามเมื่อตัดสินใจระหว่างประเภทค่า (structs) และประเภทการอ้างอิง (คลาส) คือถ้าเหมาะสมที่จะมีอินสแตนซ์ที่ซ้ำกัน

เช่น เอาเลข 5 ซึ่งก็คือ Int คุณสามารถคัดลอกได้หลายครั้ง มีราคาถูก (ตามโครงสร้าง) และไม่ก่อให้เกิดปัญหาหากคัดลอก

ทีนี้ลองพิจารณา FileHandle มันสามารถเป็นโครงสร้างได้หรือไม่? ใช่. มันควรจะเป็นโครงสร้างหรือไม่? ไม่ การสร้างโครงสร้างหมายความว่าหมายเลขอ้างอิงจะถูกคัดลอกเมื่อใดก็ตามที่ส่งผ่านเป็นอาร์กิวเมนต์หรือจัดเก็บเป็นคุณสมบัติ หมายความว่าทุกคนจะมีการอ้างอิงที่แตกต่างกันไปยังหมายเลขอ้างอิงนั้น (จงใจละเว้นการคัดลอกเมื่อเขียน) หากเจ้าของข้อมูลอ้างอิงรายหนึ่งตัดสินใจปิดจุดจับ การดำเนินการนี้จะทำให้การอ้างอิง FileHandle อื่นๆ ทั้งหมดเป็นโมฆะโดยทางอ้อม ซึ่งอาจกระตุ้นให้เกิดพฤติกรรมที่ไม่คาดคิด

ในกรณีเฉพาะของคุณ ฉันจะบอกว่าให้ Player เป็นคลาส ฉันถือว่าผู้เล่นจะมีเอฟเฟกต์ UI บางอย่างเช่นกัน ดังนั้นจึงเหมาะสมที่จะเป็นประเภทอ้างอิง อย่างไรก็ตาม หากใช้ Player เพื่อจุดประสงค์ทางสถิติเท่านั้น ให้สร้างเป็นโครงสร้าง

person Cristik    schedule 17.02.2018