Каковы школы ООП?

Существуют ли философские различия между Smalltalk OOP и Simula OOP?

Это вопрос, связанный с Java и С# vs С++ косвенно. Насколько я понимаю, С++ основан на Simula, но Java и С# являются более или менее из семейства Smalltalk.

Ответы

Ответ 1

Несколько ключевых различий в стиле "Стиль" в расширенном баннере ООП.

Во всех случаях утверждение о системе статического или динамического типа означает преимущественно то или иное, проблема далека от четкого или четко определенного. Кроме того, многие языки выбирают размывание линии между выборами, поэтому это не список двоичных вариантов любым способом.

Полиморфное позднее связывание

или "что означает foo.Bar(x)?"

  • Иерархия типов сглажена для конкретной реализации на один экземпляр (часто делается с помощью vtable) и часто позволяет явно ссылаться на реализацию базовых классов,
    • Концептуально вы смотрите на наиболее специфический тип, который foo находится на callsite. Если он имеет реализацию Bar для вызываемого параметра x, если не выбран родительский элемент foo и процесс повторяется.
    • Примеры: С++/Java/С#, " Simula style" часто используется.
  • Чистое сообщение. Код в foo, который обрабатывает сообщения с именем "Bar", просят принять x. Только имя имеет значение, а не какие-либо предположения, которые, возможно, имели место на сайте вызова, о том, что именно должен был быть Бар. Контрастность с предыдущим стилем, в котором рассматривался метод, известен как нечто определенное на всем, что было известно о иерархии типов, определенной во время компиляции (хотя точное место в иерархии осталось до времени исполнения).
    • Примеры: Objective-C/Ruby, Smalltalk style" часто используется.

1 часто используется в статически типизированных фреймворках, где это ошибка, проверенная во время компиляции, чтобы такая реализация не существовала. Далее языки часто различают Bar (x) и Bar (y), если x и y - разные типы. Это перегрузка метода, и полученные методы с таким же именем рассматриваются как совершенно разные.

2 часто используется в динамических языках (которые, как правило, избегают перегрузки методов), поэтому возможно, что во время выполнения тип foo не имеет обработчика для сообщения с именем "Bar", разные языки обрабатывают это в разных пути.

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

Inheritance/Повторное использование

или "Откуда берутся дети?"

  • Основанный на классе
    • Реализации методов организованы в группы, называемые классами. Когда желательно наследование реализации, определяется класс, который расширяет родительский класс. Таким образом, он получает все открытые аспекты родителя (оба поля и методы) и может выбрать изменение определенных/всех этих аспектов, но не может удалить их. Вы можете добавлять и обновлять, но не удалять.
    • Примеры: С++/Java/С# (обратите внимание, что и SmallTalk, и Simula используют это)
  • Прототип основан на
    • Любой экземпляр объекта представляет собой просто набор идентифицированных методов (обычно идентифицируемых по имени) и указывается в виде (снова названных) полей. Всякий раз, когда требуется новый экземпляр этого типа, существующий экземпляр можно использовать для клонирования нового. Этот новый класс сохраняет копию состояния и методов предыдущего класса, но затем может быть изменен для удаления, добавления или изменения существующих именованных полей и методов.
    • Примеры: Self/JavaScript

Снова 1 имеет тенденцию происходить на статических языках, 2 в динамике, хотя это отнюдь не требование, которое они просто поддаются стилю.

Интерфейс или класс

или "что и как?"

  • Интерфейсы перечисляют требуемые методы. Это контракт
    • Примеры: VB6
  • Методы списка классов, которые требуются, но могут необязательно поставлять их реализацию
    • Примеры: Simula

Это не двоичный выбор. Большинство языков, основанных на классе, допускают концепцию абстрактных методов (те, которые еще не реализованы). Если у вас есть класс, в котором все методы являются абстрактными (называемый чистым виртуальным в С++), то то, что класс представляет собой, в значительной степени представляет собой интерфейс, хотя и тот, который, возможно, также определил некоторое состояние (поля). Истинный интерфейс не должен иметь никакого состояния (поскольку он определяет только то, что возможно, а не как это происходит.

Только более старые языки ООП, как правило, полагаются исключительно на то или другое.
VB6 имеет только интерфейсы и не имеет наследования реализации.
Simula позволяет вам объявлять чистые виртуальные классы, но вы можете создавать их (с ошибками во время использования)

Одиночное или множественное наследование

или "Кто такой папа?"

  • Single
    • Только один тип может быть родителем для другого. В вышеописанной форме Class вы можете расширить (принять реализацию) только один тип. Обычно эта форма включает концепцию интерфейсов в качестве первоклассных аспектов языка, чтобы компенсировать это.
    • преимущества включают более чистые метаданные и интроспекцию, более простые языковые правила.
    • осложнения включают затруднение приведения полезных методов в объем (такие как MixIns и методы расширения, чтобы уменьшить эту проблему)
    • Примеры: С#/java
  • Несколько - вы можете расширить несколько классов
    • преимущества включают в себя определенные структуры, которые легче моделировать и проектировать.
    • осложнения включают сложные правила разрешения конфликтов, особенно когда существуют перегруженные методы, которые могут принимать либо родительский тип.
    • Примеры: С++/Eiffel

Этот вопрос вызывает значительную дискуссию, тем более что это ключевой отличительный признак между реализацией COP OOP и многими современными статически типизированными языками, воспринимаемыми как возможные преемники, такие как С# и java.

Изменчивость

или "что вы хотите сделать со мной?"

  • Mutable
    • Объекты, созданные после создания, могут изменить свое состояние.
  • Imutable
    • Объекты, созданные после создания, не могут быть изменены.

Часто это не все или ничего, это просто по умолчанию (обычно используемые языки OOP по умолчанию изменяются по умолчанию). Это может сильно повлиять на то, как язык структурирован. Многие в основном функциональные языки, включенные в функции ООП, по умолчанию имеют объекты, имеющие неизменяемое состояние.

"Перфективность" их ООП

или "Все ли объект?"

  • Абсолютно все в системе рассматривается как объект (возможно, вплоть до самих методов, которые являются просто другим видом объекта и могут взаимодействовать таким же образом, как и другие объекты).
    • Примеры: SmallTalk
  • Не все это объект, вы не можете передавать сообщения ко всему (хотя система может перепрыгивать через обручи, чтобы они казались такими, как вы можете)
    • Примеры: С++/С#/Java (см. примечание *)

Это довольно сложно, так как такие методы, как автоматическое боксирование примитивов, кажутся похожими на все, но вы обнаружите, что существует несколько граничных случаев, когда эта "магия компилятора" обнаружена, и пословичный волшебник из страны Оз находится за занавеской в ​​результате проблемы или ошибки. В языках с неизменяемостью по умолчанию это менее вероятно, так как ключевой аспект объектов (который они содержат как методы, так и состояние) означает, что вещи, похожие на объекты, но не имеющие меньше возможностей для осложнений.

  • В отношении Java/С# autoboxing (или в С#) позволяет обрабатывать синтаксически любую переменную, как если бы это был объект, но на самом деле это не так, и это проявляется в таких областях, как попытка блокировки объекта с автообъектом (отклоняется компилятором как это было бы очевидной ошибкой).

Статический или динамический

или "Кто вы такой, как вы думаете?"

Более распространенный аспект языкового дизайна, а не один, чтобы войти сюда, но выбор, присущий этому решению, влияет на многие аспекты ООП, как упоминалось ранее.

Просто аспекты полиморфного позднего связывания могут зависеть от:

  • Тип объекта, которому передается сообщение (во время компиляции/времени выполнения)
  • Тип параметра (s), которые передаются (во время компиляции/времени выполнения)

Чем более динамичный язык становится более сложным, эти решения, как правило, становятся, но наоборот, чем больше вводит пользователь языка, тем лучше, чем разработчик языка принимает решение. Приведя примеры здесь, можно было бы подумать о том, что безрассудно, поскольку статически типизированные языки могут быть изменены, чтобы включить динамические аспекты (например, С# 4.0).

Ответ 2

Я бы поставил Java и С# в лагере Simula:

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

  • Smalltalk структурно типизирован (alias duck typing), в то время как остальные четыре номинально типизируются.

(Что такое Java и С# совместно с Smalltalk в основном базируется на VM, но мало влияет на стиль программирования).

Ответ 3

Java и С# определенно не принадлежат к семейству Smalltalk. Алан Кей даже сказал, что когда он создал ООП, у него не было ничего подобного Java или С++. Java, С# и С++ все интерпретируют ООП примерно так же.

Языки, подобные Smalltalk и Ruby, имеют совершенно другую модель, основанную на передаче сообщений. В классах С++ существуют пространства имен для методов и состояний. Вызов метода привязывается во время компиляции. Smalltalk не связывает "вызов метода" до времени выполнения. Результатом этого является то, что в С++

foo->bar

скомпилирован для обозначения "вызов метода штриховки на объекте foo". Если bar не является виртуальным, я бы предположил, что адрес метода bar определенно ссылается.

В Smalltalk

foo bar

означает "отправить панель сообщений объекту foo". foo может делать все, что захочет, с этим сообщением, когда оно поступит. Поведение по умолчанию - вызвать метод с именем bar, но это не требуется. Это свойство используется в Ruby для доступа к столбцам ActiveRecord. Когда у вас есть объект ActiveRecord и вы отправляете ему имя столбца в своей таблице базы данных в виде сообщения, если нет метода с указанным именем, он проверяет, есть ли столбец по этому имени в таблице, и если возвращается значение.

Передача сообщений может показаться крошечной, нерелевантной деталью, но из нее остальная ООП легко протекает.

"ООП для меня означает только обмен сообщениями, локальное сохранение и защиту и скрытие процесса состояния, а также крайнее позднее связывание всех вещей. Это можно сделать в Smalltalk и в LISP. Возможно, существуют другие системы, в которых это возможно, но я не знаю о них". - Алан Кей, создатель Smalltalk

Ответ 4

Eiffel - это статически типизированный, скомпилированный, многократный нативный чистый язык OOP.

http://dev.eiffel.com

Ответ 5

Современный (и я использую термин легко) языки программирования OO. Цель C является наиболее похожей на smalltalk.

Messaages:

В С++, С# и Java: сообщения привязываются во время компиляции.
Вы можете думать о вызове метода как сообщении, отправляемом объекту.

В Objective C сообщения Smalltalk: сообщения связаны во время выполнения.

Ответ 6

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

Ответ 7

Java, С# и С++ следуют аналогичной стратегии ООП. Он основан на вызовах функций, которые связаны во время компиляции. В зависимости от того, что он вызывает, либо прямой вызов функции, либо смещение в vtable фиксируется, когда происходит компиляция. В отличие от Smalltalk OOP основан на передаче сообщений. Концептуально каждый вызов метода представляет собой сообщение получающему объекту, спрашивающее, имеет ли он метод под названием "Foo."

Smalltalk не имеет понятия интерфейсов. Он имеет только похожие методы. В группе языков С++ все связано с интерфейсами. Нельзя реализовать AddRef и Release без реализации QueryInterface (даже если это просто заглушка), потому что все они являются частью интерфейса IUnknown. В Smalltalk нет IUnknown. Существует только набор из трех функций, любой из которых может быть реализован или нет.

Ответ 8

Я бы сказал, что существует принципиально большая разница между ООП на основе классов (из которых все Smalltalk, Simula, С# и Java являются примерами) и ООП на основе прототипов (которые начинаются с Self и наиболее распространены в JavaScript).

Ответ 9

Помимо вышеуказанных пунктов, существует также концептуальное разбиение Smalltalk vs. Simula.

Концептуально "Smalltalk-style" обычно указывает, что метод, выполняемый при вызове сообщения, определяется во время выполнения, помогая полиморфизму.

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

В середине у нас есть Java: все методы виртуальны по умолчанию, но статически типизированы и имеют диспетчеризацию типа компиляции.

Пример:

// C++
class Base {
  void doSomething() {
    cout << "Base::doSomething() called!\n";
  }
}
class Derived : Base {
  void doSomething() {
     cout << "Derived::doSomething() called!\n";
  }
}
int main() {
  Base* b = new Base();
  Derived* d = new Derived();
  b->doSomething(); // prints "Base::doSomething() called!"
  d->doSomething(); // prints "Derived::doSomething() called!"
  Base* d2 = d;     // OK; Liskov substitution principle.
  d2->doSomething(); // prints "Base::doSomething called!"  (!)
  delete b;
  delete d;
  return 0;
}

VS:

// Objective-C
//Base.h
@interface Base
{
}
-(void)doSomething
@end
//Base.m
#import "Base.h"
@implementation Base
-(void) doSomething {
  printf("doSomething sent to Base!");
}
@end
//Derived.h
#import "Base.h"
#import "Base.m"
@interface Derived : Base
{
}
@end
//Derived.m
#import "Derived.h"
@implementation Derived
-(void) doSomething {
  printf("doSomething sent to Derived!")
}
@end

//Main.m
#import "Base.h"
#import "Base.m"
#import "Derived.h"
#import "Derived.m"
int main() {
  Base* b = [[Base alloc] init];
  Derived* d = [[Derived alloc] init];
  [b doSomething]; // prints "doSomething sent to Base!"
  [d doSomething]; // prints "doSomething sent to Derived!"
  Base* d2 = d;
  [d2 doSomething]; // prints "doSomething sent to Derived!"
  [b release];
  [d release];
  return 0;
}