В Smalltalk вы обычно реализуете #printOn:
и получаете #asString
от его унаследованной версии, которая идет по строкам
Object >> asString
| stream |
stream := '' writeStream.
self printOn: stream.
^stream contents
Реальная реализация этого метода может немного отличаться в вашей среде, идея остается прежней.
Поскольку это дано, обычно хорошей идеей является реализация #printOn:
вместо #asString
. В вашем случае это будет реализовано как
Element >> printOn: aStream
aStream
nextPutAll: 'Element with width ';
nextPutAll: width asString;
nextPutAll: ' and height ';
nextPutAll: height asString
, а затем, как указали Джейк и Люкер,
Element >> asDescription
^self asString
Другими словами, вы (обычно) не хотите реализовывать #asString
, но #printOn:
. Этот подход лучше, поскольку он использует преимущества наследования и обеспечивает согласованность между #printOn:
и #asString
, что обычно ожидается. Кроме того, это даст вам возможность начать знакомство с Streams
, которые играют центральную роль в Smalltalk.
Заметьте, кстати, что в моей реализации я использовал width asString
и heigh asString
. Ваш код пытается объединить (дважды) String
с Number
:
'Element with width ', width, ' and height ', height.
, который не будет работать, поскольку вы можете объединять только экземпляры String
с #,
.
Однако на большинстве диалектов вы можете избежать отправки #asString
, используя #print:
вместо #nextPutAll:
, что-то вроде:
Element >> printOn: aStream
aStream
nextPutAll: 'Element with width ';
print: width;
nextPutAll: ' and height ';
print: height
, который немного менее многословен и поэтому предпочтителен.
И последнее. Я бы порекомендовал изменить первую строку выше с этой:
nextPutAll: self class name;
nextPutAll: ' with width ';
вместо жесткого кодирования имени класса. Это может оказаться полезным, если в будущем вы подклассом Element
, потому что вам не нужно настраивать #printOn:
и любые его производные (например, #asDescription
).
Заключительная мысль: я бы переименовал селектор #asDescription
в #description
. Предлог as
предназначен для преобразования объекта в другой класс (вот почему #asString
в порядке). Но, похоже, это не так.
Приложение: почему?
Существует причина, по которой #asString
реализован в терминах #printOn:
, а не наоборот: общность . Хотя усилия (сложность кода) одинаковы, #printOn:
явно выиграл, поскольку он будет работать с любым символом Stream
. В частности, он будет работать без каких-либо изменений с
- Файлы (экземпляры
FileStream
)
- Розетки (экземпляры
SocketStream
)
-
Transcript
Другими словами, благодаря реализации #printOn:
можно получить #asString
бесплатно (наследование) и - одновременно - возможность выгрузить представление объекта в файлы и сокеты. Transcript
особенно интересен, поскольку он поддерживает протокол Stream
для записи и, следовательно, может использоваться для тестирования перед отправкой любых байтов на внешние устройства.
Помните!
В Smalltalk цель состоит в том, чтобы иметь объекты, поведение которых простое и общее сразу, не просто простое!