Composition au lieu d'héritage dans le langage de programmation Delight

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.








All Articles