Làm thế nào để suy nghĩ về các mẫu thiết kế và thực hành OOP thay đổi trong các ngôn ngữ năng động và gõ yếu?


11

Có một câu hỏi khá hữu ích dọc theo những dòng này (" Các mẫu thiết kế không phải OOP? "), Nhưng tôi tò mò hơn về quan điểm chuyển tiếp cho một người mới bắt đầu với các ngôn ngữ được gõ và năng động.

Đó là: giả sử tôi đã lập trình trong C ++, C # hoặc Java trong nhiều năm và tiếp thu rất nhiều sự khôn ngoan dọc theo các mẫu của thiết kế GoF, Các mô hình kiến ​​trúc ứng dụng doanh nghiệp của Fowler , các nguyên tắc RẮN , v.v ... Bây giờ tôi m học hỏi về Ruby, Python, JavaScript, v.v. và tự hỏi kiến ​​thức của tôi áp dụng như thế nào. Có lẽ tôi có thể thực hiện các bản dịch trực tiếp trong nhiều trường hợp, nhưng gần như chắc chắn điều đó sẽ không tận dụng được lợi thế của thiết lập mới của tôi. Vịt gõ một mình biến rất nhiều suy nghĩ dựa trên giao diện của tôi trên đầu của nó.

Những gì vẫn giữ nguyên? Các thay đổi? Có những nguyên tắc hướng dẫn như RẮN, hoặc các mẫu chính tắc (có lẽ hoàn toàn mới) mà một người mới học ngôn ngữ năng động nên biết?

Câu trả lời:


7

Những gì vẫn giữ nguyên? Các thay đổi?

Các mô hình là như nhau. Các kỹ thuật ngôn ngữ thay đổi.

Có nguyên tắc hướng dẫn như RẮN,

Đúng. Thật vậy, họ vẫn là nguyên tắc hướng dẫn. Không có gì thay đổi.

hoặc các mẫu chính tắc (có lẽ hoàn toàn mới) mà một người mới học ngôn ngữ năng động nên biết?

Một số điều là duy nhất. Chủ yếu là tác động là các kỹ thuật thực hiện thay đổi.

Một mô hình là - tốt - một mô hình . Không phải là một luật. Không phải là chương trình con. Không phải là một vĩ mô. Đó chỉ là một ý tưởng tốt được lặp đi lặp lại bởi vì đó là một ý tưởng tốt.

Ý tưởng tốt không lỗi thời hoặc thay đổi đáng kể.

Ghi chú khác. Python không "đánh máy yếu". Nó được gõ mạnh hơn Java hoặc C ++ vì không có thao tác truyền. [Vâng, có một cách để làm mờ lớp liên quan đến một đối tượng, nhưng đó không phải là điều được thực hiện ngoại trừ để chứng minh một quan điểm hợp pháp, phức tạp.]

Cũng thế. Hầu hết các mẫu thiết kế dựa trên các cách khác nhau để khai thác đa hình.

Nhìn vào Bang hoặc Lệnh hoặc Memento làm ví dụ. Họ có hệ thống phân cấp lớp để tạo ra một trạng thái đa hình, các lệnh hoặc các vật lưu niệm về các thay đổi trạng thái. Không có gì thay đổi đáng kể khi bạn làm điều này trong Python. Những thay đổi nhỏ bao gồm sự nới lỏng của hệ thống phân cấp lớp chính xác vì tính đa hình trong Python phụ thuộc vào các phương thức phổ biến không phải là tổ tiên chung.

Ngoài ra, một số mẫu chỉ đơn giản là một nỗ lực để đạt được ràng buộc muộn. Hầu hết các mẫu liên quan đến Factory là một nỗ lực cho phép dễ dàng thay đổi thành hệ thống phân cấp lớp mà không cần biên dịch lại mọi mô-đun C ++ trong ứng dụng. Đây không phải là tối ưu hóa thú vị trong một ngôn ngữ năng động. Tuy nhiên, một Nhà máy như một cách để che giấu các chi tiết thực hiện vẫn có giá trị rất lớn.

Một số mẫu là một nỗ lực để lái trình biên dịch và liên kết. Singleton , ví dụ, tồn tại để tạo ra những quả cầu khó hiểu nhưng ít nhất là gói gọn chúng. Các lớp đơn Python không phải là một triển vọng dễ chịu. Nhưng các mô-đun Python đã là singletons, vì vậy nhiều người trong chúng ta chỉ sử dụng một mô-đun và tránh cố gắng gây rối với lớp Singleton .


Tôi sẽ không nói rằng "không có gì thay đổi" với RẮN. Tùy thuộc vào ngôn ngữ và mô hình đối tượng của nó, Nguyên tắc đóng mở và Nguyên tắc thay thế Liskov có thể là vô nghĩa. (JavaScript và Go đều xuất hiện trong tâm trí.)
Mason Wheeler

@Mason Wheeler. Mở-Đóng là ngôn ngữ độc lập theo kinh nghiệm của tôi. Bạn sẽ phải cung cấp một số ví dụ cụ thể hơn về cách thiết kế đóng mở "vô nghĩa" với JavaScript hoặc Go. Sự thay thế Liskov, có lẽ, không áp dụng cho JavaScript, nhưng mẫu thiết yếu - đa hình - dường như vẫn được áp dụng.
S.Lott

@ S.Lott: Cập nhật đẹp trong chỉnh sửa; chúng thú vị hơn nhiều so với câu trả lời ban đầu: P. Cảm ơn đã sửa lỗi Python của tôi. Nói chung, các ví dụ mẫu cụ thể và cách chúng liên kết với các ngôn ngữ động, đa hình, liên kết muộn, v.v ... là hoàn hảo.
Domenic

@ S.Lott: Bởi vì Mở / Đóng là về sự kế thừa, những ngôn ngữ đó không có. (Ngoài ra, ý tưởng về một đối tượng bị "đóng cửa để sửa đổi" sẽ không phù hợp với nhiều lập trình viên Ruby ...)
Mason Wheeler

@Mason Wheeler: Cảm ơn bạn đã làm rõ về Mở / Đóng. Tôi nghĩ rằng ngoại lệ JavaScript là quan trọng, nhưng vì câu hỏi rất mở (liệt kê JavaScript, Python và Ruby, cũng như một ngôn ngữ gọi là ETC) nên tôi không chắc cách giải quyết trường hợp đặc biệt.
S.Lott

8

Peter Norvig đã đưa ra câu hỏi này vào năm 1998, đọc http://norvig.com/design-potypes/ppframe.htmlm để biết một bộ chi tiết mà anh ấy nhận thấy và http://c2.com/cgi/wiki?AreDesignPotypesMissingL LanguageFeatures cho thảo luận thêm về điểm.

Phiên bản ngắn là khi ngôn ngữ của bạn có nhiều tính năng hơn, thì các mẫu thiết kế lặp đi lặp lại có xu hướng trở nên đơn giản hơn - thường đến mức vô hình. Ông thấy rằng điều này đúng với hầu hết các mẫu thiết kế mà GoF đã xác định.


8

Lập trình trong một ngôn ngữ hướng đối tượng động sử dụng nhiều mẫu và nguyên tắc giống nhau, nhưng có một số điều chỉnh và khác biệt do môi trường:

Thay thế giao diện bằng cách gõ vịt - Trường hợp Gang of Four sẽ cho bạn sử dụng lớp cơ sở trừu tượng với các hàm ảo thuần túy và bạn sẽ sử dụng giao diện trong Java, bằng ngôn ngữ động, bạn chỉ cần hiểu. Vì bạn có thể sử dụng bất kỳ đối tượng nào ở bất cứ đâu và nó sẽ hoạt động tốt nếu nó thực hiện các phương thức được gọi thực sự, bạn không cần xác định giao diện chính thức. Nó có thể đáng để ghi lại một tài liệu , để nó rõ ràng những gì thực sự cần thiết.

Các chức năng cũng là Đối tượng - Có rất nhiều mẫu về phân tách quyết định khỏi hành động; Lệnh, Chiến lược, Chuỗi trách nhiệm, v.v. Trong một ngôn ngữ có chức năng hạng nhất, thường đơn giản là chỉ truyền một hàm xung quanh thay vì tạo các đối tượng bằng .doIt()các phương thức. Những mẫu này biến thành "sử dụng hàm bậc cao hơn."

ĐÃ BÁN - Nguyên tắc phân chia giao diện có tác động lớn nhất ở đây, vì không có giao diện. Bạn vẫn nên xem xét nguyên tắc, nhưng bạn không thể thống nhất nó vào mã của mình. Chỉ có sự cảnh giác cá nhân sẽ bảo vệ bạn ở đây. Về mặt tích cực, nỗi đau do vi phạm nguyên tắc này giảm đi nhiều trong môi trường năng động thông thường.

"... trong riêng tôi ... Thành ngữ!" - Mỗi ngôn ngữ có các thực tiễn tốt và các thực tiễn xấu và bạn sẽ phải học chúng và tuân theo chúng, nếu bạn muốn mã tốt nhất trong các ngôn ngữ đó. Ví dụ, một mẫu lặp được viết hoàn hảo có thể được cười trong một ngôn ngữ được tích hợp sẵn trong danh sách.


3

Theo kinh nghiệm của tôi, một số Mẫu vẫn hữu ích trong Python và thậm chí dễ cài đặt hơn so với các ngôn ngữ tĩnh hơn. Một số Mẫu OTOH không cần thiết, hoặc thậm chí không tán thành, như Mẫu đơn. Sử dụng một biến cấp mô-đun hoặc chức năng thay thế. Hoặc sử dụng Mô hình Borg.

Thay vì thiết lập một Mô hình sáng tạo, nó thường đủ để vượt qua một cuộc gọi xung quanh tạo ra các đối tượng. Đó có thể là một hàm, một đối tượng với một __call__phương thức hoặc thậm chí là một lớp, vì không có new()trong Python, chỉ là một lời gọi của chính lớp đó:

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)

Mẫu trạng thái và chiến lược chia sẻ một cấu trúc rất giống nhau trong các ngôn ngữ như C ++ và Java. Ít hơn trong Python. Mẫu chiến lược giữ nguyên ít nhiều giống nhau, nhưng Mẫu trạng thái hầu như không cần thiết. Mẫu trạng thái trong các ngôn ngữ tĩnh mô phỏng sự thay đổi của lớp khi chạy. Trong Python, bạn có thể làm điều đó: thay đổi lớp của một đối tượng khi chạy. Miễn là bạn làm điều đó theo cách được kiểm soát, đóng gói, bạn sẽ ổn:

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

Các mẫu dựa trên Công cụ loại tĩnh sẽ không hoạt động hoặc hoạt động hoàn toàn khác. Bạn không phải viết nhiều mã tấm nồi hơi, ví dụ Mẫu khách truy cập: trong Java và C ++, bạn phải viết một phương thức chấp nhận trong mọi lớp có thể truy cập, trong khi trong Python, bạn có thể kế thừa chức năng đó thông qua lớp mixin, như 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)

Nhiều tình huống yêu cầu ứng dụng Mẫu trong Ngôn ngữ tĩnh không thực hiện nhiều bằng Python. Nhiều thứ có thể được giải quyết với các kỹ thuật khác, như các hàm bậc cao hơn (trang trí, nhà máy chức năng) hoặc các lớp meta.


Bây giờ tôi nhận ra rằng câu trả lời của bạn bao gồm câu hỏi tôi vừa hỏi: Ghi đè __class__để triển khai một nhà máy trong Python có phải là một ý tưởng tốt không?
rds
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.