Pertanyaan pemula tentang pola desain dekorator


18

Saya sedang membaca artikel pemrograman dan disebutkan pola dekorator. Saya telah memprogram untuk sementara waktu tetapi tanpa pendidikan atau pelatihan formal apa pun, tetapi saya mencoba mempelajari tentang pola standar dan semacamnya.

Jadi saya mencari Dekorator, dan menemukan artikel Wikipedia di atasnya. Saya sekarang mengerti konsep pola Penghias, tetapi saya agak bingung dengan bagian ini:

Sebagai contoh, pertimbangkan sebuah jendela dalam sistem windowing. Untuk mengizinkan pengguliran konten jendela, kami mungkin ingin menambahkan scrollbar horizontal atau vertikal, sesuai keperluan. Asumsikan windows diwakili oleh instance dari kelas Window, dan menganggap kelas ini tidak memiliki fungsionalitas untuk menambahkan scrollbar. Kita bisa membuat ScrollingWindow subclass yang menyediakannya, atau kita bisa membuat ScrollingWindowDecorator yang menambahkan fungsionalitas ini ke objek Window yang ada. Pada titik ini, solusi mana pun akan baik-baik saja.

Sekarang mari kita asumsikan kita juga menginginkan kemampuan untuk menambahkan batas pada windows kita. Sekali lagi, kelas Window asli kami tidak memiliki dukungan. Subclass ScrollingWindow sekarang menimbulkan masalah, karena telah secara efektif menciptakan jendela jenis baru. Jika kita ingin menambahkan dukungan perbatasan ke semua jendela, kita harus membuat subkelas WindowWithBorder dan ScrollingWindowWithBorder. Jelas, masalah ini semakin buruk dengan setiap fitur baru yang akan ditambahkan. Untuk solusi dekorator, kita cukup membuat BorderedWindowDecorator baru — saat runtime, kita dapat mendekorasi jendela yang ada dengan ScrollingWindowDecorator atau BorderedWindowDecorator atau keduanya, sesuai keinginan kita.

OK, ketika mereka mengatakan untuk menambahkan perbatasan ke semua jendela, mengapa tidak menambahkan fungsionalitas ke dalam kelas Window asli untuk memungkinkan opsi? Seperti yang saya lihat, subkelas hanya untuk menambahkan fungsionalitas spesifik ke kelas, atau mengganti metode kelas. Jika saya perlu menambahkan fungsionalitas ke semua objek yang ada, mengapa saya tidak memodifikasi superclass saja?

Ada baris lain di artikel itu:

Pola dekorator adalah alternatif untuk subclassing. Subkelas menambahkan perilaku pada waktu kompilasi, dan perubahan memengaruhi semua instance kelas asli; mendekorasi dapat memberikan perilaku baru pada saat run-time untuk objek individu.

Saya tidak mendapatkan apa yang mereka katakan "... perubahan memengaruhi semua instance dari kelas asli" - bagaimana subklas mengubah kelas induk? Bukankah itu inti dari subklasifikasi?

Saya akan berasumsi bahwa artikel itu, seperti banyak Wiki, tidak ditulis dengan jelas. Saya bisa melihat kegunaan Penghias di baris terakhir - "... memberikan perilaku baru saat run-time untuk objek individu."

Tanpa membaca tentang pola ini, jika saya perlu mengubah perilaku pada saat run-time untuk masing-masing objek, saya mungkin akan membangun beberapa metode menjadi super atau subkelas untuk mengaktifkan / menonaktifkan perilaku tersebut. Tolong bantu saya benar-benar memahami kegunaan Dekorator, dan mengapa pemikiran pemula saya cacat?


menghargai hasil edit, Walter ... fyi, saya menggunakan "kawan-kawan" untuk tidak mengecualikan wanita, tetapi sebagai salam informal.
Jim

Hasil edit hanya untuk menghapus salam secara umum. Ini bukan protokol SO / SE standar untuk menggunakannya dalam pertanyaan [jangan khawatir, kami tidak berpikir itu tidak sopan untuk langsung masuk ke pertanyaan]
Farrell

keren Terimakasih! hanya saja dia menandainya "menghapus gender" dan aku akan membenci siapa pun untuk berpikir aku misoginis atau sesuatu !! :)
Jim

Saya banyak menggunakannya untuk menghindari hal-hal prosedural hukum yang disebut "layanan": medium.com/@wrong.about/…
Zapadlo

Jawaban:


13

Pola dekorator adalah yang lebih menyukai komposisi daripada warisan [paradigma OOP lain yang berguna untuk dipelajari]

Manfaat utama dari pola dekorator - lebih dari subclassing adalah untuk memungkinkan lebih banyak opsi campuran & cocok. Jika Anda memiliki, misalnya, 10 perilaku berbeda yang dimiliki oleh sebuah jendela, maka ini berarti - dengan subkelas - Anda perlu membuat setiap kombinasi yang berbeda, yang juga pasti akan menyertakan banyak penggunaan kembali kode.
Namun, apa yang terjadi ketika Anda memutuskan untuk menambahkan perilaku baru?

Dengan dekorator, Anda hanya menambahkan kelas baru yang menjelaskan perilaku ini, dan hanya itu - polanya memungkinkan Anda untuk secara efektif memasukkan ini tanpa modifikasi pada sisa kode.
Dengan subklasifikasi, Anda mendapatkan mimpi buruk.
Satu pertanyaan yang Anda ajukan adalah "bagaimana subklas mengubah kelas induk?" Bukannya itu mengubah kelas induk; ketika ia mengatakan sebuah instance, itu berarti objek apa pun yang Anda 'instantiated' [jika Anda menggunakan Java atau C #, misalnya, dengan menggunakan newperintah]. Maksudnya adalah, ketika Anda menambahkan perubahan ini ke kelas, Anda tidak punya pilihan untuk perubahan itu ada di sana, bahkan jika Anda tidak benar-benar membutuhkannya.

Atau, Anda dapat menempatkan semua fungsinya ke dalam satu kelas dengan itu dihidupkan / dimatikan melalui bendera ... tetapi ini berakhir dengan satu kelas yang menjadi lebih besar dan lebih besar seiring proyek Anda tumbuh.
Bukan hal yang aneh untuk memulai proyek Anda dengan cara ini, dan refactor menjadi pola dekorator setelah Anda mencapai massa kritis yang efektif.

Poin menarik yang harus dibuat: Anda dapat menambahkan fungsi yang sama beberapa kali; jadi Anda bisa, misalnya, memiliki jendela dengan jumlah ganda, tiga kali lipat atau berapa pun atau batas yang Anda butuhkan.

Poin utama dari pola ini adalah untuk mengaktifkan perubahan run-time: Anda mungkin tidak tahu bagaimana Anda ingin melihat jendela sampai program berjalan, dan ini memungkinkan Anda untuk memodifikasinya dengan mudah. Memang, ini bisa dilakukan melalui subclassing, tetapi tidak sebaik itu.
Dan akhirnya, memungkinkan fungsionalitas ditambahkan ke kelas yang mungkin tidak dapat Anda edit - misalnya di kelas yang disegel / final, atau yang disediakan dari API lain


2
Terima kasih, Farrell - ketika Anda mengatakan "Atau, Anda dapat menempatkan semua fungsinya ke dalam satu kelas dengan itu dinyalakan / dimatikan melalui flag ... tetapi ini berakhir dengan satu kelas yang menjadi lebih besar dan lebih besar seiring proyek Anda tumbuh." yang membuatnya klik untuk saya. Saya pikir bagian dari masalah ini adalah saya tidak pernah bekerja di kelas yang begitu besar sehingga dekorator masuk akal bagi saya. Berpikir besar, saya pasti bisa melihat manfaatnya ... terima kasih!
Jim

Juga, bagian tentang kelas tersegel masuk akal ... terima kasih!
Jim

3

Pertimbangkan kemungkinan menambahkan scrollbar dan batas dengan subclassing. Jika Anda menginginkan setiap kemungkinan, Anda mendapatkan empat kelas (Python):

class Window(object):
    def draw(self):
        "do actual drawing of window"

class WindowWithScrollBar(Window):
    def draw(self):
        Window.draw(self)
        "do actual drawing of scrollbar"

class WindowWithBorder(Window):
    def draw(self):
        Window.draw(self)
        "do actual drawing of border"

class WindowWithScrollBarAndBorder(Window):
    def draw(self):
        WindowWithScrollBar.draw(self)
        WindowWithBorder.draw(self)

Sekarang, dalam WindowWithScrollBarAndBorder.draw(), jendela ditarik dua kali, dan kedua kalinya mungkin atau mungkin tidak menimpa Scrollbar yang sudah ditarik, tergantung pada implementasinya. Jadi kode turunan Anda sangat terkait dengan implementasi kelas lain, dan Anda harus peduli tentang hal itu setiap kali Anda mengubah Windowperilaku kelas. Sebuah solusi adalah dengan menyalin-menempelkan kode dari kelas super ke kelas turunan dan menyesuaikannya dengan kebutuhan kelas turunan, tetapi setiap perubahan dalam kelas super harus disalin-tempel lagi dan disesuaikan lagi, jadi sekali lagi kelas turunannya adalah tergabung erat dengan kelas dasar (melalui kebutuhan untuk copy-paste-menyesuaikan). Masalah lain adalah bahwa jika Anda membutuhkan properti lain yang mungkin atau mungkin tidak dimiliki jendela, Anda harus menggandakan setiap kelas:

class Window(object):
    ...

class WindowWithGimmick(Window):
    ...

class WindowWithScrollBar(Window):
    ...

class WindowWithBorder(Window):
    ...

class WindowWithScrollBarAndBorder(Window):
    ...

class WindowWithScrollBarAndGimmick(Window):
    ...

class WindowWithBorderAndGimmick(Window):
    ...

class WindowWithScrollBarAndBorderAndGimmick(Window):
    ...

Itu berarti untuk satu set fitur yang saling independen f dengan | f | sebagai jumlah fitur, Anda harus mendefinisikan 2 ** | f | kelas, mis. jika Anda memiliki 10 fitur, Anda mendapatkan 1024 kelas yang sangat rapat. Jika Anda menggunakan Pola Penghias, setiap fitur dapatkan kelasnya yang independen dan longgar dan Anda hanya memiliki 1 + | f | kelas (itu 11 untuk contoh di atas).


Lihat, pemikiran saya tidak akan terus menambahkan kelas - itu akan menambahkan funtionality ke kelas (sub) asli. jadi di Window kelas (objek): Anda memiliki scrollBar = true, border = false, dll ... Jawaban Farrell menunjukkan kepada saya di mana itu bisa salah, namun - hanya mengarah ke kelas yang membengkak dan semacamnya ... terima kasih atas jawabannya, +1!
Jim

baik, +1 begitu saya punya perwakilan untuk melakukannya!
Jim

2

Saya bukan ahli dalam pola khusus ini, tetapi seperti yang saya lihat, pola dekorator dapat diterapkan ke kelas yang Anda mungkin tidak memiliki kemampuan untuk mengubah atau sub-kelas (mereka mungkin bukan kode Anda dan disegel, misalnya ). Dalam contoh Anda, bagaimana jika Anda tidak menulis kelas Window tetapi hanya mengkonsumsinya? Selama kelas Window memiliki antarmuka dan Anda memprogram melawan antarmuka itu, Dekorator Anda dapat menggunakan antarmuka yang sama tetapi memperluas fungsionalitasnya.

Contoh yang Anda sebutkan sebenarnya dibahas di sini dengan cukup rapi:

Memperluas fungsionalitas objek dapat dilakukan secara statis (pada waktu kompilasi) dengan menggunakan pewarisan namun mungkin perlu untuk memperluas fungsionalitas objek secara dinamis (saat runtime) ketika objek digunakan.

Perhatikan contoh khas jendela grafis. Untuk memperluas fungsionalitas jendela grafis misalnya dengan menambahkan bingkai ke jendela, akan perlu memperluas kelas jendela untuk membuat kelas FramedWindow. Untuk membuat jendela berbingkai, Anda perlu membuat objek dari kelas FramedWindow. Namun tidak mungkin untuk memulai dengan jendela biasa dan memperluas fungsinya saat runtime menjadi jendela berbingkai.

http://www.oodesign.com/decorator-pattern.html

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.