Существуют ли философские различия между Smalltalk OOP и Simula OOP?
Это вопрос, связанный с Java и С# vs С++ косвенно. Насколько я понимаю, С++ основан на Simula, но Java и С# являются более или менее из семейства Smalltalk.
Существуют ли философские различия между Smalltalk OOP и Simula OOP?
Это вопрос, связанный с Java и С# vs С++ косвенно. Насколько я понимаю, С++ основан на Simula, но Java и С# являются более или менее из семейства Smalltalk.
Несколько ключевых различий в стиле "Стиль" в расширенном баннере ООП.
Во всех случаях утверждение о системе статического или динамического типа означает преимущественно то или иное, проблема далека от четкого или четко определенного. Кроме того, многие языки выбирают размывание линии между выборами, поэтому это не список двоичных вариантов любым способом.
или "что означает foo.Bar(x)
?"
1 часто используется в статически типизированных фреймворках, где это ошибка, проверенная во время компиляции, чтобы такая реализация не существовала. Далее языки часто различают Bar (x) и Bar (y), если x и y - разные типы. Это перегрузка метода, и полученные методы с таким же именем рассматриваются как совершенно разные.
2 часто используется в динамических языках (которые, как правило, избегают перегрузки методов), поэтому возможно, что во время выполнения тип foo не имеет обработчика для сообщения с именем "Bar", разные языки обрабатывают это в разных пути.
Оба могут быть реализованы за кулисами тем же способом, если это необходимо (часто по умолчанию для второго, стиль Smalltalk должен вызывать функцию, но во всех случаях это не определяется определенным образом). Так как прежний метод часто может быть легко реализован как простые вызовы функции смещения указателя, он может быть легче сделан относительно быстро. Это не означает, что другие стили также не могут выполняться быстро, но при этом может потребоваться больше работы, чтобы гарантировать, что при этом не будет скомпрометирована большая гибкость.
или "Откуда берутся дети?"
Снова 1 имеет тенденцию происходить на статических языках, 2 в динамике, хотя это отнюдь не требование, которое они просто поддаются стилю.
или "что и как?"
Это не двоичный выбор. Большинство языков, основанных на классе, допускают концепцию абстрактных методов (те, которые еще не реализованы). Если у вас есть класс, в котором все методы являются абстрактными (называемый чистым виртуальным в С++), то то, что класс представляет собой, в значительной степени представляет собой интерфейс, хотя и тот, который, возможно, также определил некоторое состояние (поля). Истинный интерфейс не должен иметь никакого состояния (поскольку он определяет только то, что возможно, а не как это происходит.
Только более старые языки ООП, как правило, полагаются исключительно на то или другое.
VB6 имеет только интерфейсы и не имеет наследования реализации.
Simula позволяет вам объявлять чистые виртуальные классы, но вы можете создавать их (с ошибками во время использования)
или "Кто такой папа?"
Этот вопрос вызывает значительную дискуссию, тем более что это ключевой отличительный признак между реализацией COP OOP и многими современными статически типизированными языками, воспринимаемыми как возможные преемники, такие как С# и java.
или "что вы хотите сделать со мной?"
Часто это не все или ничего, это просто по умолчанию (обычно используемые языки OOP по умолчанию изменяются по умолчанию). Это может сильно повлиять на то, как язык структурирован. Многие в основном функциональные языки, включенные в функции ООП, по умолчанию имеют объекты, имеющие неизменяемое состояние.
или "Все ли объект?"
Это довольно сложно, так как такие методы, как автоматическое боксирование примитивов, кажутся похожими на все, но вы обнаружите, что существует несколько граничных случаев, когда эта "магия компилятора" обнаружена, и пословичный волшебник из страны Оз находится за занавеской в результате проблемы или ошибки. В языках с неизменяемостью по умолчанию это менее вероятно, так как ключевой аспект объектов (который они содержат как методы, так и состояние) означает, что вещи, похожие на объекты, но не имеющие меньше возможностей для осложнений.
или "Кто вы такой, как вы думаете?"
Более распространенный аспект языкового дизайна, а не один, чтобы войти сюда, но выбор, присущий этому решению, влияет на многие аспекты ООП, как упоминалось ранее.
Просто аспекты полиморфного позднего связывания могут зависеть от:
Чем более динамичный язык становится более сложным, эти решения, как правило, становятся, но наоборот, чем больше вводит пользователь языка, тем лучше, чем разработчик языка принимает решение. Приведя примеры здесь, можно было бы подумать о том, что безрассудно, поскольку статически типизированные языки могут быть изменены, чтобы включить динамические аспекты (например, С# 4.0).
Я бы поставил Java и С# в лагере Simula:
Smalltalk, будучи динамически типизированным, полностью отличается от четырех других языков, которые вы цитируете.
Smalltalk структурно типизирован (alias duck typing), в то время как остальные четыре номинально типизируются.
(Что такое Java и С# совместно с Smalltalk в основном базируется на VM, но мало влияет на стиль программирования).
Java и С# определенно не принадлежат к семейству Smalltalk. Алан Кей даже сказал, что когда он создал ООП, у него не было ничего подобного Java или С++. Java, С# и С++ все интерпретируют ООП примерно так же.
Языки, подобные Smalltalk и Ruby, имеют совершенно другую модель, основанную на передаче сообщений. В классах С++ существуют пространства имен для методов и состояний. Вызов метода привязывается во время компиляции. Smalltalk не связывает "вызов метода" до времени выполнения. Результатом этого является то, что в С++
foo->bar
скомпилирован для обозначения "вызов метода штриховки на объекте foo". Если bar не является виртуальным, я бы предположил, что адрес метода bar определенно ссылается.
В Smalltalk
foo bar
означает "отправить панель сообщений объекту foo". foo
может делать все, что захочет, с этим сообщением, когда оно поступит. Поведение по умолчанию - вызвать метод с именем bar
, но это не требуется. Это свойство используется в Ruby для доступа к столбцам ActiveRecord. Когда у вас есть объект ActiveRecord и вы отправляете ему имя столбца в своей таблице базы данных в виде сообщения, если нет метода с указанным именем, он проверяет, есть ли столбец по этому имени в таблице, и если возвращается значение.
Передача сообщений может показаться крошечной, нерелевантной деталью, но из нее остальная ООП легко протекает.
"ООП для меня означает только обмен сообщениями, локальное сохранение и защиту и скрытие процесса состояния, а также крайнее позднее связывание всех вещей. Это можно сделать в Smalltalk и в LISP. Возможно, существуют другие системы, в которых это возможно, но я не знаю о них". - Алан Кей, создатель Smalltalk
Eiffel - это статически типизированный, скомпилированный, многократный нативный чистый язык OOP.
Современный (и я использую термин легко) языки программирования OO. Цель C является наиболее похожей на smalltalk.
В С++, С# и Java: сообщения привязываются во время компиляции.
Вы можете думать о вызове метода как сообщении, отправляемом объекту.
В Objective C сообщения Smalltalk: сообщения связаны во время выполнения.
Я бы сказал, что статически типизированные и динамически типизированные ООП являются двумя отдельными дисциплинами в пределах одной школы ООП.
Java, С# и С++ следуют аналогичной стратегии ООП. Он основан на вызовах функций, которые связаны во время компиляции. В зависимости от того, что он вызывает, либо прямой вызов функции, либо смещение в vtable фиксируется, когда происходит компиляция. В отличие от Smalltalk OOP основан на передаче сообщений. Концептуально каждый вызов метода представляет собой сообщение получающему объекту, спрашивающее, имеет ли он метод под названием "Foo."
Smalltalk не имеет понятия интерфейсов. Он имеет только похожие методы. В группе языков С++ все связано с интерфейсами. Нельзя реализовать AddRef и Release без реализации QueryInterface (даже если это просто заглушка), потому что все они являются частью интерфейса IUnknown. В Smalltalk нет IUnknown. Существует только набор из трех функций, любой из которых может быть реализован или нет.
Я бы сказал, что существует принципиально большая разница между ООП на основе классов (из которых все Smalltalk, Simula, С# и Java являются примерами) и ООП на основе прототипов (которые начинаются с Self и наиболее распространены в JavaScript).
Помимо вышеуказанных пунктов, существует также концептуальное разбиение 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;
}