Cet article décrit l'une des approches de la prochaine étape de développement de la POO (programmation orientée objet). L'approche classique de la POO est basée sur le concept d'héritage, qui à son tour impose de sérieuses restrictions à l'utilisation et à la modification du code prêt à l'emploi. Lors de la création de nouvelles classes, il n'est pas toujours possible d'hériter de classes existantes (le problème de l' héritage en forme de losange ) ou de modifier des classes existantes dont de nombreuses autres classes ont déjà hérité (une classe de base fragile (ou trop gonflée)). Lors du développement du langage de programmation Delight, une approche alternative a été choisie pour travailler avec les classes et leur composition - CPC (programmation orientée composants).
Droit au but
, . , . , , . , - , .
, . . .
class BaseBehavior
unitPos: UnitPos [shared]
fn DoTurn [virtual]
class PathBuilder
unitPos: UnitPos [shared]
fn Moving:boolean [virtual]
...
fn BuildPath(x:int, y:int) [virtual]
...
// ... and some more helper functions ...
BaseBehavior - , , .
PathBuilder - ( ).
[shared] .
, :
class SimpleBehavior
base: BaseBehavior [shared]
path: PathBuilder [shared]
fne DoTurn // override of BaseBehavior.DoTurn
if path.Moving = false
path.BuildRandomPath
class AgressiveBehavior
open SimpleBehavior [shared]
fne DoTurn // override of SimpleBehavior.DoTurn
d: float = path.GetDistance(player.x, player.y) // get distance from this unit to player
if d < 30
path.BuildPath(player.x, player.y) // run to player
else
nextFn // inherited call to next DoTurn
class ScaredBehavior
open SimpleBehavior [shared]
fne DoTurn // override of SimpleBehavior.DoTurn
d: float = path.GetDistance(player.x, player.y) // get distance from this unit to player
if d < 50
path.BuildPathAwayFrom(player.x, player.y) // run away from player
else
nextFn // inherited call to next DoTurn
:
SimpleBehavior - .
AgressiveBehavior - , . SimpleBehavior.
ScaredBehavior - , SimpleBehavior.
open - .
fne - (override) .
nextFn - .
, :
class UncertainBehavior
open AgressiveBehavior [shared]
open ScaredBehavior [shared]
"" . , DoTurn, AgressiveBehavior.DoTurn. , . , ScaredBehavior.DoTurn - , . , SimpleBehavior.DoTurn .
(AgressiveBehavior), (ScaredBehavior) (UncertainBehavior). ? ? ? . . :
class PathBuilder_air //
path: PathBuilder [shared]
fne BuildPath(x:int, y:int)
...
class PathBuilder_water //
path: PathBuilder [shared]
fne BuildPath(x:int, y:int)
...
:
class Shark
open PathBuilder_water [shared]
open AgressiveBehavior [shared]
"", , AgressiveBehavior, , PathBuilder (shared), AgressiveBehavior ( SimpleBehavior) PathBuilder_water ( PathBuilder). AgressiveBehavior , . , - , :
class Fish
open PathBuilder_water [shared]
open ScaredBehavior [shared]
class Eagle
open PathBuilder_air [shared]
open UncertainBehavior [shared]
class Pigeon
open PathBuilder_air [shared]
open ScaredBehavior [shared]
class Wolf
open AgressiveBehavior [shared]
, - - -.
Delight
Delight :
class NonVirtualClass
val: OtherClass
fn SomeFn
Trace('Hello world')
val , OtherClass.
, , [virtual]
fn SomeFn [virtual]
Trace('Hello virtual world')
/ fne ( fn)
fne SomeFn
Trace('Hello overrided world')
( ) . , , [shared] (), fne :
class BaseClass
fn SomeFn [virtual]
Trace('Hello virtual world')
class NewClass
base: BaseClass [shared]
fne SomeFn
Trace('Hello overrided world')
nextFn
nextFn .
( ) ++
class BaseClass
{
public:
virtual void SomeFn()
{
Trace('Hello virtual world');
}
};
class NewClass : public virtual BaseClass
{
virtual void SomeFn() override
{
Trace('Hello overrided world');
BaseClass::SomeFn();
}
};
[shared], . , shared , , [shared] , , [shared] ( vtable).
:
class Base
val: int
class ClsA
base: Base [shared]
class ClsB
base: Base [shared]
class ClsC
a: ClsA [shared]
b: ClsB [shared]
ClsC, (Base, ClsA, ClsB) val () . , ++.
, ( , , ), . Delight open ( ). , ( this).
class ClsA
open Base [shared]
fne Constructor
val = 10
, . , :
, (shared) , ;
(shared) , .
, :
, ;
.
, nextFn. , (virtual call), (inherited call).
, :
class Base
fn SomeVirtFn [virtual]
Trace('Base')
class ClsA
open Base [shared]
fne SomeVirtFn
Trace('ClsA')
class ClsB
open Base [shared]
fne SomeVirtFn
Trace('ClsB')
class ClsC
open ClsA [shared]
open ClsB [shared]
fne SomeVirtFn
Trace('ClsC')
....
fn Main
c: ClsC
c.SomeVirtFn
:
ClsC
ClsA
ClsB
Base
Cette approche permet de surcharger les fonctions d'une classe par une autre, tout en étant au même niveau de la hiérarchie. Grâce à cela, les classes peuvent être constituées de composants prêts à l'emploi qui se chevauchent ou complètent les fonctions d'autres personnes, ce qui facilite grandement la composition du code. Pendant la composition, la fonctionnalité principale de la classe finale est décomposée en plusieurs blocs de construction de base, dont la combinaison donne les résultats souhaités. Delight prend également en charge la composition de code statique, mais c'est déjà important pour un autre article.