Почему одноэлементные объекты более объектно-ориентированы?
В статье "Программирование в Scala: подробное пошаговое руководство" автор сказал:
Одним из способов, которым Scala является более объектно-ориентированным, чем Java, является то, что классы в Scala не могут иметь статических членов. Вместо этого в Scala есть одноэлементные объекты.
Почему одноэлементный объект более объектно-ориентирован? Какая польза не от использования статических элементов, а от одноэлементных объектов?
7 ответов
Попытка "большой картины"; большая часть этого была рассмотрена в других ответах, но, кажется, нет единого всеобъемлющего ответа, который бы объединял все это и объединял точки. Так что здесь идет...
Статические методы класса не являются методами объекта, это означает, что:
- Статические члены не могут быть унаследованы от родительского класса / признака
- Статические члены не могут быть использованы для реализации интерфейса
Статические члены класса не могут быть переданы в качестве аргумента какой-либо функции
(и из-за вышеуказанных пунктов...)
- Статические члены не могут быть переопределены
- Статические члены не могут быть полиморфными
Суть объектов в том, что они могут наследовать от родительских объектов, реализовывать интерфейсы и передаваться в качестве аргументов - статические члены не имеют ни одного из этих свойств, поэтому они не являются действительно объектно-ориентированными, они представляют собой не что иное, как пространство имен.
Синглтон-объекты, с другой стороны, являются полноправными членами объектного сообщества.
Еще одно очень полезное свойство синглетонов заключается в том, что их можно легко изменить в более поздний момент времени, чтобы они не были синглетонами, это особенно болезненный рефакторинг, если вы начинаете со статических методов.
Представьте, что вы разработали программу для печати адресов и представляли взаимодействия с принтером с помощью статических методов для некоторого класса, а затем вы захотите добавить второй принтер и позволить пользователю выбирать, какой из них он будет использовать... не будет забавным опытом!
Объекты-одиночки ведут себя как классы в том смысле, что они могут расширять / реализовывать другие типы.
Я не могу сделать это в Java только с помощью статических классов - это просто сахар по сравнению с одноэлементным шаблоном Java с getInstance
это позволяет (по крайней мере) более хорошие пространства имен / стабильные идентификаторы и скрывает различие.
Подсказка: это называется объектно- ориентированным программированием.
Шутки в сторону.
Может быть, я упускаю что-то принципиально важное, но я не понимаю, в чем суть суеты: объекты более объектно-ориентированы, чем не-объекты, потому что они являются объектами. Это действительно нуждается в объяснении?
Примечание: хотя это звучит так, я действительно не пытаюсь звучать самодовольно. Я посмотрел на все остальные ответы и нашел их ужасно запутанными. Для меня очевидно, что объекты и методы являются более объектно-ориентированными, чем пространства имен и процедуры (что на самом деле представляют собой статические "методы") по самому определению "объектно-ориентированные".
Альтернативой наличию одноэлементных объектов было бы создание классов самими объектами, как, например, Ruby, Python, Smalltalk, Newspeak.
Для статических членов нет объекта. Класс действительно просто является пространством имен.
В синглтоне всегда есть хотя бы один объект.
Честно говоря, это расщепление волосков.
Он более объектно-ориентирован в том смысле, что для данного класса Scala каждый вызов метода является вызовом метода для этого объекта. В Java статические методы не взаимодействуют с состоянием объекта.
На самом деле, учитывая объект a
класса A
статическим методом m()
звонить считается плохой практикой a.m()
, Вместо этого рекомендуется позвонить A.m()
(Я верю, что "Затмение" предупредит вас). Статические методы Java не могут быть переопределены, они могут быть просто скрыты другим методом:
class A {
public static void m() {
System.out.println("m from A");
}
}
public class B extends A {
public static void m() {
System.out.println("m from B");
}
public static void main(String[] args) {
A a = new B();
a.m();
}
}
Что будет a.m()
Распечатать?
В Scala вы вставляете статические методы в сопутствующие объекты A и B, и цель будет более ясной, как если бы вы явно ссылались на спутника A или B.
Добавление того же примера в Scala:
class A
object A {
def m() = println("m from A")
}
class B extends A
object B {
def m() = println("m from B")
def main(args: Array[String]) {
val a = new B
A.m() // cannot call a.m()
}
}
Есть некоторые различия, которые могут быть важны в некоторых сценариях. В Java вы не можете переопределить статический метод, поэтому, если у вас есть класс со статическими методами, вы не сможете настроить и переопределить часть его поведения. Если вы использовали одноэлементный объект, вы можете просто подключить одноэлемент, созданный из подкласса.
Это действительно маркетинговая вещь. Рассмотрим два примера:
class foo
static const int bar = 42;
end class
class superfoo
Integer bar = ConstInteger.new(42);
end class
Теперь, какие здесь наблюдаемые различия?
- на хорошем языке созданное дополнительное хранилище остается тем же.
- Foo.bar и Superfoo.bar имеют абсолютно одинаковые подписи, доступ и т. Д.
- Superfoo.bar может быть размещен по-разному, но это деталь реализации
Это напоминает мне о религиозных войнах 20 лет назад по поводу того, были ли C++ или Java "действительно" объектно-ориентированными, поскольку в конце концов оба представленных примитивных типа не являются "настоящими" объектами - так, например, вы не можете наследовать от int
но может от Integer
,