ООП и динамическая типизация (не статическая или динамическая) - PullRequest
10 голосов
/ 17 декабря 2009

Какие принципы ООП, если таковые имеются, не применяются или применяются по-другому в динамически типизированной среде, в отличие от статически типизированной среды (например, Ruby против C #)? Это не призыв к дебатам «Статический против динамического», а скорее я хотел бы посмотреть, есть ли принятые принципы по обе стороны от этого разрыва, которые относятся к одной, а не к другой, или применяются по-разному. Фразы типа «предпочитают композицию наследованию» хорошо известны в литературе ООП со статической типизацией. Они так же применимы на динамической стороне?

Например, в динамически типизированной среде может показаться, что степень детализации связи не превышает уровня метода. Другими словами, любой вызов данной функции только связывает вызывающего с этим конкретным интерфейсом, который любой класс может удовлетворить - или, другими словами, все, что крякает как эта конкретная утка.

В Java, с другой стороны, степень детализации связи может достигать уровня пакета. Не только конкретный вызов метода устанавливает контракт с другим классом / интерфейсом, но также связывает его с пакетом / jar / сборкой этого класса / интерфейса.

Разве такие различия порождают разные принципы и закономерности? Если так, были ли эти различия сформулированы? В книге Ruby Pickaxe есть раздел, который немного движется в этом направлении (Duck Typing / Classes Not Types), но мне интересно, есть ли что-нибудь еще. Мне известны шаблоны проектирования в Ruby , но я их не читал.

РЕДАКТИРОВАТЬ - Утверждалось, что Лисков не применяет то же самое в динамической среде, как в статической среде, но я не могу не думать, что это так. С одной стороны, нет высокоуровневого контракта со всем классом. Но не являются ли все обращения к какому-либо данному классу неявным контрактом, который должен выполняться дочерними классами так, как предписывает Лисков? Учтите следующее. Вызовы "do some bar stuff" создают контракт, который должен посещать дочерние классы. Разве это не случай «обработки специализированного объекта, как если бы он был базовым классом?»:

class Bartender
    def initialize(bar)
       @bar = bar
    end

    def do_some_bar_stuff
        @bar.open
        @bar.tend
        @bar.close
    end
end

class Bar
    def open
        # open the doors, turn on the lights
    end
    def tend
        # tend the bar
    end
    def close
        #clean the bathrooms
    end
end

class BoringSportsBar < Bar
    def open
        # turn on Golden Tee, fire up the plasma screen
    end

    def tend
        # serve lots of Bud Light
    end
end

class NotQuiteAsBoringSportsBar < BoringSportsBar
    def open
        # turn on vintage arcade games
    end
end

class SnootyBeerSnobBar < Bar
    def open
        # replace empty kegs of expensive Belgians
    end

    def tend
        # serve lots of obscure ales, porters and IPAs from 124 different taps
    end
end

# monday night
bartender = Bartender.new(BoringSportsBar.new)
bartender.do_some_bar_stuff

# wednesday night
bartender = Bartender.new(SnootyBeerSnobBar.new)
bartender.do_some_bar_stuff

# friday night
bartender = Bartender.new(NotQuiteAsBoringSportsBar.new)
bartender.do_some_bar_stuff

Ответы [ 4 ]

5 голосов
/ 17 декабря 2009

Существенная разница, которую вы затрагиваете, я думаю:

  • группа языков 1. Фактические методы, которые вызываются при вызове, например, object.method1, object.method2, object.method3, могут изменяться в течение всего времени существования объекта.

  • группа языков 2. Фактические методы, которые вызываются при вызове, например, object.method1, object.method2, object.method3, не могут изменяться в течение всего времени существования объекта.

Языки в группе 1, как правило, имеют динамическую типизацию и не поддерживают интерфейсы, проверенные во время компиляции, а языки в группе 2, как правило, имеют статическую типизацию и поддерживают замкнутые интерфейсы во время компиляции.

Я бы сказал, что все принципы ОО применимы к обоим, но

  • В группе 1 может потребоваться дополнительное (явное) кодирование для реализации проверок (во время выполнения вместо времени компиляции), чтобы утверждать, что новые объекты создаются со всеми соответствующими методами, подключенными для выполнения контракта интерфейса, как нет проверки соглашения об интерфейсе во время компиляции (если вы хотите сделать код группы 1 более похожим на группу 2)

  • в группе 2 может потребоваться дополнительное кодирование для моделирования изменений фактического метода, вызванного для вызова метода, с использованием дополнительных флагов состояния для вызова подметодов, или для обобщения метода или набора методов в ссылке к одному из нескольких объектов, прикрепленных к основному объекту, где каждый из нескольких объектов имеет разные реализации методов (если вы хотите сделать код группы 2 более похожим на код группы 1)

  • сами ограничения на дизайн в языках группы 2 делают их лучше для более крупных проектов, где простота общения (в отличие от понимания) становится более важной

  • отсутствие ограничений на дизайн в языках группы 1 облегчает работу с небольшими проектами, где программисту легче проверить, соблюдаются ли различные конструктивные ограничения сразу, просто потому, что код меньше

  • создание кода из одной группы языков, подобной другой, интересно и заслуживает изучения, но суть языковых различий заключается в том, насколько хорошо они помогают командам разных размеров (- я верю! :))

  • Существуют и другие различия

  • может потребоваться больше или меньше усилий для реализации ОО-проекта на одном или другом языке, в зависимости от конкретных принципов.


EDIT

Итак, чтобы ответить на ваш оригинальный вопрос, я рассмотрел

http://c2.com/cgi/wiki?PrinciplesOfObjectOrientedDesign

И

http://www.dofactory.com/patterns/Patterns.aspx

На практике принципы ОО не соблюдаются в системе по разным причинам (и, конечно, по некоторым причинам). Удовлетворительные причины, включенные в тех случаях, когда проблемы производительности перевешивают чисто проблемы качества дизайна, когда культурные преимущества альтернативной структуры / наименования перевешивают чисто проблемы качества дизайна и когда затраты на дополнительную работу по реализации функции, не являющейся стандартным способом для конкретного языка, перевешивают преимущества чистый дизайн.

Более грубые шаблоны, такие как Абстрактная фабрика, Строитель, Метод фабрики, Прототип, Адаптер, Стратегия, Командная цепочка, Мост, Прокси, Наблюдатель, Посетитель и даже MVC / MMVM, имеют тенденцию меньше использоваться в небольших системах, потому что количество количество сообщений о коде меньше, поэтому выгода от создания таких структур не так велика.

Более мелкозернистые шаблоны, такие как State, Command, Factory Method, Composite, Decorator, Facade, Flyweight, Memento, Template, возможно, более распространены в коде группы 1, но часто несколько шаблонов проектирования применяются не к объекту как таковому, а к различные части объекта, тогда как в группе 2 шаблоны кода обычно присутствуют по одному шаблону на объект.

ИМХО, в большинстве языков группы 1 имеет смысл думать о всех глобальных данных и функциях как о некоем объекте "Приложение". Я знаю, что мы стираем грани между процедурным и ОО-программированием, но этот вид кода определенно крякает как объект «приложения» во многих случаях! :)

Некоторые очень мелкозернистые шаблоны проектирования, такие как Iterator, как правило, встроены в языки группы 1.

3 голосов
/ 17 декабря 2009

Позвольте мне начать с того, что лично принцип ООП, который не работает как на динамически, так и на статически типизированных языках, не является принципом.

Тем не менее, вот пример:

Принцип сегрегации интерфейса (http://objectmentor.com/resources/articles/isp.pdf)) гласит, что клиенты должны зависеть от самого конкретного интерфейса, который отвечает их потребностям. Если клиентскому коду необходимо использовать два метода класса C, то C должен реализовать интерфейс I, содержащий только эти два метода и клиент будет использовать I, а не C. Этот принцип не имеет значения в динамически типизированных языках, где интерфейсы не нужны (поскольку интерфейсы определяют типы, а типы не нужны в языке, где переменные не содержат типов)

[править]

Второй пример - принцип обращения зависимостей (http://objectmentor.com/resources/articles/dip.pdf). Этот принцип утверждает, что это «стратегия зависимости от интерфейсов или абстрактных функций и классов, а не от конкретных функций и классов». Опять же, в динамически типизированном языке клиента код не зависит ни от чего - он просто определяет сигнатуры методов - тем самым устраняя этот принцип.

Третий пример - принцип подстановки Лискова (http://objectmentor.com/resources/articles/lsp.pdf).). Примером учебника для этого принципа является класс Square, который подклассирует класс Rectangle. И затем клиентский код, который вызывает метод setWidth () для переменной Rectangle, удивляется когда высота также изменяется, поскольку фактический объект является квадратом. Опять же, в динамически типизированном языке переменные не имеют типа, класс Rectangle не будет упомянут в клиентском коде, и, следовательно, таких сюрпризов не возникнет.

1 голос
/ 30 декабря 2010

У меня есть «радикальный» взгляд на все это: по моему мнению, при поддержке математики ООП не работает в статически типизированной среде для каких-либо интересных задач. Я определяю интересное как значение абстрактных отношений. Это легко доказать (см. «Проблема ковариации»).

Суть этой проблемы заключается в том, что концепции ООП обещают, что это способ моделирования абстракций и в сочетании с программированием контрактов, предоставляемым статической типизацией, отношения не могут быть реализованы без нарушения инкапсуляции. Просто попробуйте любой ковариантный бинарный оператор, чтобы увидеть: попробуйте реализовать «меньше чем» или «добавить» в C ++. Вы можете легко закодировать базовую абстракцию, но не сможете ее реализовать.

В динамических системах нет формализованных типов высокого уровня и нет необходимости в инкапсуляции, поэтому ОО действительно работает, в частности, системы на основе прототипов, такие как оригинальный Smalltalk, фактически предоставляют рабочие модели, которые вообще не могут быть закодированы со статическими ограничениями типизации.

Чтобы ответить на вопрос по-другому: фундаментальное предположение о самом вопросе изначально ошибочно. У ОО нет никаких последовательных принципов, потому что это не последовательная теория, потому что не существует каких-либо его моделей с достаточной мощностью, чтобы справляться с чем-либо, кроме простых задач программирования. Отличие заключается в том, что вы отказываетесь: в динамических системах вы отказываетесь от инкапсуляции, в статических системах вы просто переключаетесь на модели, которые работают (функциональное программирование, шаблоны и т. Д.), Поскольку все статически типизированные системы поддерживают эти вещи.

0 голосов
/ 17 декабря 2009

Интерфейсы могут добавить некоторый уровень накладных расходов, особенно если вы напрямую зависите от чужого API. Простое решение - не зависит от чужого API.

Пусть каждый объект говорит с интерфейсами, которые, как он хотел бы, существуют в идеальном мире. Если вы сделаете это, у вас получатся небольшие интерфейсы с небольшой областью действия. Поступая так, вы получите ошибки времени компиляции при изменении интерфейсов.

Чем меньше и конкретнее ваши интерфейсы, тем меньше «бухгалтерии» вам придется делать при изменении интерфейса.

Одним из реальных преимуществ статической типизации является не статическое знание того, какие методы вы можете вызывать, а гарантия того, что объекты-значения уже проверены ... если вам нужно имя, а имя должно быть <10 символов, создайте Назовите класс, который инкапсулирует эту проверку (хотя не обязательно какие-либо аспекты ввода-вывода - оставьте его типом чистого значения), и компилятор может помочь вам перехватывать ошибки во время компиляции, а не проверять во время выполнения. </p>

Если вы собираетесь использовать статический язык, используйте его в своих интересах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...