คำถามเกี่ยวกับรูปแบบการออกแบบมัณฑนากร Newbie


18

ฉันอ่านบทความการเขียนโปรแกรมและกล่าวถึงรูปแบบของมัณฑนากร ฉันเขียนโปรแกรมมาซักพัก แต่ไม่มีการศึกษาหรือการฝึกอบรมอย่างเป็นทางการ แต่ฉันพยายามเรียนรู้เกี่ยวกับรูปแบบมาตรฐานและเช่นนั้น

ดังนั้นฉันจึงค้นหามัณฑนากรและพบบทความ Wikipediaอยู่ ตอนนี้ฉันเข้าใจแนวคิดของรูปแบบมัณฑนากร แต่ตอนนี้ฉันสับสนเล็กน้อย:

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

ทีนี้สมมติว่าเราต้องการความสามารถในการเพิ่มเส้นขอบให้กับหน้าต่างของเราด้วย คลาส Window ดั้งเดิมของเราไม่สนับสนุน ขณะนี้คลาสย่อย ScrollingWindow มีปัญหาเนื่องจากได้สร้างหน้าต่างชนิดใหม่อย่างมีประสิทธิภาพ หากเราต้องการเพิ่มการรองรับชายแดนให้กับหน้าต่างทั้งหมดเราต้องสร้างคลาสย่อย WindowWithBorder และ ScrollingWindowWithBorder เห็นได้ชัดว่าปัญหานี้แย่ลงเมื่อมีการเพิ่มคุณสมบัติใหม่ทุกอย่าง สำหรับโซลูชันมัณฑนากรเราเพียงสร้าง BorderedWindowDecorator ใหม่ - ที่รันไทม์เราสามารถตกแต่งหน้าต่างที่มีอยู่ด้วย ScrollingWindowDecorator หรือ BorderedWindowDecorator หรือทั้งสองอย่างตามที่เราเห็นสมควร

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

มีอีกบรรทัดในบทความ:

รูปแบบมัณฑนากรเป็นอีกทางเลือกหนึ่งของการทำคลาสย่อย การจัดคลาสย่อยเพิ่มลักษณะการทำงาน ณ เวลารวบรวมและการเปลี่ยนแปลงมีผลต่ออินสแตนซ์ทั้งหมดของคลาสดั้งเดิม การตกแต่งสามารถให้พฤติกรรมใหม่ในเวลาทำงานสำหรับแต่ละวัตถุ

ฉันไม่เข้าใจว่าพวกเขาพูดว่า "... การเปลี่ยนแปลงส่งผลกระทบต่ออินสแตนซ์ทั้งหมดของคลาสดั้งเดิม" - คลาสย่อยเปลี่ยนแปลงคลาสแม่ได้อย่างไร นั่นคือจุดรวมของคลาสย่อยหรือไม่?

ฉันจะสมมติว่าบทความเช่นเดียวกับ Wiki หลาย ๆ อันนั้นไม่ได้เขียนไว้อย่างชัดเจน ฉันสามารถเห็นประโยชน์ของมัณฑนากรในบรรทัดสุดท้าย - "... แสดงพฤติกรรมใหม่ในเวลาทำงานสำหรับแต่ละวัตถุ"

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


ขอบคุณการแก้ไขวอลเตอร์ ... fyi ฉันใช้ "คน" เพื่อไม่รวมผู้หญิง แต่เป็นการทักทายอย่างไม่เป็นทางการ
Jim

การแก้ไขจะเป็นการลบคำทักทายโดยทั่วไป มันไม่ได้มาตรฐานดังนั้นโปรโตคอล / SE ใช้หนึ่งในคำถาม [ไม่ต้องกังวลเราไม่คิดว่ามันหยาบคายกับกระโดดตรงเข้ามาถาม]
แฟร์เรลล์

เจ๋งขอบคุณ! มันเป็นเพียงแค่ว่าเขาติดแท็กมันว่า "ลบเพศ" และฉันเกลียดใครก็ตามที่คิดว่าฉันเป็นผู้หญิงหรืออะไรบางอย่าง !! :)
Jim

ฉันใช้มันอย่างหนักเพื่อหลีกเลี่ยงสิ่งที่ดำเนินการตามกฎหมายเหล่านั้นเรียกว่า "services": medium.com/@wrong.about/ ......
Zapadlo

คำตอบ:


13

รูปแบบมัณฑนากรเป็นสิ่งหนึ่งที่สนับสนุนการแต่งแต้มเรื่องมรดก [กระบวนทัศน์ OOP อื่นที่มีประโยชน์ในการเรียนรู้]

ประโยชน์หลักของรูปแบบการตกแต่งภายใน - การทำ subclassing คือการอนุญาตตัวเลือกการผสม & การจับคู่ที่มากขึ้น ตัวอย่างเช่นถ้าคุณมี 10 พฤติกรรมที่แตกต่างกันที่หน้าต่างสามารถมีได้หมายความว่าด้วยการทำคลาสย่อยคุณจำเป็นต้องสร้างชุดค่าผสมที่แตกต่างกันซึ่งจะรวมถึงการใช้รหัสซ้ำจำนวนมากอย่างหลีกเลี่ยงไม่ได้
อย่างไรก็ตามจะเกิดอะไรขึ้นเมื่อคุณตัดสินใจเพิ่มพฤติกรรมใหม่

ด้วยมัณฑนากรคุณเพียงแค่เพิ่มคลาสใหม่ซึ่งอธิบายลักษณะการทำงานนี้และนั่นคือ - รูปแบบช่วยให้คุณสามารถวางสิ่งนี้ได้อย่างมีประสิทธิภาพโดยไม่ต้องทำการดัดแปลงใด ๆ กับโค้ดที่เหลือ
ด้วยการแบ่งคลาสคุณจะมีฝันร้ายในมือของคุณ
คำถามหนึ่งที่คุณถามคือ "subclassing เปลี่ยนคลาสแม่อย่างไร" ไม่ใช่ว่ามันจะเปลี่ยนชั้นผู้ปกครอง; เมื่อมันกล่าวถึงอินสแตนซ์มันหมายถึงวัตถุใด ๆ ที่คุณ 'อินสแตนซ์' [ถ้าคุณใช้ Java หรือ C # เช่นโดยใช้newคำสั่ง] สิ่งที่มันหมายถึงคือเมื่อคุณเพิ่มการเปลี่ยนแปลงเหล่านี้ในชั้นเรียนคุณไม่มีทางเลือกสำหรับการเปลี่ยนแปลงที่จะมีแม้ว่าคุณจะไม่ต้องการมันจริง ๆ

หรือคุณสามารถใส่ฟังก์ชั่นทั้งหมดของเขาไว้ในคลาสเดียวโดยเปิด / ปิดผ่านธง ... แต่สิ่งนี้จะจบลงด้วยคลาสเดี่ยวที่ใหญ่ขึ้นเรื่อย ๆ เมื่อโครงการของคุณเติบโตขึ้น
มันไม่ใช่เรื่องแปลกที่จะเริ่มโครงการของคุณด้วยวิธีนี้และปรับโครงสร้างเป็นมัณฑนากรเมื่อคุณได้รับผลกระทบอย่างมีประสิทธิภาพ

จุดที่น่าสนใจที่ควรทำ: คุณสามารถเพิ่มฟังก์ชั่นเดียวกันหลาย ๆ ครั้ง; ตัวอย่างเช่นคุณสามารถมีหน้าต่างที่มีสองเท่าสามเท่าหรือจำนวนเท่าใดก็ได้ตามที่คุณต้องการ

จุดสำคัญของรูปแบบคือการเปิดใช้งานการเปลี่ยนแปลงแบบรันไทม์: คุณอาจไม่ทราบว่าคุณต้องการให้หน้าต่างดูจนกระทั่งโปรแกรมทำงานและสิ่งนี้ช่วยให้คุณสามารถปรับเปลี่ยนได้อย่างง่ายดาย ได้รับสิ่งนี้สามารถทำได้ผ่าน subclassing แต่ไม่เป็นอย่างดี
และในที่สุดก็อนุญาตให้เพิ่มฟังก์ชันการทำงานในคลาสที่คุณอาจไม่สามารถแก้ไขได้ - ตัวอย่างเช่นในคลาสที่ปิดผนึก / ครั้งสุดท้ายหรือที่จัดทำจาก API อื่น


2
ขอบคุณฟาร์เรล - เมื่อคุณพูดว่า "หรือคุณสามารถใส่ฟังก์ชั่นทั้งหมดของเขาไว้ในคลาสเดียวโดยเปิด / ปิดผ่านธง ... แต่สิ่งนี้จะจบลงด้วยคลาสเดี่ยวที่ใหญ่ขึ้นและใหญ่ขึ้นเมื่อโครงการของคุณเติบโต" ที่ทำให้คลิกสำหรับฉัน ฉันคิดว่าส่วนหนึ่งของปัญหาคือฉันไม่เคยทำงานในชั้นเรียนที่มีขนาดใหญ่จนเกินไปมัณฑนากรเข้ากับฉัน เมื่อคิดใหญ่ฉันจะเห็นประโยชน์อย่างแน่นอน ... ขอบคุณ!
Jim

นอกจากนี้ส่วนที่เกี่ยวกับคลาสที่ปิดผนึกทำให้รู้สึกเป็นตัน ... ขอบคุณ!
Jim

3

พิจารณาความเป็นไปได้ในการเพิ่มแถบเลื่อนและเส้นขอบด้วยการแบ่งคลาสย่อย ถ้าคุณต้องการทุกความเป็นไปได้คุณจะได้สี่คลาส (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)

ในตอนนี้WindowWithScrollBarAndBorder.draw()หน้าต่างจะถูกวาดสองครั้งและครั้งที่สองอาจหรือไม่อาจเขียนทับแถบเลื่อนที่วาดไว้แล้วขึ้นอยู่กับการใช้งาน ดังนั้นรหัสที่ได้รับของคุณจะถูกผนวกเข้ากับการใช้คลาสอื่นอย่างแน่นหนาและคุณต้องใส่ใจว่าทุกครั้งที่คุณเปลี่ยนWindowพฤติกรรมของคลาส วิธีแก้ปัญหาคือการคัดลอก - วางรหัสจากคลาสซูเปอร์ถึงคลาสที่ได้รับและปรับให้เหมาะกับความต้องการของคลาสที่ได้รับ แต่การเปลี่ยนแปลงในคลาสซูเปอร์ทุกครั้งจะต้องคัดลอกวางอีกครั้งและปรับอีกครั้งดังนั้นคลาสที่ได้รับ เชื่อมต่ออย่างแน่นหนากับคลาสพื้นฐาน (ผ่านความจำเป็นในการคัดลอกวางปรับ) ปัญหาอีกประการหนึ่งคือถ้าคุณต้องการคุณสมบัติอื่นที่หน้าต่างอาจมีหรือไม่มีคุณต้องเพิ่มเป็นสองเท่าทุกคลาส:

class Window(object):
    ...

class WindowWithGimmick(Window):
    ...

class WindowWithScrollBar(Window):
    ...

class WindowWithBorder(Window):
    ...

class WindowWithScrollBarAndBorder(Window):
    ...

class WindowWithScrollBarAndGimmick(Window):
    ...

class WindowWithBorderAndGimmick(Window):
    ...

class WindowWithScrollBarAndBorderAndGimmick(Window):
    ...

นั่นหมายถึงชุดของคุณสมบัติอิสระอิสระซึ่งประกอบด้วย f กับ | f | ด้วยจำนวนฟีเจอร์คุณต้องกำหนด 2 ** | f | ชั้นเรียนเช่น ถ้าคุณมีฟีเจอร์ 10 อย่างคุณจะได้รับ 1024 คลาสที่บีบอัดอย่างแน่นหนา หากคุณใช้รูปแบบของมัณฑนากรทุก ๆ คุณสมบัติจะมีความเป็นอิสระและเป็นคู่กันอย่างหลวม ๆ และคุณมีเพียง 1 + | f | คลาส (นั่นคือ 11 สำหรับตัวอย่างด้านบน)


ดูสิความคิดของฉันจะไม่เพิ่มชั้นเรียนต่อไป - มันเป็นการเพิ่มความสนุกให้กับชั้นเรียนดั้งเดิม (ย่อย) ดังนั้นในหน้าต่างคลาส (วัตถุ): คุณมี scrollBar = true, border = false, ฯลฯ ... คำตอบฟาร์เรลแสดงให้ฉันเห็นว่าสามารถไปไหนได้ผิดพลาด - เพียงแค่นำไปสู่ชั้นเรียนป่อง +1!
Jim

ดี +1 เมื่อฉันมีตัวแทนให้ทำ!
Jim

2

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

ตัวอย่างที่คุณพูดถึงถูกกล่าวถึงที่นี่ค่อนข้างเรียบร้อย:

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

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

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.