Абстрактный класс в Smalltalk-Squeak. Что это?

#smalltalk #squeak

#smalltalk #squeak

Вопрос:

Если я правильно понял, абстрактный класс — это тот, у которого есть хотя бы один абстрактный метод?

Теперь, если он абстрактный, то я, как предполагается, не могу создавать экземпляры этого класса?

Например, said Abst — это имя абстрактного класса (поскольку он содержит абстрактный метод), поэтому:

 a := Abst new.
  

является незаконным и должен выдавать ошибку / исключение?
или проблема должна возникнуть здесь:

 a := Abst class new.
  

?

ОБНОВЛЕНИЕ: Как и предлагалось, я создал следующий метод, который не позволит пользователю создавать экземпляры класса, но он не работает:

 makeAbstract: aClass    
    aClass compile: 'new
                ^ self subclassResponsibility'.
  

Ответ №1:

Добро пожаловать в Smalltalk! Одна из замечательных особенностей Smalltalk заключается в том, что он доверяет разработчикам, которые извлекают выгоду из власти, которая приходит с этим доверием. Поэтому такие слова, как «невозможно» и «незаконно», редко применимы.

Как и большинство других вещей, абстрактные классы в Smalltalk больше похожи на предложение / указатель, чем на жесткий закон. Две подсказки, которые вы ищете, — это #subclassResponsibility и #shouldNotImplement. Эти два метода являются подсказками для подклассов, включать или не включать конкретный метод. Проверьте отправителей на наличие примеров на изображении (всегда отличная отправная точка для вопросов).

Поскольку «абстрактный», как описано выше, действительно зависит от каждого метода, ваши примеры не будут генерировать ошибку (если только #subclassResponsibility или #shouldNotImplement не вызываются из initialize .

Две маленькие вещи:

  • Имена классов в Smalltalk пишутся с заглавной буквы, поэтому Abst, а не abst .
  • Поиск в Google проходит долгий путь. Три из четырех верхних ссылок для «абстрактного класса smalltalk» — это все, что вам нужно (эта, в частности, выглядела как надо).

ОБНОВЛЕНИЕ: если вы хотите сообщить пользователям вашего класса, что им не следует создавать экземпляры (как в вашем комментарии ниже), вы могли бы написать:

 Abstract>>new
    ^ self subclassResponsibility.
  

Тогда «Abstract new» -> ошибка, но «AbstractSubclass new» в порядке.

Хотя по-прежнему нет гарантии, что AbstractSubclass переопределил абстрактный метод (не #new , а тот, из-за которого вы в первую очередь захотели запретить создание экземпляра), на практике это не будет проблемой. Если бы вы действительно хотели, вы могли бы поставить проверку, возможно, в #initialize , которая гарантирует, что ни один из методов экземпляра не вызывает #subclassResponsibility , но не беспокойтесь, если у вас нет веской причины.

ОБНОВЛЕНИЕ 2: Ваш служебный метод для создания абстрактного класса будет:

 Class>>makeAbstract

    self class compile: 'new
                ^ self subclassResponsibility'.
  

Комментарии:

1. Спасибо, итак, если нет такого понятия, как «незаконно» или «невозможно», какова на самом деле связь между абстрактным классом и тем, что он не создает экземпляры такого класса? Например, у меня есть класс, и я добавляю абстрактный метод во время выполнения, а затем (все еще во время выполнения), когда я попытаюсь создать экземпляр этого класса, я хочу, чтобы он сгенерировал ошибку, чтобы он не позволил мне создать этот экземпляр. Где это должно быть обработано?

2. @Sean DeNigris, я пытался это сделать, но у меня не получилось ошибок при вызове new. Смотрите обновление выше.

3. Вы были очень близки. Смотрите мою правку в теле метода выше (добавлен «класс»). Ваш код помещает #new на стороне экземпляра. Кроме того, если вы поместите его в класс, вам не нужно передавать имя класса.

4. @Sean DeNigris, спасибо, теперь это работает. Итак, что я сделал, это добавил к экземпляру? Когда я только что скомпилировал AClass: ‘somecode’ для других методов, я мог видеть в браузере, что метод был добавлен, поэтому он добавлял методы без добавления «class», как вы написали. В чем разница? И да, я знаю, что имя класса не требуется, но makeAbstract — это метод, который я добавил в свой собственный класс, а не в сам класс.

5. @Sean DeNigris, это имеет какое-то отношение к метаклассу?

Ответ №2:

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

Ответ №3:

Да, абстрактный класс должен иметь по крайней мере один абстрактный метод, но нет, вы все еще можете создавать экземпляры этого класса.

Что вам следует сделать, так это создать конкретные классы, которые наследуются от абстрактного класса, для которого вы можете создавать экземпляры и вызывать методы.

Абстрактные методы в Smalltalk имеют конкретную реализацию, которая делает их и класс абстрактными:

 method
    self subclassResponsibility
  

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

Если вы видите ошибку, касающуюся ответственности за подклассы, ваш код либо вызвал метод в абстрактном классе, либо ваш подкласс не предоставил реализацию для метода.

Комментарии:

1. Я добавил метод, который переопределяет new некоторого класса с ^ self subclassResponsibility, но все равно я не получил ошибок при попытке создать экземпляр этого класса (см. Код в ОБНОВЛЕНИИ выше).

Ответ №4:

Я рекомендую вам прочитать книгу Pharo By Example. Вы можете найти его здесь: http://pharobyexample.org / и вы найдете много интересного. Это открытая книга, бесплатная, и вы можете скачать pdf. На самом деле, то, о чем вы спрашиваете, объясняется в главе 5, стр. 88.

Ответ №5:

(Исправленный) «new-blocker» создает небольшое неудобство для его конкретных подклассов: им придется переопределять new и они не смогут использовать новую функциональность каких-либо унаследованных суперклассов. Вы можете обойти это с помощью небольшой защиты, которая проверяет, действительно ли это абстрактный класс, который пытаются создать:

 AbstractClass class >> new
    self == AbstractClass ifTrue:[
        ^ self abstractClassInstantiationError
    ].
    ^ super new
  

(обратите внимание на идентичность -сравните здесь, которая работает, даже если вы наложили несколько абстрактных классов друг на друга)

Комментарии:

1. Вы можете увидеть аналогичную проблему (и решение) с абстрактными TestCase классами в SUnit: ищите разработчиков #isAbstract .