Tvillingmönster

Inom mjukvaruteknik är Twin -mönstret ett mjukvarudesignmönster som gör det möjligt för utvecklare att modellera flera arv i programmeringsspråk som inte stöder multipelt arv. Detta mönster undviker många av problemen med multipelt arv.

Definition

Istället för att ha en enda klass som är härledd från två superklasser, ha två separata underklasser som var och en härrör från en av de två superklasserna. Dessa två underklasser är nära kopplade, så båda kan ses som ett tvillingobjekt med två ändar.

Tillämplighet

Tvillingmönstret kan användas:

  • att modellera multipelt arv på ett språk där multipelarv inte stöds
  • för att undvika vissa problem med multipla arv.

Strukturera

Det kommer att finnas två eller flera föräldraklasser som används för att ärvas. Det kommer att finnas underklasser som var och en kommer från en av superklasserna. Underklasserna är ömsesidigt länkade via fält, och varje underklass kan åsidosätta metoderna som ärvts från superklassen. Nya metoder och fält deklareras vanligtvis i en underklass.

Följande diagram visar den typiska strukturen för multipelt arv:

Typical multiple inheritance

Följande diagram visar tvillingmönsterstrukturen efter att ha ersatt den tidigare multipelarvsstrukturen:

Twin pattern

Samarbeten

Varje barnklass är ansvarig för det protokoll som ärvt från sin förälder. Den hanterar meddelanden från detta protokoll och vidarebefordrar andra meddelanden till sin partnerklass.

Klienter av tvillingmönstret refererar direkt till ett av tvillingobjekten och det andra via dess tvillingfält.

Klienter som förlitar sig på överordnade klassers protokoll kommunicerar med objekt i respektive barnklass.

Exempelkod

Följande kod är en skissad implementering av ett datorspelbräde med rörliga bollar.

Klass för spelplanen:

 
     
       
      
    
 public  class  Gameboard  utökar  Canvas  {  public  int  width  ,  height  ;  public  GameItem  firstItem  ;  } 

Kodskiss för GameItem-klassen:

    
     
      
     
       
         
         
         
       
         
                 
              
    
             
              
            
          
          
         
    
 public  abstract  class  GameItem  {  Gameboard  board  ;  int  posX  ,  posY  ;  GameItem  nästa  ;  offentlig  abstrakt  void  draw  ();  public  abstract  void  click  (  MouseEvent  e  );  offentliga  abstrakta  booleska  skär  (  GameItem  annat  );  public  abstract  void  collideWith  (  GameItem  other  );  public  void  check  ()  {  GameItem  x  ;  for  (  x  =  board  .  firstItem  ;  x  !=  null  ;  x  =  x  .  next  )  if  (  skär  (  x  ))  kolliderar med  (  x  );  }  public  static  BallItem  newBall  (  int  posX  ,  int  posY  ,  int  radius  )  {  //method of GameBoard  BallItem  ballItem  =  new  BallItem  (  posX  ,  posY  ,  radius  );  BallThread  ballThread  =  ny  BallThread  ();  ballArtikel  .  tvilling  =  bollTråd  ;  bollTråd  .  twin  =  ballArtikel  ;  returnera  bollArtikel  ;  }  } 

Kodskiss för BallItem-klassen:

     
     
        
     
       
            
              
       
          
         
           
    
         
           
                 
                     
                     
                     
          
    
         
            
                  
    
 public  class  BallItem  utökar  GameItem  {  BallThread  twin  ;  int  radie  ;  int  dx  ,  dy  ;  boolesk  suspenderad  ;  public  void  draw  ()  {  board  .  getGraphics  ().  drawOval  (  posX  -  radie  ,  posY  -  radie  ,  2  *  radie  ,  2  *  radie  );  }  public  void  move  ()  {  posX  +=  dx  ;  posY  +=  dy  ;  }  public  void  klicka på  ()  {  if  (  suspended  )  twin  .  CV  ();  annars  tvilling  .  suspendera  ();  avstängd  =  !  avstängd  ;  }  public  boolean  intersects  (  GameItem  other  )  {  if  (  other  instansof  Wall  )  return  posX  -  radius  <=  other  .  posX  &&  annat  .  posX  <=  posX  +  radie  ||  posY  -  radie  <=  annan  .  posY  &&  annat  .  posY  <=  posY  +  radie  ;  annars  returnerar  falskt  ;  }  public  void  collideWith  (  GameItem  other  )  {  Wall  wall  =  (  Wall  )  other  ;  if  (  vägg  .  ärVertikal  )  dx  =  -dx  ;  _  annat  dy  =  -  dy  ;  }  } 

Kodskiss för BallThread-klassen:

     
     
       
          
               
        
    
 public  class  BallThread  utökar  tråden  {  BallItem  twin  ;  public  void  run  ()  {  while  (  true  )  {  twin  .  rita  ();  /*radera*/  tvilling  .  flytta  ();  tvilling  .  rita  ();  }  }  } 

Implementering av tvillingmönstret

Följande frågor bör övervägas:

  • Dataabstraktion - partnerklasser i tvillingklassen måste vara tätt kopplade, eftersom de förmodligen måste komma åt varandras privata fält och metoder. I Java kan detta uppnås genom att placera partnerklasserna i ett gemensamt paket och ge paketsynlighet för de nödvändiga fälten och metoderna. I Modula-3 och i Oberon kan partnerklasser placeras i en gemensam modul.
  • Effektivitet - Eftersom tvillingmönstret använder komposition som kräver vidarebefordran av meddelanden, kan tvillingmönstret vara mindre effektivt än arv. Men eftersom multipelarv ändå är något mindre effektivt än enkelarv, kommer omkostnaderna inte att vara ett stort problem.
  • Cyklisk referens - Tvillingmönstret förlitar sig på att varje tvilling refererar till den andra tvillingen, vilket orsakar ett cykliskt referensscenario. Vissa språk kan kräva att sådana cykliska referenser hanteras speciellt för att undvika en minnesläcka . Till exempel kan en referens behöva göras "svag" för att cykeln ska kunna bryta.

Se även