การคิดเกี่ยวกับรูปแบบการออกแบบและการปฏิบัติของ OOP เปลี่ยนไปอย่างไรในภาษาที่มีการเปลี่ยนแปลงและมีการพิมพ์น้อย


11

มีคำถามที่เป็นประโยชน์อยู่แล้วในบรรทัดเหล่านี้ (" รูปแบบการออกแบบที่ไม่ใช่ OOP? ") แต่ฉันอยากรู้มากขึ้นเกี่ยวกับมุมมองการเปลี่ยนผ่านสำหรับใครบางคนที่เพิ่งเริ่มต้นด้วยภาษาแบบไดนามิกและพิมพ์อ่อนแอ

นั่นคือ: สมมติว่าฉันได้รับการเขียนโปรแกรมใน C ++, C # หรือ Java เป็นเวลาหลายปีและดูดซึมจำนวนมากของภูมิปัญญาตามสายของรูปแบบการออกแบบ GoF ฟาวเลอร์ของรูปแบบของ Enterprise Application สถาปัตยกรรม , หลักการมูลฝอยฯลฯ ตอนนี้ฉัน' การเล่นน้ำใน Ruby, Python, JavaScript และอื่น ๆ และสงสัยว่าความรู้ของฉันใช้อย่างไร สันนิษฐานว่าฉันสามารถทำการแปลโดยตรงในหลายกรณี แต่เกือบจะแน่นอนว่าจะไม่ได้รับประโยชน์เต็มที่จากการตั้งค่าใหม่ของฉัน การพิมพ์แบบเป็ดเพียงอย่างเดียวทำให้ความคิดแบบอินเทอร์เฟซบนหัวของฉันเยอะ

อะไรที่ยังคงเหมือนเดิม? การเปลี่ยนแปลงอะไร มีหลักการชี้แนะเช่น SOLID หรือรูปแบบที่เป็นที่ยอมรับ (อาจเป็นเรื่องใหม่ทั้งหมด) ที่มือใหม่ควรรู้หรือไม่?

คำตอบ:


7

อะไรที่ยังคงเหมือนเดิม? การเปลี่ยนแปลงอะไร

รูปแบบเหมือนกัน เทคนิคการเปลี่ยนภาษา

มีหลักการชี้นำเช่น SOLID หรือไม่

ใช่. แท้จริงแล้วพวกเขายังคงเป็นหลักการชี้นำ ไม่มีอะไรเปลี่ยนแปลง

หรือรูปแบบมาตรฐาน (อาจเป็นรูปแบบใหม่ทั้งหมด) ซึ่งเป็นภาษาแบบไดนามิกที่มือใหม่ควรรู้

บางสิ่งมีเอกลักษณ์ ผลกระทบส่วนใหญ่คือการใช้เทคนิคการเปลี่ยนแปลง

รูปแบบคือ - ดี - เป็นรูปแบบ ไม่ใช่กฎหมาย ไม่ใช่รูทีนย่อย ไม่ใช่มาโคร มันเป็นความคิดที่ดีที่เกิดขึ้นซ้ำแล้วซ้ำอีกเพราะเป็นความคิดที่ดี

ความคิดที่ดีไม่ได้มีสไตล์หรือเปลี่ยนแปลงไปอย่างมาก

บันทึกอื่น ๆ Python ไม่ใช่ "พิมพ์อย่างอ่อน" มันพิมพ์ได้แรงกว่า Java หรือ C ++ เพราะไม่มีการใช้งานร่าย [ใช่มีวิธีที่ทำให้เหลวไหลในชั้นเรียนที่เกี่ยวข้องกับวัตถุ แต่มันไม่ใช่สิ่งที่ทำยกเว้นการพิสูจน์ประเด็นจู้จี้จุกจิกและถูกกฎหมาย]

ด้วย รูปแบบการออกแบบส่วนใหญ่จะขึ้นอยู่กับวิธีต่างๆในการใช้ประโยชน์จากความหลากหลาย

ดูStateหรือCommandหรือMemento เป็นตัวอย่าง พวกเขามีลำดับชั้นเรียนเพื่อสร้างสถานะ polymorphic คำสั่งหรือของที่ระลึกของการเปลี่ยนแปลงสถานะ ไม่มีอะไรเปลี่ยนแปลงอย่างมีนัยสำคัญเมื่อคุณทำเช่นนี้ใน Python การเปลี่ยนแปลงเล็กน้อยรวมถึงการผ่อนคลายของลำดับชั้นที่แม่นยำเพราะความหลากหลายในงูหลามขึ้นอยู่กับวิธีการทั่วไปที่ไม่ได้เป็นบรรพบุรุษร่วมกัน

นอกจากนี้รูปแบบบางอย่างเป็นเพียงความพยายามที่จะบรรลุผลผูกพัน รูปแบบที่เกี่ยวข้องกับโรงงานส่วนใหญ่เป็นความพยายามในการอนุญาตให้เปลี่ยนลำดับชั้นของคลาสได้ง่ายโดยไม่ต้องคอมไพล์โมดูล C ++ ใหม่ทุกครั้งในแอปพลิเคชัน นี่ไม่ใช่การเพิ่มประสิทธิภาพที่น่าสนใจในภาษาแบบไดนามิก อย่างไรก็ตามโรงงานซึ่งเป็นวิธีการปกปิดรายละเอียดการใช้งานยังคงมีค่ามาก

รูปแบบบางอย่างเป็นความพยายามในการขับเคลื่อนคอมไพเลอร์และตัวเชื่อมโยง ยกตัวอย่างเช่นซิงเกิลมีอยู่เพื่อสร้างความสับสน แต่อย่างน้อยก็แคปซูล Python ซิงเกิลคลาสไม่ได้เป็นโอกาสดี แต่โมดูลของ Python นั้นเป็นซิงเกิลตันแล้วพวกเราหลายคนก็ใช้มอดูลและหลีกเลี่ยงการยุ่งกับคลาสSingleton


ฉันจะไม่พูดว่า "ไม่มีอะไรเปลี่ยนแปลง" กับ SOLID หลักการแบบเปิดและหลักการทดแทน Liskov ขึ้นอยู่กับภาษาและโมเดลวัตถุของทั้งสองอาจไม่มีความหมาย (JavaScript และ Go ทั้งคู่ต่างคำนึง)
Mason Wheeler

@Mason Wheeler Open-Closed เป็นภาษาอิสระในประสบการณ์ของฉัน คุณจะต้องให้ตัวอย่างที่ชัดเจนมากขึ้นว่าการออกแบบแบบเปิดปิดเป็น "ไร้ความหมาย" ด้วย JavaScript หรือ Go บางทีการทดแทน Liskov อาจใช้ไม่ได้กับ JavaScript แต่รูปแบบที่สำคัญ - polymorphism - ยังดูเหมือนว่าจะใช้
S.Lott

@ S.Lott: อัปเดตที่ดีในการแก้ไข; พวกเขาน่าสนใจมากกว่าคำตอบเดิม: P ขอบคุณสำหรับการแก้ไขข้อผิดพลาด Python ของฉัน โดยทั่วไปตัวอย่างรูปแบบที่เฉพาะเจาะจงและวิธีที่พวกเขาผูกเป็นภาษาแบบไดนามิก, ความหลากหลาย, การผูกปลายและอื่น ๆ ที่สมบูรณ์แบบ
Domenic

@ S.Lott: เนื่องจากเปิด / ปิดเป็นเรื่องเกี่ยวกับการสืบทอดซึ่งภาษาเหล่านั้นไม่มี (เช่นกันความคิดของวัตถุที่ถูก "ปิดเพื่อดัดแปลง" จะไม่ดีกับนักเขียนรหัสทับทิมจำนวนมาก ... )
Mason Wheeler

@Mason Wheeler: ขอบคุณสำหรับคำชี้แจงเกี่ยวกับการเปิด / ปิด ฉันคิดว่าข้อยกเว้นของ JavaScript มีความสำคัญ แต่เนื่องจากคำถามนั้นเปิดกว้าง (การแสดงรายการ JavaScript, Python และ Ruby รวมถึงภาษาที่เรียกว่า ETC) ฉันไม่แน่ใจว่าจะจัดการกับกรณีพิเศษอย่างไร
S.Lott

8

Peter Norvig ได้ใช้คำถามนี้ในปี 1998 อ่านhttp://norvig.com/design-patterns/ppframe.htmเพื่อดูรายละเอียดสิ่งต่าง ๆ ที่เขาสังเกตเห็นและhttp://c2.com/cgi/wiki?AreDesignPatternsMissingLanguageFeaturesสำหรับ อภิปรายเพิ่มเติมเกี่ยวกับประเด็น

รุ่นสั้นคือเมื่อภาษาของคุณมีคุณสมบัติมากขึ้นแล้วรูปแบบการออกแบบซ้ำ ๆ มักจะง่ายขึ้น - บ่อยครั้งจนมองไม่เห็น เขาพบว่าสิ่งนี้เป็นจริงสำหรับรูปแบบการออกแบบส่วนใหญ่ที่ GoF ได้ระบุไว้


8

การเขียนโปรแกรมในภาษาเชิงวัตถุแบบไดนามิกใช้หลายรูปแบบและหลักการเดียวกัน แต่มีการปรับแต่งและความแตกต่างบางอย่างเนื่องจากสภาพแวดล้อม:

แทนที่การเชื่อมต่อด้วย Duck Typing - ที่ Gang of Four จะบอกให้คุณใช้คลาสพื้นฐานที่เป็นนามธรรมด้วยฟังก์ชั่นเสมือนจริงและคุณจะใช้ส่วนต่อประสานใน Java ในภาษาไดนามิกคุณต้องเข้าใจเท่านั้น เนื่องจากคุณอาจใช้วัตถุใดก็ได้และมันจะทำงานได้ดีถ้ามันใช้วิธีการที่เรียกว่าจริง ๆ คุณไม่จำเป็นต้องกำหนดส่วนต่อประสานที่เป็นทางการ อาจเป็นเอกสารที่ควรค่าแก่การพิจารณาเพื่อให้ชัดเจนว่าอะไรคือสิ่งที่จำเป็นต้องใช้จริง

ฟังก์ชั่นเป็นวัตถุด้วย - มีหลายรูปแบบที่เกี่ยวกับการแยกการตัดสินใจออกจากการกระทำ คำสั่งกลยุทธ์ห่วงโซ่ความรับผิดชอบ ฯลฯ ในภาษาที่มีฟังก์ชั่นชั้นหนึ่งมักจะมีเหตุผลที่จะเพียงแค่ส่งผ่านฟังก์ชั่นรอบ ๆ แทนที่จะทำให้วัตถุด้วย.doIt()วิธีการ รูปแบบเหล่านี้เปลี่ยนเป็น "ใช้ฟังก์ชันคำสั่งซื้อที่สูงขึ้น"

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

"... ในเฉพาะของฉันเอง ... สำนวน!" - แต่ละภาษามีแนวทางปฏิบัติที่ดีและไม่ดีและคุณจะต้องเรียนรู้และปฏิบัติตามหากคุณต้องการรหัสที่ดีที่สุดในภาษาเหล่านั้น รูปแบบตัววนซ้ำที่เขียนอย่างสมบูรณ์อาจถูกหัวเราะในภาษาที่มีความเข้าใจในรายการเช่นกัน


3

จากประสบการณ์ของฉันรูปแบบบางอย่างยังคงมีประโยชน์ใน Python และตั้งค่าได้ง่ายกว่าภาษาแบบคงที่มากกว่า OTOH บางรูปแบบไม่จำเป็นหรือแม้แต่ขมวดคิ้วเช่นเดียวกับรูปแบบซิงเกิล ใช้ตัวแปรหรือฟังก์ชั่นระดับโมดูลแทน หรือใช้รูปแบบ Borg

แทนที่จะตั้งค่ารูปแบบการสร้างมันมักจะเพียงพอที่จะผ่าน callable ที่สร้างวัตถุ นั่นอาจเป็นฟังก์ชั่นวัตถุที่มี__call__เมธอดหรือแม้แต่คลาสเนื่องจากไม่มีnew()ใน Python เพียงแค่เรียกใช้คลาสเอง:

def make_da_thing(maker, other, stuff):
    da_thing = maker(other + 1, stuff + 2)
    # ... do sth
    return da_thing

def maker_func(x, y):
     return x * y

class MakerClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
...
a = make_da_thing(maker_func, 5, 8)
b = make_da_thing(MakerClass, 6, 7)

รูปแบบสถานะและกลยุทธ์ใช้โครงสร้างที่คล้ายกันมากในภาษาเช่น C ++ และ Java น้อยกว่าดังนั้นใน Python รูปแบบกลยุทธ์ยังคงเหมือนเดิมไม่มากก็น้อย แต่รูปแบบของรัฐก็ไม่จำเป็น รูปแบบสถานะในภาษาแบบคงที่จำลองการเปลี่ยนแปลงของคลาสที่รันไทม์ ใน Python คุณสามารถทำได้ดังนี้เปลี่ยน class ของ object ที่ runtime ตราบใดที่คุณทำมันในแบบที่ควบคุมได้คุณควรจะทำได้:

class On(object):
    is_on = True
    def switch(self):
        self.__class__ = Off

class Off(object):
    is_on = False
    def switch(self):
        self.__class__ = On
...

my_switch = On()
assert my_switch.is_on
my_switch.switch()
assert not my_switch.is_on

รูปแบบที่ใช้ Static Type Dispatch จะไม่ทำงานหรือทำงานแตกต่างกันมาก คุณไม่จำเป็นต้องเขียนรหัสแผ่นบอยเลอร์มากนักเช่นรูปแบบผู้เข้าชม: ใน Java และ C ++ คุณต้องเขียนวิธีการยอมรับในทุกคลาสที่เยี่ยมชมได้ในขณะที่ใน Python คุณสามารถสืบทอดการทำงานนั้นผ่านคลาส Mixin เช่น Visitable:

class Visitable(object):
    def accept(self, visitor):
        visit = getattr(visitor, 'visit' + self.__class__.__name__)
        return visit(self)
...

class On(Visitable):
    ''' exactly like above '''

class Off(Visitable):
    ''' exactly like above '''

class SwitchStatePrinter(object): # Visitor
    def visitOn(self, switch):
         print 'the switch is on'
    def visitOff(self, switch):
         print 'the switch is off'

class SwitchAllOff(object): # Visitor
    def visitOn(self, switch):
         switch.switch()
    def visitOff(self, switch):
         pass
...
print_state = SwitchStatePrinter()
turn_em_off = SwitchAllOff()
for each in my_switches:
    each.accept(print_state)
    each.accept(turn_em_off)

หลาย ๆ สถานการณ์ที่เรียกใช้แอปพลิเคชันของรูปแบบในภาษาแบบคงที่ไม่ได้ทำใน Python มากนัก หลายสิ่งสามารถแก้ไขได้ด้วยเทคนิคอื่น ๆ เช่นฟังก์ชั่นการสั่งซื้อที่สูงขึ้น (มัณฑนากรโรงงานทำงาน) หรือคลาสเมตา


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