Як змінюється мислення щодо моделей дизайну та практики OOP у динамічних та слабо типових мовах?


11

У цих напрямках є досить корисне питання (" Шаблони дизайну, що не є OOP? "), Але мені цікавіше перехідна точка зору для тих, хто тільки починає працювати з динамічними та слабо набраними мовами.

Тобто: скажімо, я багато років програмував на C ++, C # або Java і поглинав багато мудрості в рамках моделей дизайну GoF, Шаблонів Фоулера архітектури корпоративних додатків , принципів SOLID тощо. Тепер я ' Я блукаю в Ruby, Python, JavaScript тощо і цікавлюсь, як застосовуються мої знання. Імовірно, я міг робити прямі переклади у багатьох випадках, але майже напевно це не використовувало б мою нову установку. Введення самок дак перетворює багато моїх інтерфейсів на основі інтерфейсу.

Що залишається тим самим? Що змінюється? Чи є такі керівні принципи, як SOLID, або канонічні зразки (можливо, абсолютно нові), які повинен знати новачок у динамічній мові?

Відповіді:


7

Що залишається тим самим? Що змінюється?

Шаблони однакові. Змінюються мовні прийоми.

Чи є такі принципові принципи, як SOLID,

Так. Дійсно, вони залишаються керівними принципами. Нічого не змінюється.

чи канонічні зразки (можливо, абсолютно нові), які повинен знати новачок у динамічній мові?

Деякі речі унікальні. В основному вплив полягає в тому, що змінюються методи впровадження.

Візерунок - ну - візерунок . Не закон. Не підпрограма. Не макрос. Це просто гарна ідея, яка повторюється, бо це гарна ідея.

Хороші ідеї не виходять зі стилю або кардинально змінюються.

Інші примітки. Python не "слабо набраний". Він сильніше набраний, ніж Java або C ++, тому що немає операцій з додаванням. [Так, є спосіб виправити клас, пов’язаний з об'єктом, але це не та річ, що робиться, окрім того, щоб довести метушливий, легалістичний пункт.]

Також. Більшість моделей дизайну базуються на різних способах використання поліморфізму.

Подивіться на держави або командування або Memento в якості прикладів. Вони мають ієрархії класів для створення поліморфних станів, команд або спогадів змін стану. Нічого суттєво не змінюється, коли ви робите це в Python. Незначні зміни включають розслаблення точної ієрархії класів, тому що поліморфізм у Python залежить від загальних методів, а не загальних предків.

Також деякі зразки - це просто спроба досягти пізнього зв’язування. Більшість моделей, пов'язаних із Заводом, - це спроба дозволити легку зміну ієрархії класів без повторної компіляції кожного модуля C ++ у додатку. Це не така цікава оптимізація в динамічній мові. Однак Фабрика як спосіб приховати деталі реалізації все ще має величезне значення.

Деякі зразки - це спроба запустити компілятор і лінкер. Наприклад, Сінглтон існує для створення заплутаних глобалів, але принаймні їх інкапсуляції. Класи однотонних Python не є приємною перспективою. Але модулі Python вже є однотонними, тому багато хто з нас просто використовують модуль і уникають спроб возитися з класом Singleton .


Я б не сказав, що "SOLID" нічого не змінюється. Залежно від мови та її об'єктної моделі, Принцип відкритого закриття та принцип заміни Ліскова можуть бути безглуздими. (JavaScript і Go обидва приходять до тями.)
Мейсон Уілер

@ Мейсон Уілер. Відкрите-закрите є мовою незалежною. Вам доведеться навести кілька конкретніших прикладів того, як відкритий закритий дизайн "безглуздий" за допомогою JavaScript або Go. Заміна Ліскова, мабуть, не стосується JavaScript, але істотна закономірність - поліморфізм - як і раніше, як правило, застосовується.
S.Lott

@ S.Lott: Приємні оновлення в редагуванні; вони були набагато цікавіші за оригінальну відповідь: P. Дякуємо, що виправили мою помилку Python. Загалом, конкретні приклади візерунка та те, як вони пов'язані з динамічними мовами, поліморфізм, пізнє зв’язування тощо, є ідеальними.
Доменіч

@ S.Lott: Тому що Open / Closed - це спадкування, якого ці мови не мають. (Крім того, думка про те, що "закритий для модифікації" об'єкт не буде добре сидіти з багатьма кодеками Ruby ...)
Мейсон Уілер

@ Mason Wheeler: Дякую за роз’яснення щодо відкритого / закритого. Я думаю, що виняток JavaScript є важливим, але оскільки питання настільки відкрите (перелік JavaScript, Python та Ruby, а також мова під назвою ETC), я не знаю, як вирішити особливий випадок.
S.Lott

8

Пітер Норвіг взявся за це саме питання в 1998 році, прочитайте http://norvig.com/design-patterns/ppframe.htm, щоб ознайомитись із набором детальних речей, які він помітив, і http://c2.com/cgi/wiki?AreDesignPatternsMissingLanguageFeatures for подальше обговорення по суті.

Коротка версія полягає в тому, що коли ваша мова має більше можливостей, то повторювані дизайнерські зразки, як правило, стають простішими - часто до того, що вони не помітні. Він виявив, що це стосується більшості моделей дизайну, які було визначено Міністерством фінансів.


8

Програмування в динамічній об'єктно-орієнтованій мові використовує багато однакових моделей та принципів, але є певні зміни та відмінності, зумовлені середовищем:

Замініть інтерфейси на друк на качках - там, де Банда четвірок скаже вам використовувати абстрактний базовий клас із чистими віртуальними функціями, а ви використовуєте інтерфейс на Java, динамічною мовою, вам потрібно лише розуміння. Оскільки ви можете використовувати будь-який об’єкт де завгодно, і він буде працювати чудово, якщо він реалізує методи, які насправді викликаються, не потрібно визначати формальний інтерфейс. Можливо, варто документувати один, щоб було зрозуміло, що насправді потрібно.

Функції - Об'єкти занадто - Є безліч шаблонів, які стосуються відокремлення рішення від дії; Команда, стратегія, ланцюжок відповідальності тощо. Мовою з першокласними функціями часто розумно просто передавати функцію, а не робити об'єкти .doIt()методами. Ці шаблони перетворюються на "використовувати функцію вищого порядку".

ПРОДАЄТЬСЯ - Принцип поділу інтерфейсу найбільше вражає тут, тому що інтерфейсів немає. Ви все ще повинні врахувати принцип, але ви не можете його повторно вписати у свій код. Тут захистить вас лише особиста пильність. З іншого боку, біль, викликаний порушенням цього принципу, значно зменшується у звичайних динамічних умовах.

"... в моїй особливій ... Ідіома!" - Кожна мова має добрі та погані практики, і вам доведеться вивчити їх та дотримуватися їх, якщо ви хочете найкращий код з цих мов. Ідеально написаний ітераторський зразок може сміятися мовою, наприклад, із вбудованими списками.


3

На моєму досвіді, деякі Шаблони все ще корисні в Python і навіть простіші в налаштуванні, ніж у більш статичних мовах. Деякі зразки OTOH просто не потрібні або навіть нахмурені, як, наприклад, шаблон Singleton. Замість цього використовуйте змінну рівня або модуль. Або скористайтеся схемою Борг.

Замість того, щоб створити шаблон творчості, досить часто пропускати дзвінок навколо, який створює об'єкти. Це може бути функція, об'єкт із __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 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

Шаблони, що покладаються на диспетчер статичного типу, не працюватимуть, або працюватимуть зовсім інакше. Вам не потрібно писати стільки коду пластини котла, наприклад шаблон шаблону відвідувачів: у Java та C ++ ви повинні написати метод прийняття у кожному видимому класі, тоді як у Python ви можете успадкувати цю функціональність за допомогою класу mixin, наприклад Visible:

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.