Delightプログラミング言語での継承ではなく構成

この記事では、OOP(オブジェクト指向プログラミング)の開発の次の段階へのアプローチの1つについて説明します。OOPへの古典的なアプローチは、継承の概念に基づいており、継承は、既製のコードの使用と変更に深刻な制限を課します。新しいクラスを作成するときに、既存のクラスから継承したり(ひし形の継承の問題)、他の多くのクラスがすでに継承している既存のクラスを変更したり(壊れやすい(または過度に肥大化した)基本クラス)できるとは限りませんDelightプログラミング言語を開発する際、クラスとその構成を操作するための代替アプローチであるCPC(コンポーネント指向プログラミング)が選択されました。





ポイントにまっすぐ

言語の基本、その構文、およびCPCの規則から始める必要があります。しかし、これはかなり退屈なので、特定のゲームの例に直接進みましょう。構成自体はOOPと同じ原則に基づいて構築されているため、以下のすべてを理解するには、OOPの完全な知識が必要です。それがどのように機能するかについての詳細は、この後の次のセクションで見つけることができます。





一部のクリーチャーがマップ内を移動できる条件付きゲームの例を考えてみましょう。これらの生き物の行動のコードを書いてみましょう。基本クラスから始めましょう。





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
      
      



このアプローチにより、階層の同じレベルにありながら、あるクラスの関数を別のクラスでオーバーロードできます。このおかげで、クラスは他の人の機能と重複または補完する既製のコンポーネントで構成でき、コードの構成が大幅に容易になります。構成中に、最終クラスの主な機能はいくつかの基本的な構成要素に分解され、それらの組み合わせによって望ましい結果が得られます。Delightは静的コード構成もサポートしていますが、これはすでに別の記事の資料です。








All Articles