Или даже так, «Pure Pascal» версия :)
- Код: Выделить всё
type
PPetBehaviour = ^TPetBehaviour;
TPet = object
Name: AnsiString;
Behaviour: PPetBehaviour;
constructor Init(const _Name: AnsiString);
procedure TellAboutYourSelf; inline;
end;
TPetBehaviour = record
TellAboutYourSelf: procedure (const Pet: TPet);
end;
constructor TPet.Init(const _Name: AnsiString);
begin
Name := _Name;
end;
procedure TPet.TellAboutYourSelf;
begin
Behaviour^.TellAboutYourSelf(Self);
end;
procedure TCat_TellAbout(const Pet: TPet); // TCat implementation part
begin
Writeln('Я кот по имени ', Pet.Name, '.');
end;
procedure TDog_TellAbout(const Pet: TPet); // TDog implementation part
begin
Writeln('Я ', Pet.Name, ', гав-гав!');
end;
const
TCat: TPetBehaviour = ( // declaration of TCat
TellAboutYourSelf: @TCat_TellAbout
);
TDog: TPetBehaviour = ( // declaration of TDog
TellAboutYourSelf: @TDog_TellAbout
);
// using
var
Pet: TPet;
begin
Pet.Init('Бусик');
Pet.Behaviour := @TCat;
Pet.TellAboutYourSelf;
Pet.Behaviour := @TDog;
Pet.TellAboutYourSelf;
end.
Что лично мне не нравится в подобных подходах:
1. Каждый «виртуальный» метод нужно реализовывать через явный параметр объекта (Animal), поля и методы не видны сами по себе.
2. Не вполне понятно как правильно объявить protected-поля в TAnimal, чтобы только потомки TTellAbout их видели.
object не привносит сам по себе что-то новое функционально (всё можно реализовать самостоятельно через ссылки на функции), но даёт некий синтаксический сахар и хотелось бы сохранить по-максимуму наглядность реализации конкретный классов.
Это я выступаю в защиту параметризации конструктора, а не смены поведения на лету (что действительно лучше реализовывать самостоятельно, а не хакать vmt).