Cara yang baik untuk membuat kelas untuk jenis kartu bermain yang lebih kompleks daripada yang ditemukan di dek standar?

9

Saya sangat baru dalam pemrograman berorientasi objek, dan saya mencoba untuk mulai belajar dengan python dengan membuat permainan kartu sederhana (seperti yang terlihat tradisional!). Saya telah melakukan contoh berikut yang berfungsi dengan baik, dan mengajari saya tentang membuat beberapa instance PlayingCard()kelas untuk membuat instance Deck()kelas:

class PlayingCard(object):
    def __init__(self, suit, val):
        self.suit = suit
        self.value = val

    def print_card(self):
        print("{} of {}".format(self.value, self.suit))

class Deck(object):
    def __init__(self):
        self.playingcards = []
        self.build()

    def build(self):
        for s in ["Spades", "Clubs", "Diamonds", "Hearts"]:
            for v in range(1,14):
                self.playingcards.append(PlayingCard(s,v))

deck = Deck()



Saya ingin membuat sesuatu sekarang dengan kartu yang lebih kompleks, bukan hanya kartu standar 52 (yang memiliki nilai tambah yang bagus). Dek yang ada dalam benak saya adalah permainan kartu Monopoli:

masukkan deskripsi gambar di sini

Ada 3 jenis kartu fundamental - kartu AKSI, KARTU PROPERTI, dan kartu UANG. Kartu aksi melakukan tindakan yang berbeda, kartu properti milik set warna yang berbeda, dan kartu uang dapat memiliki nilai yang berbeda. Selain itu, kartu properti dapat menjadi "wildcard", dan dapat digunakan sebagai bagian dari salah satu dari dua set. Akhirnya, setiap kartu juga memiliki nilai uang yang setara (ditunjukkan di sudut atas setiap kartu). Dalam kartu aksi sewa, kartu hanya dapat berlaku untuk properti warna yang ditunjukkan pada kartu.

Pertanyaan saya umumnya bagaimana menangani situasi seperti ini, dan apa cara yang baik untuk memasukkan kartu-kartu yang berbeda ini dalam program python berbasis kelas? Haruskah saya mempertahankan PlayingCard()kelas tunggal saya , dan hanya memiliki banyak input, seperti PlayingCard(type="PROPERTY", value="3M"). Atau akan lebih baik untuk membuat kelas terpisah seperti ActionPlayingCard(), PropertyPlayingCard(), dll? Atau ada cara yang lebih baik? Seperti yang saya katakan, saya berada di awal pembelajaran saya di sini, dan bagaimana mengatur jenis situasi ini dalam hal desain tingkat yang lebih tinggi.

Terimakasih banyak.

teeeeee
sumber
Jika Anda menemukan bahwa berbagai jenis kartu berbagi beberapa fitur, Anda dapat menggunakan warisan, atau bahkan kelas Abstrak. Anda dapat membaca tentang dan menggunakan Pola Pabrik sehingga Anda dapat lulus jenis kartu dan kelas yang sesuai akan digunakan
Tomerikoo
@Tomerikoo Terima kasih telah menunjukkan itu - Saya telah membaca sedikit tentang pola pabrik yang Anda sebutkan. Seperti yang saya pahami, ini sangat berguna ketika Anda tidak tahu sebelumnya objek mana yang perlu Anda buat (mungkin hanya tahu saat runtime). Namun, karena dalam kasus ini saya tahu seperti apa keseluruhan dek (berapa banyak masing-masing jenis kartu, apa yang mereka lakukan, dll), apakah pola pabrik berlaku di sini?
teeeeee

Jawaban:

3

Ketika Anda mendekati masalah dengan OOP , Anda biasanya ingin memodelkan perilaku dan properti dengan cara yang dapat digunakan kembali, yaitu, Anda harus memikirkan abstraksi dan mengatur hierarki kelas Anda berdasarkan itu.

Saya akan menulis sesuatu seperti berikut:

class Card:
    def __init__(self, money_value=0):
        self.money_value = money_value

class ActionCard(Card):
    def __init__(self, action, money_value=0):
        super().__init__(money_value=money_value)

        self.action = action

class RentActionCard(ActionCard):
    def __init__(self, action, color, money_value=0):
        super().__init__(action, money_value=money_value)

        self.color = color

    def apply(self, property_card):
        if property_card.color != self.color:
            # Don't apply
        # Apply

class PropertyCard(Card):
    def __init__(self, color, money_value=0):
        super().__init__(money_value=money_value)

        self.color = color

class WildcardPropertyCard(PropertyCard):
    def __init__(self, color, money_value=0):
        super().__init__(color, money_value=money_value)

class MoneyCard(Card):
    def __init__(self, money_value=0):
        super().__init__(money_value=money_value)


Karena Python menjadi bahasa yang diketik secara dinamis, OOP sedikit lebih sulit untuk dibenarkan menurut pendapat saya, karena kami hanya bisa mengandalkan pengetikan bebek dan pengikatan dinamis , cara Anda mengatur hierarki Anda kurang penting.

Jika saya memodelkan masalah ini dalam C # misalnya, saya tanpa ragu akan menggunakan hierarki yang ditunjukkan di atas, karena saya dapat mengandalkan polimorfisme untuk mewakili berbagai jenis dan memandu aliran logika saya berdasarkan jenis kartu yang sedang dianalisis.

Beberapa komentar terakhir:

  1. Python memiliki tipe bawaan yang sangat kuat, tetapi sebagian besar waktu menggunakan tipe kustom baru yang membuatnya menjadi lebih mudah.
  2. Anda tidak harus mewarisi objectsejak mengetikkan Python 3 (yang merupakan satu-satunya yang dipertahankan hingga hari ini) mewarisi dari objectsecara default.

Tetapi, pada akhirnya, tidak ada jawaban yang sempurna, cara terbaik adalah mencoba kedua pendekatan dan melihat apa yang lebih nyaman bagi Anda.

alex
sumber
7

Inilah yang kami sebut "keputusan desain". Seringkali cara yang "benar" adalah masalah pendapat. Sebagai seorang pemula, saya pikir akan sangat berguna untuk mencoba kedua implementasi untuk melihat bagaimana mereka bekerja. Akan ada trade off tidak peduli mana yang Anda pilih. Anda harus memutuskan trade off mana yang paling penting. Membuat keputusan semacam ini akan diinformasikan saat Anda memperoleh lebih banyak pengalaman.

Magang Kode
sumber
Ya, terima kasih, saya melakukan persis seperti yang Anda katakan - hanya mencari bimbingan dari orang-orang yang cukup berpengalaman untuk secara naluriah tahu pendekatan yang baik, dan mudah-mudahan mengerti mengapa.
Teeeeee
2

Anda bisa menggunakan warisan. Di sinilah Anda membuat kelas utama kemudian memiliki sub-kelas yang masih mengandung fungsi dan nilai dari kelas ibu namun juga dapat memiliki nilai dan fungsi tambahan untuk kelas tertentu.

class Apple:
    def __init__(self, yearMade):
        pass

    def ring(self):
        print('ring ring')

class iPhone(Apple):
    def __init__(self, number)
        number = number

    def func():
        pass

Sekarang kelas iPhone memiliki fungsi yang sama dengan kelas Apple dan fungsinya sendiri. Jika Anda ingin mempelajari lebih lanjut tentang warisan saya sarankan melakukan penelitian.

MoriartyPy
sumber
Terima kasih, saya memahami warisan dan cara kerjanya. Saya lebih tertarik pada bagaimana hal itu dapat diterapkan pada situasi spesifik saya, mengingat sifat-sifat yang dimiliki oleh dek Monopoli.
Teeeeee
@eeeeee Karena setiap kartu memiliki nilai, Anda dapat memperdagangkannya dan dapat memainkannya, Anda dapat membuat fungsi / prosedur di kelas untuk menangani acara ini dan kemudian memiliki atribut dan fungsi tambahan untuk kartu tertentu di sub kelas.
MoriartyPy
0

Untuk monopoli, saya akan merancang sudut pandang pendaratan game. Bukan kartu. Kartu hanya mewakili pendaratan untuk dunia nyata.

Ali Berat Çetin
sumber
Tolong jelaskan.
teeeeee